From 9b008e6d740cdf2d670597f9e36da786cecb8340 Mon Sep 17 00:00:00 2001 From: Ismail Cherri Date: Thu, 17 Oct 2024 13:54:24 +0200 Subject: [PATCH] SONAR-23191 Filter updates based on MQR or STANDARD modes --- .../api/mocks/QualityProfilesServiceMock.ts | 70 ++++++- .../src/main/js/api/quality-profiles.ts | 26 ++- .../quality-profiles/changelog/Changelog.tsx | 11 +- .../changelog/ChangelogContainer.tsx | 191 ++++++------------ .../changelog/ChangesList.tsx | 21 +- .../changelog/SeverityChange.tsx | 5 +- .../__tests__/ChangelogContainer-it.tsx | 38 +++- .../src/main/js/queries/quality-profiles.ts | 101 ++++++--- .../src/main/js/types/quality-profiles.ts | 23 +++ .../resources/org/sonar/l10n/core.properties | 2 +- 10 files changed, 296 insertions(+), 192 deletions(-) create mode 100644 server/sonar-web/src/main/js/types/quality-profiles.ts diff --git a/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts index e348ef7dd2a..f1b3ef928c1 100644 --- a/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/QualityProfilesServiceMock.ts @@ -37,20 +37,17 @@ import { } from '../../types/clean-code-taxonomy'; import { SearchRulesResponse } from '../../types/coding-rules'; import { IssueSeverity } from '../../types/issues'; +import { QualityProfileChangelogFilterMode } from '../../types/quality-profiles'; import { SearchRulesQuery } from '../../types/rules'; import { Dict, Paging, ProfileInheritanceDetails, RuleDetails } from '../../types/types'; import { - CompareResponse, - Profile, - ProfileProject, - SearchQualityProfilesParameters, - SearchQualityProfilesResponse, activateRule, addGroup, addUser, associateProject, changeProfileParent, compareProfiles, + CompareResponse, copyProfile, createQualityProfile, deactivateRule, @@ -63,12 +60,16 @@ import { getProfileProjects, getQualityProfile, getQualityProfileExporterUrl, + Profile, + ProfileProject, removeGroup, removeUser, renameProfile, restoreQualityProfile, searchGroups, searchQualityProfiles, + SearchQualityProfilesParameters, + SearchQualityProfilesResponse, searchUsers, setDefaultProfile, } from '../quality-profiles'; @@ -296,6 +297,37 @@ export default class QualityProfilesServiceMock { credentialWords: 'foo,bar', }, }), + mockQualityProfileChangelogEvent({ + date: '2019-02-23T03:12:32+0100', + action: 'UPDATED', + ruleKey: 'c:rule4', + ruleName: 'Rule 5', + params: { + newCleanCodeAttribute: CleanCodeAttribute.Complete, + newCleanCodeAttributeCategory: CleanCodeAttributeCategory.Intentional, + oldCleanCodeAttribute: CleanCodeAttribute.Lawful, + oldCleanCodeAttributeCategory: CleanCodeAttributeCategory.Responsible, + impactChanges: [ + { + newSeverity: SoftwareImpactSeverity.Medium, + newSoftwareQuality: SoftwareQuality.Reliability, + }, + { + oldSeverity: SoftwareImpactSeverity.High, + oldSoftwareQuality: SoftwareQuality.Maintainability, + }, + ], + }, + }), + mockQualityProfileChangelogEvent({ + date: '2019-01-23T03:12:32+0100', + action: 'UPDATED', + ruleKey: 'c:rule6', + ruleName: 'Rule 6', + params: { + severity: IssueSeverity.Critical, + }, + }), ]; } @@ -682,9 +714,16 @@ export default class QualityProfilesServiceMock { return this.reply({}); }; - handleGetProfileChangelog: typeof getProfileChangelog = (since, to, { language }, page) => { + handleGetProfileChangelog: typeof getProfileChangelog = (data) => { + const { + profile: { language }, + since, + to, + page, + filterMode = QualityProfileChangelogFilterMode.MQR, + } = data; const PAGE_SIZE = 50; - const p = page || 1; + const p = page ?? 1; const events = this.changelogEvents.filter((event) => { if (event.ruleKey.split(':')[0] !== language) { return false; @@ -695,6 +734,23 @@ export default class QualityProfilesServiceMock { if (to && new Date(to) <= new Date(event.date)) { return false; } + if ( + filterMode === QualityProfileChangelogFilterMode.MQR && + event.action === 'UPDATED' && + event.params && + !Object.keys(event.params).includes('impactChanges') + ) { + return false; + } + + if ( + filterMode === QualityProfileChangelogFilterMode.STANDARD && + event.action === 'UPDATED' && + !event.params?.severity + ) { + return false; + } + return true; }); diff --git a/server/sonar-web/src/main/js/api/quality-profiles.ts b/server/sonar-web/src/main/js/api/quality-profiles.ts index 21a61af4e4f..2c293b4bb84 100644 --- a/server/sonar-web/src/main/js/api/quality-profiles.ts +++ b/server/sonar-web/src/main/js/api/quality-profiles.ts @@ -29,6 +29,7 @@ import { SoftwareImpactSeverity, SoftwareQuality, } from '../types/clean-code-taxonomy'; +import { QualityProfileChangelogFilterMode } from '../types/quality-profiles'; import { Dict, Paging, ProfileInheritanceDetails, UserSelected } from '../types/types'; export interface ProfileActions { @@ -119,7 +120,7 @@ export function getProfileInheritance({ }: Pick): Promise<{ ancestors: ProfileInheritanceDetails[]; children: ProfileInheritanceDetails[]; - profile: ProfileInheritanceDetails; + profile: ProfileInheritanceDetails | null; }> { return getJSON('/api/qualityprofiles/inheritance', { language, @@ -182,17 +183,28 @@ export interface ChangelogResponse { paging: Paging; } -export function getProfileChangelog( - since: any, - to: any, - { language, name: qualityProfile }: Profile, - page?: number, -): Promise { +interface ChangelogData { + filterMode: QualityProfileChangelogFilterMode; + page?: number; + profile: Profile; + since: string; + to: string; +} + +export function getProfileChangelog(data: ChangelogData): Promise { + const { + filterMode, + page, + profile: { language, name: qualityProfile }, + since, + to, + } = data; return getJSON('/api/qualityprofiles/changelog', { since, to, language, qualityProfile, + filterMode, p: page, }); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.tsx index 65257c141d9..f8d8c883822 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.tsx @@ -17,13 +17,13 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { LinkStandalone } from '@sonarsource/echoes-react'; import classNames from 'classnames'; import { isSameMinute } from 'date-fns'; import { CellComponent, ContentCell, FlagMessage, - Link, Note, Table, TableRow, @@ -34,6 +34,7 @@ import * as React from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; import { parseDate } from '../../../helpers/dates'; +import { isDefined } from '../../../helpers/types'; import { getRulesUrl } from '../../../helpers/urls'; import { ProfileChangelogEvent } from '../types'; import ChangesList from './ChangesList'; @@ -42,7 +43,7 @@ interface Props { events: ProfileChangelogEvent[]; } -export default function Changelog(props: Props) { +export default function Changelog(props: Readonly) { const intl = useIntl(); const sortedRows = sortBy( @@ -126,8 +127,10 @@ export default function Changelog(props: Props) { - {event.ruleName && ( - {event.ruleName} + {isDefined(event.ruleName) && ( + + {event.ruleName} + )} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx index 0f37e03c4ac..e795ab941e2 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.tsx @@ -17,155 +17,92 @@ * 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 } from '@sonarsource/echoes-react'; -import { Spinner } from 'design-system'; +import { Button, Spinner } from '@sonarsource/echoes-react'; import * as React from 'react'; -import { withRouter } from '~sonar-aligned/components/hoc/withRouter'; -import { Location, Router } from '~sonar-aligned/types/router'; -import { ChangelogResponse, getProfileChangelog } from '../../../api/quality-profiles'; +import { useLocation, useRouter } from '~sonar-aligned/components/hoc/withRouter'; import { parseDate, toISO8601WithOffsetString } from '../../../helpers/dates'; import { translate } from '../../../helpers/l10n'; +import { isDefined } from '../../../helpers/types'; +import { useGetQualityProfileChangelog } from '../../../queries/quality-profiles'; +import { useStandardExperienceMode } from '../../../queries/settings'; +import { QualityProfileChangelogFilterMode } from '../../../types/quality-profiles'; import { withQualityProfilesContext } from '../qualityProfilesContext'; -import { Profile, ProfileChangelogEvent } from '../types'; +import { Profile } from '../types'; import { getProfileChangelogPath } from '../utils'; import Changelog from './Changelog'; import ChangelogEmpty from './ChangelogEmpty'; import ChangelogSearch from './ChangelogSearch'; interface Props { - location: Location; profile: Profile; - router: Router; } -interface State { - events?: ProfileChangelogEvent[]; - loading: boolean; - page?: number; - total?: number; -} - -class ChangelogContainer extends React.PureComponent { - mounted = false; - state: State = { loading: true }; - - componentDidMount() { - this.mounted = true; - this.loadChangelog(); - } - - componentDidUpdate(prevProps: Props) { - if (prevProps.location !== this.props.location) { - this.loadChangelog(); - } - } - - componentWillUnmount() { - this.mounted = false; - } - - stopLoading() { - if (this.mounted) { - this.setState({ loading: false }); - } - } - - loadChangelog() { - this.setState({ loading: true }); - const { - location: { query }, - profile, - } = this.props; - - getProfileChangelog(query.since, query.to, profile) - .then((r: ChangelogResponse) => { - if (this.mounted) { - this.setState({ - events: r.events, - total: r.paging.total, - page: r.paging.pageIndex, - loading: false, - }); - } - }) - .catch(this.stopLoading); - } - - loadMore(event: React.SyntheticEvent) { - event.preventDefault(); - event.currentTarget.blur(); - - if (this.state.page != null) { - this.setState({ loading: true }); - const { - location: { query }, - profile, - } = this.props; - - getProfileChangelog(query.since, query.to, profile, this.state.page + 1) - .then((r: ChangelogResponse) => { - if (this.mounted && this.state.events) { - this.setState(({ events = [] }) => ({ - events: [...events, ...r.events], - total: r.paging.total, - page: r.paging.pageIndex, - loading: false, - })); - } - }) - .catch(this.stopLoading); - } - } - - handleDateRangeChange = ({ from, to }: { from?: Date; to?: Date }) => { - const path = getProfileChangelogPath(this.props.profile.name, this.props.profile.language, { +function ChangelogContainer(props: Readonly) { + const { profile } = props; + const { data: isStandardMode } = useStandardExperienceMode(); + const router = useRouter(); + const { + query: { since, to }, + } = useLocation(); + + const filterMode = isStandardMode + ? QualityProfileChangelogFilterMode.STANDARD + : QualityProfileChangelogFilterMode.MQR; + + const { + data: changeLogResponse, + isLoading, + fetchNextPage, + } = useGetQualityProfileChangelog({ + since, + to, + profile, + filterMode, + }); + + const events = changeLogResponse?.pages.flatMap((page) => page.events) ?? []; + const total = changeLogResponse?.pages[0].paging.total; + + const handleDateRangeChange = ({ from, to }: { from?: Date; to?: Date }) => { + const path = getProfileChangelogPath(profile.name, profile.language, { since: from && toISO8601WithOffsetString(from), to: to && toISO8601WithOffsetString(to), }); - this.props.router.push(path); + router.push(path); }; - handleReset = () => { - const path = getProfileChangelogPath(this.props.profile.name, this.props.profile.language); - this.props.router.push(path); + const handleReset = () => { + const path = getProfileChangelogPath(profile.name, profile.language); + router.replace(path); }; - render() { - const { query } = this.props.location; - - const shouldDisplayFooter = - this.state.events != null && - this.state.total != null && - this.state.events.length < this.state.total; - - return ( -
-
- - -
+ const shouldDisplayFooter = isDefined(events) && isDefined(total) && events.length < total; + + return ( +
+
+ + +
- {this.state.events != null && this.state.events.length === 0 && } + {isDefined(events) && events.length === 0 && } - {this.state.events != null && this.state.events.length > 0 && ( - - )} + {isDefined(events) && events.length > 0 && } - {shouldDisplayFooter && ( -
- -
- )} -
- ); - } + {shouldDisplayFooter && ( +
+ +
+ )} +
+ ); } -export default withQualityProfilesContext(withRouter(ChangelogContainer)); +export default withQualityProfilesContext(ChangelogContainer); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.tsx index dc01693ab87..1cf60b39fb7 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { useStandardExperienceMode } from '../../../queries/settings'; import { ProfileChangelogEvent } from '../types'; import CleanCodeAttributeChange from './CleanCodeAttributeChange'; import ParameterChange from './ParameterChange'; @@ -28,7 +29,7 @@ interface Props { changes: ProfileChangelogEvent['params']; } -export default function ChangesList({ changes }: Props) { +export default function ChangesList({ changes }: Readonly) { const { severity, oldCleanCodeAttribute, @@ -39,15 +40,18 @@ export default function ChangesList({ changes }: Props) { ...rest } = changes ?? {}; + const { data: isStandardMode } = useStandardExperienceMode(); + return (
    - {severity && ( + {severity && isStandardMode && (
  • )} - {oldCleanCodeAttribute && + {!isStandardMode && + oldCleanCodeAttribute && oldCleanCodeAttributeCategory && newCleanCodeAttribute && newCleanCodeAttributeCategory && ( @@ -61,11 +65,12 @@ export default function ChangesList({ changes }: Props) { )} - {impactChanges?.map((impactChange, index) => ( -
  • - -
  • - ))} + {!isStandardMode && + impactChanges?.map((impactChange, index) => ( +
  • + +
  • + ))} {Object.keys(rest).map((key) => (
  • diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.tsx index 7912c9a159a..95cd310a8df 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.tsx @@ -25,11 +25,10 @@ interface Props { severity: string; } -export default function SeverityChange({ severity }: Props) { +export default function SeverityChange({ severity }: Readonly) { return (
    - {translate('quality_profiles.deprecated_severity_set_to')}{' '} - + {translate('quality_profiles.severity_set_to')}
    ); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx index 66b303fb58f..8f2c7d2f09a 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/__tests__/ChangelogContainer-it.tsx @@ -24,6 +24,7 @@ import QualityProfilesServiceMock from '../../../../api/mocks/QualityProfilesSer import SettingsServiceMock from '../../../../api/mocks/SettingsServiceMock'; import { mockQualityProfileChangelogEvent } from '../../../../helpers/testMocks'; import { renderAppRoutes } from '../../../../helpers/testReactTestingUtils'; +import { SettingsKey } from '../../../../types/settings'; import routes from '../../routes'; jest.mock('../../../../api/quality-profiles'); @@ -80,20 +81,24 @@ afterEach(() => { settingsMock.reset(); }); -it('should see the changelog', async () => { +it('should see the changelog in MQR', async () => { const user = userEvent.setup(); renderChangeLog(); const rows = await ui.row.findAll(); - expect(rows).toHaveLength(6); + expect(rows).toHaveLength(7); expect(ui.emptyPage.query()).not.toBeInTheDocument(); ui.checkRow(1, 'May 23, 2019', 'System', 'quality_profiles.changelog.ACTIVATED', 'Rule 0'); ui.checkRow(2, 'April 23, 2019', 'System', 'quality_profiles.changelog.DEACTIVATED', 'Rule 0', [ - /quality_profiles.deprecated_severity_set_to severity.MAJOR/, + /^$/, // Should be empty ]); ui.checkRow(3, '', '', '', 'Rule 1', [ - /quality_profiles.deprecated_severity_set_to severity.CRITICAL/, + /quality_profiles.changelog.cca_and_category_changed.*COMPLETE.*INTENTIONAL.*LAWFUL.*RESPONSIBLE/, + /quality_profiles.changelog.impact_added.severity_impact.*MEDIUM.*RELIABILITY/, + /quality_profiles.changelog.impact_removed.severity_impact.HIGH.*MAINTAINABILITY/, + ]); + ui.checkRow(6, 'February 23, 2019', 'System', 'quality_profiles.changelog.UPDATED', 'Rule 5', [ /quality_profiles.changelog.cca_and_category_changed.*COMPLETE.*INTENTIONAL.*LAWFUL.*RESPONSIBLE/, /quality_profiles.changelog.impact_added.severity_impact.*MEDIUM.*RELIABILITY/, /quality_profiles.changelog.impact_removed.severity_impact.HIGH.*MAINTAINABILITY/, @@ -102,11 +107,28 @@ it('should see the changelog', async () => { expect(screen.getByText('/coding_rules?rule_key=c%3Arule0')).toBeInTheDocument(); }); +it('should return standard mode changelogs only', async () => { + settingsMock.set(SettingsKey.MQRMode, 'false'); + renderChangeLog(); + + const rows = await ui.row.findAll(); + expect(rows).toHaveLength(7); + ui.checkRow(1, 'May 23, 2019', 'System', 'quality_profiles.changelog.ACTIVATED', 'Rule 0', [ + /^$/, + ]); + ui.checkRow(2, 'April 23, 2019', 'System', 'quality_profiles.changelog.DEACTIVATED', 'Rule 0', [ + /quality_profiles.severity_set_to severity.MAJOR/, + ]); + ui.checkRow(6, 'January 23, 2019', 'System', 'quality_profiles.changelog.UPDATED', 'Rule 6', [ + /quality_profiles.severity_set_to severity.CRITICAL/, + ]); +}); + it('should filter the changelog', async () => { const user = userEvent.setup(); renderChangeLog(); - expect(await ui.row.findAll()).toHaveLength(6); + expect(await ui.row.findAll()).toHaveLength(7); await user.click(ui.startDate.get()); await user.click(screen.getByRole('gridcell', { name: '20' })); await user.click(document.body); @@ -116,7 +138,7 @@ it('should filter the changelog', async () => { await user.click(document.body); expect(await ui.row.findAll()).toHaveLength(4); await user.click(ui.reset.get()); - expect(await ui.row.findAll()).toHaveLength(6); + expect(await ui.row.findAll()).toHaveLength(7); }); it('should load more', async () => { @@ -134,7 +156,7 @@ it('should load more', async () => { await user.click(ui.showMore.get()); expect(await ui.row.findAll()).toHaveLength(101); await user.click(ui.reset.get()); - expect(await ui.row.findAll()).toHaveLength(51); + expect(await ui.row.findAll()).toHaveLength(101); // Reset should not reset the page }); it('should see short changelog for php', async () => { @@ -143,7 +165,7 @@ it('should see short changelog for php', async () => { const rows = await ui.row.findAll(); expect(rows).toHaveLength(2); ui.checkRow(1, 'May 23, 2019', 'System', 'quality_profiles.changelog.DEACTIVATED', 'PHP Rule', [ - /quality_profiles.deprecated_severity_set_to severity.CRITICAL/, + /^((?!severity.CRITICAL).)*$/, // Should not contain severity.CRITICAL /quality_profiles.changelog.cca_and_category_changed.*COMPLETE.*INTENTIONAL.*CLEAR.*RESPONSIBLE/, ]); expect(ui.showMore.query()).not.toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/queries/quality-profiles.ts b/server/sonar-web/src/main/js/queries/quality-profiles.ts index 572a54a3758..246b1137894 100644 --- a/server/sonar-web/src/main/js/queries/quality-profiles.ts +++ b/server/sonar-web/src/main/js/queries/quality-profiles.ts @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { - UseQueryResult, + infiniteQueryOptions, queryOptions, useMutation, useQuery, @@ -35,37 +35,64 @@ import { addUser, compareProfiles, deactivateRule, + getProfileChangelog, getProfileInheritance, getQualityProfile, } from '../api/quality-profiles'; -import { ProfileInheritanceDetails } from '../types/types'; -import { createQueryHook } from './common'; +import { getNextPageParam, getPreviousPageParam } from '../helpers/react-query'; +import { isDefined } from '../helpers/types'; +import { QualityProfileChangelogFilterMode } from '../types/quality-profiles'; +import { createInfiniteQueryHook, createQueryHook } from './common'; -export function useProfileInheritanceQuery( - profile?: Pick, -): UseQueryResult<{ - ancestors: ProfileInheritanceDetails[]; - children: ProfileInheritanceDetails[]; - profile: ProfileInheritanceDetails | null; -}> { - const { language, name, parentKey } = profile ?? {}; - return useQuery({ - queryKey: ['quality-profiles', 'inheritance', language, name, parentKey], - queryFn: async ({ queryKey: [, , language, name] }) => { - if (!language || !name) { - return { ancestors: [], children: [], profile: null }; - } - const response = await getProfileInheritance({ language, name }); - response.ancestors.reverse(); - return response; - }, - }); -} +const qualityProfileQueryKeys = { + all: () => ['quality-profiles'], + inheritance: (language?: string, name?: string, parentKey?: string) => [ + ...qualityProfileQueryKeys.all(), + 'inheritance', + language, + name, + parentKey, + ], + profile: (profile: Profile, compareToSonarWay = false) => [ + ...qualityProfileQueryKeys.all(), + 'details', + profile, + compareToSonarWay, + ], + changelog: ( + language: string, + name: string, + since: string, + to: string, + filterMode: QualityProfileChangelogFilterMode, + ) => [...qualityProfileQueryKeys.all(), 'changelog', language, name, since, to, filterMode], + compare: (leftKey: string, rightKey: string) => [ + ...qualityProfileQueryKeys.all(), + 'compare', + leftKey, + rightKey, + ], +}; + +export const useProfileInheritanceQuery = createQueryHook( + (profile?: Pick) => { + const { language, name, parentKey } = profile ?? {}; + return queryOptions({ + queryKey: qualityProfileQueryKeys.inheritance(language, name, parentKey), + queryFn: () => { + if (!isDefined(language) || !isDefined(name)) { + return { ancestors: [], children: [], profile: null }; + } + return getProfileInheritance({ language, name }); + }, + }); + }, +); export const useGetQualityProfile = createQueryHook( (data: Parameters[0]) => { return queryOptions({ - queryKey: ['quality-profile', 'details', data.profile, data.compareToSonarWay], + queryKey: qualityProfileQueryKeys.profile(data.profile, data.compareToSonarWay), queryFn: () => { return getQualityProfile(data); }, @@ -73,11 +100,31 @@ export const useGetQualityProfile = createQueryHook( }, ); +export const useGetQualityProfileChangelog = createInfiniteQueryHook( + (data: Parameters[0]) => { + return infiniteQueryOptions({ + queryKey: qualityProfileQueryKeys.changelog( + data.profile.language, + data.profile.name, + data.since, + data.to, + data.filterMode, + ), + queryFn: ({ pageParam }) => { + return getProfileChangelog({ ...data, page: pageParam }); + }, + getNextPageParam: (data) => getNextPageParam({ page: data.paging }), + getPreviousPageParam: (data) => getPreviousPageParam({ page: data.paging }), + initialPageParam: 1, + }); + }, +); + export function useProfilesCompareQuery(leftKey: string, rightKey: string) { return useQuery({ - queryKey: ['quality-profiles', 'compare', leftKey, rightKey], - queryFn: ({ queryKey: [, , leftKey, rightKey] }) => { - if (!leftKey || !rightKey) { + queryKey: qualityProfileQueryKeys.compare(leftKey, rightKey), + queryFn: ({ queryKey: [_1, _2, leftKey, rightKey] }) => { + if (!isDefined(leftKey) || !isDefined(rightKey)) { return null; } diff --git a/server/sonar-web/src/main/js/types/quality-profiles.ts b/server/sonar-web/src/main/js/types/quality-profiles.ts new file mode 100644 index 00000000000..ec740224d6d --- /dev/null +++ b/server/sonar-web/src/main/js/types/quality-profiles.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ +export enum QualityProfileChangelogFilterMode { + MQR = 'MQR', + STANDARD = 'STANDARD', +} 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 69a8d79f47e..0d8dc819ad4 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2261,7 +2261,7 @@ quality_profiles.copy_x_title=Copy Profile "{0}" - {1} quality_profiles.extend_x_title=Extend Profile "{0}" - {1} quality_profiles.rename_x_title=Rename Profile {0} - {1} quality_profiles.deprecated=deprecated -quality_profiles.deprecated_severity_set_to=Old severity set to +quality_profiles.severity_set_to=Severity set to quality_profiles.changelog.ACTIVATED=Activated quality_profiles.changelog.DEACTIVATED=Deactivated quality_profiles.changelog.UPDATED=Updated -- 2.39.5