diff options
author | Jay <jeremy.davis@sonarsource.com> | 2024-09-11 09:33:38 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-09-12 20:02:53 +0000 |
commit | bae60bb896fb02d58245889bc896c06b62e96cab (patch) | |
tree | ad594bd1deaeebfdc6869cdaae42ed71da664c95 /server/sonar-web/src/main/js/apps | |
parent | fbb3e8846f006067481102517dbad5126e96213e (diff) | |
download | sonarqube-bae60bb896fb02d58245889bc896c06b62e96cab.tar.gz sonarqube-bae60bb896fb02d58245889bc896c06b62e96cab.zip |
SONAR-22970 Improve branch queries and fix loading conflict
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
16 files changed, 51 insertions, 48 deletions
diff --git a/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx index e91bfd4b08d..5c5e4384619 100644 --- a/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx @@ -17,15 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { Spinner } from '@sonarsource/echoes-react'; import * as React from 'react'; import { withRouter } from '~sonar-aligned/components/hoc/withRouter'; -import { isPortfolioLike } from '~sonar-aligned/helpers/component'; import { ComponentQualifier } from '~sonar-aligned/types/component'; import { Location, Router } from '~sonar-aligned/types/router'; import withComponentContext from '../../../app/components/componentContext/withComponentContext'; import withMetricsContext from '../../../app/components/metrics/withMetricsContext'; +import { isDefined } from '../../../helpers/types'; import { CodeScope, getCodeUrl, getProjectUrl } from '../../../helpers/urls'; -import { WithBranchLikesProps, useBranchesQuery } from '../../../queries/branch'; +import { WithBranchLikesProps, useCurrentBranchQuery } from '../../../queries/branch'; import { useComponentBreadcrumbsQuery, useComponentQuery } from '../../../queries/component'; import { useComponentTreeQuery } from '../../../queries/measures'; import { getBranchLikeQuery } from '../../../sonar-aligned/helpers/branch-like'; @@ -33,7 +34,7 @@ import { Component, ComponentMeasure, Dict, Metric } from '../../../types/types' import { getCodeMetrics } from '../utils'; import CodeAppRenderer from './CodeAppRenderer'; -interface Props extends WithBranchLikesProps { +interface Props { component: Component; location: Location; metrics: Dict<Metric>; @@ -43,11 +44,13 @@ interface Props extends WithBranchLikesProps { const PAGE_SIZE = 100; function CodeApp(props: Readonly<Props>) { - const { component, metrics, router, location, branchLike } = props; + const { component, metrics, router, location } = props; const [highlighted, setHighlighted] = React.useState<ComponentMeasure | undefined>(); const [newCodeSelected, setNewCodeSelected] = React.useState<boolean>(true); const [searchResults, setSearchResults] = React.useState<ComponentMeasure[] | undefined>(); + const { data: branchLike } = useCurrentBranchQuery(component); + const { data: breadcrumbs, isLoading: isBreadcrumbsLoading } = useComponentBreadcrumbsQuery({ component: location.query.selected ?? component.key, ...getBranchLikeQuery(branchLike), @@ -161,22 +164,16 @@ function CodeApp(props: Readonly<Props>) { ); } -function withBranchLikes<P extends { component?: Component }>( +function withComponentGuard<P extends { component?: Component }>( WrappedComponent: React.ComponentType<React.PropsWithChildren<P & WithBranchLikesProps>>, ): React.ComponentType<React.PropsWithChildren<Omit<P, 'branchLike' | 'branchLikes'>>> { - return function WithBranchLike(p: P) { - const { data, isFetching } = useBranchesQuery(p.component); - + return function WithBranchLike(props: P) { return ( - (isPortfolioLike(p.component?.qualifier) || !isFetching) && ( - <WrappedComponent - branchLikes={data?.branchLikes ?? []} - branchLike={data?.branchLike} - {...p} - /> - ) + <Spinner isLoading={!isDefined(props.component)}> + <WrappedComponent {...props} /> + </Spinner> ); }; } -export default withRouter(withComponentContext(withMetricsContext(withBranchLikes(CodeApp)))); +export default withRouter(withComponentContext(withMetricsContext(withComponentGuard(CodeApp)))); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx index af0e880606f..bd7e35b6aa6 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx @@ -48,8 +48,9 @@ import { areCCTMeasuresComputed, areSoftwareQualityRatingsComputed, } from '../../../helpers/measures'; -import { useBranchesQuery } from '../../../queries/branch'; +import { useCurrentBranchQuery } from '../../../queries/branch'; import { useMeasuresComponentQuery } from '../../../queries/measures'; + import { MeasurePageView } from '../../../types/measures'; import { useBubbleChartMetrics } from '../hooks'; import Sidebar from '../sidebar/Sidebar'; @@ -73,7 +74,7 @@ import MeasuresEmpty from './MeasuresEmpty'; export default function ComponentMeasuresApp() { const { component } = React.useContext(ComponentContext); - const { data: { branchLike } = {} } = useBranchesQuery(component); + const { data: branchLike } = useCurrentBranchQuery(component); const { query: rawQuery, pathname } = useLocation(); const query = parseQuery(rawQuery); const router = useRouter(); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx index e3dd0120797..95165e1448a 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx @@ -34,7 +34,7 @@ import { getCCTMeasureValue, isDiffMetric } from '../../../helpers/measures'; import { RequestData } from '../../../helpers/request'; import { isDefined } from '../../../helpers/types'; import { getProjectUrl } from '../../../helpers/urls'; -import { useBranchesQuery } from '../../../queries/branch'; +import { useCurrentBranchQuery } from '../../../queries/branch'; import { useComponentTreeQuery, useMeasuresComponentQuery } from '../../../queries/measures'; import { useIsLegacyCCTMode } from '../../../queries/settings'; import { useLocation, useRouter } from '../../../sonar-aligned/components/hoc/withRouter'; @@ -68,7 +68,7 @@ export default function MeasureContent(props: Readonly<Props>) { const { leakPeriod, requestedMetric, rootComponent, updateQuery } = props; const metrics = useMetrics(); const { query: rawQuery } = useLocation(); - const { data: { branchLike } = {} } = useBranchesQuery(); + const { data: branchLike } = useCurrentBranchQuery(rootComponent); const router = useRouter(); const query = parseQuery(rawQuery); const { data: isLegacy } = useIsLegacyCCTMode(); diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx index 52ad7565a03..6a939589dc3 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.tsx @@ -22,7 +22,7 @@ import * as React from 'react'; import { useMetrics } from '../../../app/components/metrics/withMetricsContext'; import SourceViewer from '../../../components/SourceViewer/SourceViewer'; import { getProjectUrl } from '../../../helpers/urls'; -import { useBranchesQuery } from '../../../queries/branch'; +import { useCurrentBranchQuery } from '../../../queries/branch'; import { useComponentDataQuery } from '../../../queries/component'; import { useComponentTreeQuery } from '../../../queries/measures'; import A11ySkipTarget from '../../../sonar-aligned/components/a11y/A11ySkipTarget'; @@ -54,7 +54,7 @@ interface Props { export default function MeasureOverview(props: Readonly<Props>) { const { leakPeriod, updateQuery, rootComponent, bubblesByDomain } = props; const metrics = useMetrics(); - const { data: { branchLike } = {} } = useBranchesQuery(); + const { data: branchLike } = useCurrentBranchQuery(rootComponent); const router = useRouter(); const { query } = useLocation(); const { selected, metric: domain } = parseQuery(query); diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx index d6c55e29188..368ceb42fb4 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx @@ -20,8 +20,9 @@ import { Note } from 'design-system'; import React from 'react'; import Measure from '~sonar-aligned/components/measure/Measure'; +import { useComponent } from '../../../app/components/componentContext/withComponentContext'; import { isDiffMetric } from '../../../helpers/measures'; -import { useBranchesQuery } from '../../../queries/branch'; +import { useCurrentBranchQuery } from '../../../queries/branch'; import { MeasureEnhanced } from '../../../types/types'; interface Props { @@ -30,8 +31,10 @@ interface Props { } export default function SubnavigationMeasureValue({ measure, componentKey }: Readonly<Props>) { + const { component } = useComponent(); + const isDiff = isDiffMetric(measure.metric.key); - const { data: { branchLike } = {} } = useBranchesQuery(); + const { data: branchLike } = useCurrentBranchQuery(component); const value = isDiff ? measure.leak : measure.value; return ( diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx index 58ce845ca3e..08c95db1ca8 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx @@ -40,7 +40,7 @@ import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils'; import { translate } from '../../../helpers/l10n'; import { collapsedDirFromPath, fileFromPath } from '../../../helpers/path'; import { getBranchLikeUrl } from '../../../helpers/urls'; -import { useBranchesQuery } from '../../../queries/branch'; +import { useCurrentBranchQuery } from '../../../queries/branch'; import { SourceViewerFile } from '../../../types/types'; import { isLoggedIn } from '../../../types/users'; import { IssueOpenInIdeButton } from '../components/IssueOpenInIdeButton'; @@ -79,7 +79,7 @@ export function IssueSourceViewerHeader(props: Readonly<Props>) { const { measures, path, project, projectName, q } = sourceViewerFile; const { component } = React.useContext(ComponentContext); - const { data: branchData, isLoading: isLoadingBranches } = useBranchesQuery( + const { data: branchLike, isLoading: isLoadingBranches } = useCurrentBranchQuery( component ?? { key: project, name: projectName, @@ -89,8 +89,6 @@ export function IssueSourceViewerHeader(props: Readonly<Props>) { const { currentUser } = useCurrentUser(); const theme = useTheme(); - const branchLike = branchData?.branchLike; - const isProjectRoot = q === ComponentQualifier.Project; const borderColor = themeColor('codeLineBorder')({ theme }); diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx index e79b7b7126a..4a99bdfa447 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx @@ -61,6 +61,10 @@ let usersHandler: UsersServiceMock; let timeMarchineHandler: TimeMachineServiceMock; let qualityGatesHandler: QualityGatesServiceMock; +jest.mock('../../../../api/ce', () => ({ + getAnalysisStatus: jest.fn().mockResolvedValue({ component: { warnings: [] } }), +})); + beforeAll(() => { branchesHandler = new BranchesServiceMock(); measuresHandler = new MeasuresServiceMock(); diff --git a/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx b/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx index 212cb7725c1..a5daa75ba3d 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/AnalysisErrorMessage.tsx @@ -24,7 +24,7 @@ import { useLocation } from 'react-router-dom'; import { isBranch, isMainBranch, isPullRequest } from '~sonar-aligned/helpers/branch-like'; import { hasMessage, translate } from '../../../helpers/l10n'; import { getComponentBackgroundTaskUrl } from '../../../helpers/urls'; -import { useBranchesQuery } from '../../../queries/branch'; +import { useCurrentBranchQuery } from '../../../queries/branch'; import { BranchLike } from '../../../types/branch-like'; import { Task } from '../../../types/tasks'; import { Component } from '../../../types/types'; @@ -52,7 +52,7 @@ function isSameBranch(task: Task, branchLike?: BranchLike) { export function AnalysisErrorMessage(props: Props) { const { component, currentTask } = props; - const { data: { branchLike } = {} } = useBranchesQuery(component); + const { data: branchLike } = useCurrentBranchQuery(component); const currentTaskOnSameBranch = isSameBranch(currentTask, branchLike); const location = useLocation(); diff --git a/server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx b/server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx index 3efcac9115c..5a4daac6b9e 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/AnalysisWarningsModal.tsx @@ -38,7 +38,7 @@ interface Props { export function AnalysisWarningsModal(props: Props) { const { component, currentUser, warnings } = props; - const { mutate, isPending, variables } = useDismissBranchWarningMutation(); + const { mutate, isPending, variables } = useDismissBranchWarningMutation(component.key); const handleDismissMessage = (messageKey: string) => { mutate({ component, key: messageKey }); diff --git a/server/sonar-web/src/main/js/apps/overview/components/App.tsx b/server/sonar-web/src/main/js/apps/overview/components/App.tsx index ead3f2d259e..89e87134d9a 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/App.tsx @@ -28,7 +28,7 @@ import withAvailableFeatures, { import withComponentContext from '../../../app/components/componentContext/withComponentContext'; import Suggestions from '../../../components/embed-docs-modal/Suggestions'; import { translate } from '../../../helpers/l10n'; -import { useBranchesQuery } from '../../../queries/branch'; +import { useCurrentBranchQuery } from '../../../queries/branch'; import { Feature } from '../../../types/features'; import { Component } from '../../../types/types'; import BranchOverview from '../branches/BranchOverview'; @@ -42,14 +42,12 @@ interface AppProps extends WithAvailableFeaturesProps { export function App(props: AppProps) { const { component } = props; const branchSupportEnabled = props.hasFeature(Feature.BranchSupport); - const { data } = useBranchesQuery(component); + const { data: branchLike } = useCurrentBranchQuery(component); - if (isPortfolioLike(component.qualifier) || !data) { + if (isPortfolioLike(component.qualifier) || !branchLike) { return null; } - const { branchLike, branchLikes } = data; - return ( <> <Helmet defer={false} title={translate('overview.page')} /> @@ -63,11 +61,7 @@ export function App(props: AppProps) { <Suggestions suggestionGroup="overview" /> {!component.analysisDate && ( - <EmptyOverview - branchLike={branchLike} - branchLikes={branchLikes} - component={component} - /> + <EmptyOverview branchLike={branchLike} component={component} /> )} {component.analysisDate && ( diff --git a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx index a8df895e419..3f52c59c316 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx @@ -31,6 +31,7 @@ import { getBranchLikeDisplayName } from '../../../helpers/branch-like'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { getProjectTutorialLocation } from '../../../helpers/urls'; import { hasGlobalPermission } from '../../../helpers/users'; +import { useBranchesQuery } from '../../../queries/branch'; import { useTaskForComponentQuery } from '../../../queries/component'; import { AlmKeys } from '../../../types/alm-settings'; import { BranchLike } from '../../../types/branch-like'; @@ -41,13 +42,14 @@ import { CurrentUser, isLoggedIn } from '../../../types/users'; export interface EmptyOverviewProps { branchLike?: BranchLike; - branchLikes: BranchLike[]; component: Component; currentUser: CurrentUser; } export function EmptyOverview(props: Readonly<EmptyOverviewProps>) { - const { branchLike, branchLikes, component, currentUser } = props; + const { branchLike, component, currentUser } = props; + + const { data: branchLikes } = useBranchesQuery(component); const [currentUserCanScanProject, setCurrentUserCanScanProject] = React.useState( hasGlobalPermission(currentUser, Permissions.Scan), diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx index 5aecfd6063b..f6952019b2b 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/__tests__/PullRequestOverview-it.tsx @@ -39,6 +39,10 @@ import { CaycStatus } from '../../../../types/types'; import { NoticeType } from '../../../../types/users'; import PullRequestOverview from '../PullRequestOverview'; +jest.mock('../../../../api/ce', () => ({ + getAnalysisStatus: jest.fn().mockResolvedValue({ component: { warnings: [] } }), +})); + jest.mock('../../../../api/measures', () => { return { ...jest.requireActual('../../../../sonar-aligned/types/metrics'), diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx index ee3b1c026d0..21cc693dd84 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.tsx @@ -35,7 +35,7 @@ import { mergeRatingMeasureHistory } from '../../../helpers/activity-graph'; import { SOFTWARE_QUALITY_RATING_METRICS } from '../../../helpers/constants'; import { parseDate } from '../../../helpers/dates'; import useApplicationLeakQuery from '../../../queries/applications'; -import { useBranchesQuery } from '../../../queries/branch'; +import { useCurrentBranchQuery } from '../../../queries/branch'; import { useAllMeasuresHistoryQuery } from '../../../queries/measures'; import { useAllProjectAnalysesQuery } from '../../../queries/project-analyses'; import { useIsLegacyCCTMode } from '../../../queries/settings'; @@ -62,7 +62,7 @@ export function ProjectActivityApp() { const router = useRouter(); const { component } = useComponent(); const metrics = useMetrics(); - const { data: { branchLike } = {}, isFetching: isFetchingBranch } = useBranchesQuery(component); + const { data: branchLike, isFetching: isFetchingBranch } = useCurrentBranchQuery(component); const enabled = component?.key !== undefined && (isPortfolioLike(component?.qualifier) || (Boolean(branchLike) && !isFetchingBranch)); diff --git a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx index d7d49d8fad1..51aaf5af4de 100644 --- a/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx +++ b/server/sonar-web/src/main/js/apps/projectBranches/components/BranchLikeTabs.tsx @@ -92,7 +92,7 @@ export default function BranchLikeTabs(props: Props) { } }; - const { data: { branchLikes } = { branchLikes: [] } } = useBranchesQuery(component); + const { data: branchLikes = [] } = useBranchesQuery(component); const isBranchMode = currentTab === Tabs.Branch; const branchLikesToDisplay: BranchLike[] = isBranchMode diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx index 72771d94b84..a1ad559a864 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotHeader.tsx @@ -49,7 +49,7 @@ export interface HotspotHeaderProps { export function HotspotHeader(props: HotspotHeaderProps) { const { branchLike, component, hotspot, standards } = props; const { message, messageFormattings, rule, key } = hotspot; - const refreshBranchStatus = useRefreshBranchStatus(); + const refreshBranchStatus = useRefreshBranchStatus(component.key); const permalink = getPathUrlAsString( getComponentSecurityHotspotsUrl(component.key, branchLike, { diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx index 22801e6f3f5..5044e1aba97 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx @@ -80,7 +80,7 @@ export default function HotspotViewerTabs(props: Props) { branchLike, } = props; - const refreshBranchStatus = useRefreshBranchStatus(); + const refreshBranchStatus = useRefreshBranchStatus(component.key); const isSticky = useStickyDetection('.hotspot-tabs', { offset: TABS_OFFSET, }); |