瀏覽代碼

SONAR-13413 Issues page is not available while indexation is in progress

SONAR-13400 Portfolios pages are not available while indexation is in progress
SONAR-13398 Project page is not available while indexation is in progress
SONAR-13402 Application page is not available while indexation is in progress
tags/8.4.0.35506
Philippe Perrin 4 年之前
父節點
當前提交
337ba04ed0

+ 5
- 0
server/sonar-web/src/main/js/app/components/ComponentContainer.tsx 查看文件

@@ -42,6 +42,7 @@ import { BranchLike } from '../../types/branch-like';
import { isPortfolioLike } from '../../types/component';
import ComponentContainerNotFound from './ComponentContainerNotFound';
import { ComponentContext } from './ComponentContext';
import PageUnavailableDueToIndexation from './indexation/PageUnavailableDueToIndexation';
import ComponentNav from './nav/component/ComponentNav';

interface Props {
@@ -322,6 +323,10 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
return <ComponentContainerNotFound />;
}

if (component?.needIssueSync) {
return <PageUnavailableDueToIndexation component={component} />;
}

const { branchLike, branchLikes, currentTask, isPending, tasksInProgress } = this.state;
const isInProgress = tasksInProgress && tasksInProgress.length > 0;


+ 13
- 0
server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx 查看文件

@@ -29,6 +29,7 @@ import { mockBranch, mockMainBranch, mockPullRequest } from '../../../helpers/mo
import { mockComponent, mockLocation, mockRouter } from '../../../helpers/testMocks';
import { ComponentQualifier } from '../../../types/component';
import { ComponentContainer } from '../ComponentContainer';
import PageUnavailableDueToIndexation from '../indexation/PageUnavailableDueToIndexation';

jest.mock('../../../api/branches', () => {
const { mockMainBranch, mockPullRequest } = require.requireActual(
@@ -210,6 +211,18 @@ it('should redirect if the component is a portfolio', async () => {
expect(replace).toBeCalledWith({ pathname: '/portfolio', query: { id: componentKey } });
});

it('should display display the unavailable page if the component needs issue sync', async () => {
(getComponentData as jest.Mock).mockResolvedValueOnce({
component: { key: 'test', qualifier: ComponentQualifier.Project, needIssueSync: true }
});

const wrapper = shallowRender();

await waitAndUpdate(wrapper);

expect(wrapper.find(PageUnavailableDueToIndexation).exists()).toBe(true);
});

function shallowRender(props: Partial<ComponentContainer['props']> = {}) {
return shallow<ComponentContainer>(
<ComponentContainer

+ 5
- 1
server/sonar-web/src/main/js/app/components/extensions/PortfoliosPage.tsx 查看文件

@@ -18,8 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import withIndexationGuard from '../../../components/hoc/withIndexationGuard';
import { PageContext } from '../indexation/PageUnavailableDueToIndexation';
import GlobalPageExtension from './GlobalPageExtension';

export default function PortfoliosPage() {
export function PortfoliosPage() {
return <GlobalPageExtension params={{ pluginKey: 'governance', extensionKey: 'portfolios' }} />;
}

export default withIndexationGuard(PortfoliosPage, PageContext.Portfolios);

+ 79
- 0
server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx 查看文件

@@ -0,0 +1,79 @@
/*
* SonarQube
* Copyright (C) 2009-2020 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 * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
import withIndexationContext, {
WithIndexationContextProps
} from '../../../components/hoc/withIndexationContext';

interface Props extends WithIndexationContextProps {
pageContext?: PageContext;
component?: Pick<T.Component, 'qualifier' | 'name'>;
}

export enum PageContext {
Issues = 'issues',
Portfolios = 'portfolios'
}

export class PageUnavailableDueToIndexation extends React.PureComponent<Props> {
componentDidUpdate() {
if (this.props.indexationContext?.status.isCompleted) {
window.location.reload();
}
}

render() {
const { pageContext, component } = this.props;
let messageKey = 'indexation.page_unavailable.title';

if (pageContext) {
messageKey = `${messageKey}.${pageContext}`;
}

return (
<div className="page-wrapper-simple">
<div className="page-simple">
<h1 className="big-spacer-bottom">
<FormattedMessage
id={messageKey}
defaultMessage={translate(messageKey)}
values={{
componentQualifier: translate('qualifier', component?.qualifier ?? ''),
componentName: <em>{component?.name}</em>
}}
/>
</h1>
<Alert variant="info">
<p>{translate('indexation.page_unavailable.description')}</p>
<p className="spacer-top">
{translate('indexation.page_unavailable.description.additional_information')}
</p>
</Alert>
</div>
</div>
);
}
}

export default withIndexationContext(PageUnavailableDueToIndexation);

+ 57
- 0
server/sonar-web/src/main/js/app/components/indexation/__tests__/PageUnavailableDueToIndexation-test.tsx 查看文件

@@ -0,0 +1,57 @@
/*
* SonarQube
* Copyright (C) 2009-2020 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 { shallow } from 'enzyme';
import * as React from 'react';
import { ComponentQualifier } from '../../../../types/component';
import { PageContext, PageUnavailableDueToIndexation } from '../PageUnavailableDueToIndexation';

it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();
});

it('should refresh the page once the indexation is complete', () => {
const reload = jest.fn();
delete window.location;
(window as any).location = { reload };

const wrapper = shallowRender();

expect(reload).not.toHaveBeenCalled();

wrapper.setProps({ indexationContext: { status: { isCompleted: true } } });
wrapper.update();

expect(reload).toHaveBeenCalled();
});

function shallowRender(props?: PageUnavailableDueToIndexation['props']) {
return shallow(
<PageUnavailableDueToIndexation
indexationContext={{
status: { isCompleted: false }
}}
pageContext={PageContext.Issues}
component={{ qualifier: ComponentQualifier.Portfolio, name: 'test-portfolio' }}
{...props}
/>
);
}

+ 40
- 0
server/sonar-web/src/main/js/app/components/indexation/__tests__/__snapshots__/PageUnavailableDueToIndexation-test.tsx.snap 查看文件

@@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<div
className="page-wrapper-simple"
>
<div
className="page-simple"
>
<h1
className="big-spacer-bottom"
>
<FormattedMessage
defaultMessage="indexation.page_unavailable.title.issues"
id="indexation.page_unavailable.title.issues"
values={
Object {
"componentName": <em>
test-portfolio
</em>,
"componentQualifier": "qualifier.VW",
}
}
/>
</h1>
<Alert
variant="info"
>
<p>
indexation.page_unavailable.description
</p>
<p
className="spacer-top"
>
indexation.page_unavailable.description.additional_information
</p>
</Alert>
</div>
</div>
`;

+ 6
- 1
server/sonar-web/src/main/js/app/utils/startReactApp.tsx 查看文件

@@ -65,8 +65,10 @@ import systemRoutes from '../../apps/system/routes';
import usersRoutes from '../../apps/users/routes';
import webAPIRoutes from '../../apps/web-api/routes';
import webhooksRoutes from '../../apps/webhooks/routes';
import withIndexationGuard from '../../components/hoc/withIndexationGuard';
import App from '../components/App';
import GlobalContainer from '../components/GlobalContainer';
import { PageContext } from '../components/indexation/PageUnavailableDueToIndexation';
import MigrationContainer from '../components/MigrationContainer';
import * as theme from '../theme';
import getStore from './getStore';
@@ -300,7 +302,10 @@ export default function startReactApp(
import('../components/extensions/GlobalPageExtension')
)}
/>
<Route path="issues" component={Issues} />
<Route
path="issues"
component={withIndexationGuard(Issues, PageContext.Issues)}
/>
<RouteWithChildRoutes path="organizations" childRoutes={organizationsRoutes} />
<RouteWithChildRoutes path="projects" childRoutes={projectsRoutes} />
<RouteWithChildRoutes path="quality_gates" childRoutes={qualityGatesRoutes} />

+ 50
- 0
server/sonar-web/src/main/js/components/hoc/__tests__/withIndexationGuard-test.tsx 查看文件

@@ -0,0 +1,50 @@
/*
* SonarQube
* Copyright (C) 2009-2020 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 { mount } from 'enzyme';
import * as React from 'react';
import { IndexationContext } from '../../../app/components/indexation/IndexationContext';
import { PageContext } from '../../../app/components/indexation/PageUnavailableDueToIndexation';
import { IndexationContextInterface } from '../../../types/indexation';
import withIndexationGuard from '../withIndexationGuard';

it('should render correctly', () => {
let wrapper = mountRender();
expect(wrapper.find(TestComponent).exists()).toBe(false);

wrapper = mountRender({ status: { isCompleted: true } });
expect(wrapper.find(TestComponent).exists()).toBe(true);
});

function mountRender(context?: Partial<IndexationContextInterface>) {
return mount(
<IndexationContext.Provider value={{ status: { isCompleted: false }, ...context }}>
<TestComponentWithGuard />
</IndexationContext.Provider>
);
}

class TestComponent extends React.PureComponent {
render() {
return <h1>TestComponent</h1>;
}
}

const TestComponentWithGuard = withIndexationGuard(TestComponent, PageContext.Issues);

+ 46
- 0
server/sonar-web/src/main/js/components/hoc/withIndexationGuard.tsx 查看文件

@@ -0,0 +1,46 @@
/*
* SonarQube
* Copyright (C) 2009-2020 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 * as React from 'react';
import { IndexationContext } from '../../app/components/indexation/IndexationContext';
import PageUnavailableDueToIndexation, {
PageContext
} from '../../app/components/indexation/PageUnavailableDueToIndexation';

export default function withIndexationGuard<P>(
WrappedComponent: React.ComponentType<P>,
pageContext: PageContext
) {
return class WithIndexationGuard extends React.PureComponent<P> {
render() {
return (
<IndexationContext.Consumer>
{context =>
context?.status.isCompleted ? (
<WrappedComponent {...this.props} />
) : (
<PageUnavailableDueToIndexation pageContext={pageContext} />
)
}
</IndexationContext.Consumer>
);
}
};
}

+ 1
- 0
server/sonar-web/src/main/js/types/types.d.ts 查看文件

@@ -126,6 +126,7 @@ declare namespace T {
isFavorite?: boolean;
leakPeriodDate?: string;
name: string;
needIssueSync?: boolean;
path?: string;
refKey?: string;
qualityProfiles?: ComponentQualityProfile[];

+ 5
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties 查看文件

@@ -3566,7 +3566,11 @@ indexation.in_progress=SonarQube is reloading project data. Some projects will b
indexation.in_progress.details={0}% completed.
indexation.in_progress.admin_details=See {link}.
indexation.completed=All project data has been reloaded.

indexation.page_unavailable.title.issues=Issues page is temporarily unavailable
indexation.page_unavailable.title.portfolios=Portfolios page is temporarily unavailable
indexation.page_unavailable.title={componentQualifier} {componentName} is temporarily unavailable
indexation.page_unavailable.description=This page will be available after the data is reloaded. This might take a while depending on the amount of projects and issues in your SonarQube instance.
indexation.page_unavailable.description.additional_information=You can keep analyzing your projects during this process.
#------------------------------------------------------------------------------
#
# HOMEPAGE

Loading…
取消
儲存