aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/maintenance
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2018-02-14 10:51:22 +0100
committerGitHub <noreply@github.com>2018-02-14 10:51:22 +0100
commit8053754d961994e78ed973d37005bb6b5e8ceeae (patch)
tree42dce69c3ca726158e88e95544846449c84f4469 /server/sonar-web/src/main/js/apps/maintenance
parent94a57989f8160badd5c1e7cbb66fdbceaa3bb38d (diff)
downloadsonarqube-8053754d961994e78ed973d37005bb6b5e8ceeae.tar.gz
sonarqube-8053754d961994e78ed973d37005bb6b5e8ceeae.zip
rewrite maintenance app in react (#3055)
Diffstat (limited to 'server/sonar-web/src/main/js/apps/maintenance')
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/components/App.tsx285
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/components/MaintenanceAppContainer.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/components/SetupAppContainer.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/components/__tests__/App-test.tsx134
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap399
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/init.js37
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/main-view.js95
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/routes.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-failed.hbs2
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-not-supported.hbs2
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-required.hbs7
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-running.hbs11
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-succeeded.hbs4
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-no-migration.hbs4
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-down.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-migration.hbs3
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-offline.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-starting.hbs2
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-up.hbs5
-rw-r--r--server/sonar-web/src/main/js/apps/maintenance/templates/maintenance-main.hbs32
20 files changed, 833 insertions, 238 deletions
diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx b/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
new file mode 100644
index 00000000000..3bc5e06b616
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/maintenance/components/App.tsx
@@ -0,0 +1,285 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 * as classNames from 'classnames';
+import { getMigrationStatus, getSystemStatus, migrateDatabase } from '../../../api/system';
+import DateFromNow from '../../../components/intl/DateFromNow';
+import TimeFormatter from '../../../components/intl/TimeFormatter';
+import { translate } from '../../../helpers/l10n';
+import { getBaseUrl } from '../../../helpers/urls';
+import '../styles.css';
+
+interface Props {
+ // eslint-disable-next-line camelcase
+ location: { query: { return_to?: string } };
+ setup: boolean;
+}
+
+interface State {
+ message?: string;
+ startedAt?: string;
+ state?: string;
+ status?: string;
+ wasStarting?: boolean;
+}
+
+export default class App extends React.PureComponent<Props, State> {
+ interval?: number;
+ mounted = false;
+ state: State = {};
+
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchStatus();
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ if (this.interval) {
+ window.clearInterval(this.interval);
+ }
+ }
+
+ fetchStatus = () => {
+ const request = this.props.setup ? this.fetchMigrationState() : this.fetchSystemStatus();
+ request.catch(() => {
+ if (this.mounted) {
+ this.setState({
+ message: undefined,
+ startedAt: undefined,
+ state: undefined,
+ status: 'OFFLINE'
+ });
+ }
+ });
+ };
+
+ fetchSystemStatus = () => {
+ return getSystemStatus().then(({ status }) => {
+ if (this.mounted) {
+ this.setState({ status });
+
+ if (status === 'STARTING') {
+ this.setState({ wasStarting: true });
+ this.scheduleRefresh();
+ } else if (status === 'UP') {
+ if (this.state.wasStarting) {
+ this.loadPreviousPage();
+ }
+ } else {
+ this.scheduleRefresh();
+ }
+ }
+ });
+ };
+
+ fetchMigrationState = () => {
+ return getMigrationStatus().then(({ message, startedAt, state }) => {
+ if (this.mounted) {
+ this.setState({ message, startedAt, state });
+ if (state === 'MIGRATION_SUCCEEDED') {
+ this.loadPreviousPage();
+ } else if (state !== 'NO_MIGRATION') {
+ this.scheduleRefresh();
+ }
+ }
+ });
+ };
+
+ scheduleRefresh = () => {
+ this.interval = window.setTimeout(this.fetchStatus, 5000);
+ };
+
+ loadPreviousPage = () => {
+ setInterval(() => {
+ window.location.href = this.props.location.query['return_to'] || getBaseUrl() + '/';
+ }, 2500);
+ };
+
+ handleMigrateClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ migrateDatabase().then(
+ ({ message, startedAt, state }) => {
+ if (this.mounted) {
+ this.setState({ message, startedAt, state });
+ }
+ },
+ () => {}
+ );
+ };
+
+ render() {
+ const { state, status } = this.state;
+
+ return (
+ <div className="page-wrapper-simple" id="bd">
+ <div
+ className={classNames('page-simple', { 'panel-warning': state === 'MIGRATION_REQUIRED' })}
+ id="nonav">
+ {status === 'OFFLINE' && (
+ <>
+ <h1 className="maintenance-title text-danger">
+ {translate('maintenance.sonarqube_is_offline')}
+ </h1>
+ <p className="maintenance-text">
+ {translate('maintenance.sonarqube_is_offline.text')}
+ </p>
+ <p className="maintenance-text text-center">
+ <a href={getBaseUrl() + '/'}>{translate('maintenance.try_again')}</a>
+ </p>
+ </>
+ )}
+
+ {status === 'UP' && (
+ <>
+ <h1 className="maintenance-title">{translate('maintenance.sonarqube_is_up')}</h1>
+ <p className="maintenance-text text-center">
+ {translate('maintenance.all_systems_opetational')}
+ </p>
+ <p className="maintenance-text text-center">
+ <a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
+ </p>
+ </>
+ )}
+
+ {status === 'STARTING' && (
+ <>
+ <h1 className="maintenance-title">
+ {translate('maintenance.sonarqube_is_starting')}
+ </h1>
+ <p className="maintenance-spinner">
+ <i className="spinner" />
+ </p>
+ </>
+ )}
+
+ {status === 'DOWN' && (
+ <>
+ <h1 className="maintenance-title text-danger">
+ {translate('maintenance.sonarqube_is_down')}
+ </h1>
+ <p className="maintenance-text">{translate('maintenance.sonarqube_is_down.text')}</p>
+ <p className="maintenance-text text-center">
+ <a href={getBaseUrl() + '/'}>{translate('maintenance.try_again')}</a>
+ </p>
+ </>
+ )}
+
+ {(status === 'DB_MIGRATION_NEEDED' || status === 'DB_MIGRATION_RUNNING') && (
+ <>
+ <h1 className="maintenance-title">
+ {translate('maintenance.sonarqube_is_under_maintenance')}
+ </h1>
+ <p
+ className="maintenance-text"
+ dangerouslySetInnerHTML={{
+ __html: translate('maintenance.sonarqube_is_under_maintenance.1')
+ }}
+ />
+ <p
+ className="maintenance-text"
+ dangerouslySetInnerHTML={{
+ __html: translate('maintenance.sonarqube_is_under_maintenance.2')
+ }}
+ />
+ </>
+ )}
+
+ {state === 'NO_MIGRATION' && (
+ <>
+ <h1 className="maintenance-title">
+ {translate('maintenance.database_is_up_to_date')}
+ </h1>
+ <p className="maintenance-text text-center">
+ <a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
+ </p>
+ </>
+ )}
+
+ {state === 'MIGRATION_REQUIRED' && (
+ <>
+ <h1 className="maintenance-title">{translate('maintenance.upgrade_database')}</h1>
+ <p className="maintenance-text">{translate('maintenance.upgrade_database.1')}</p>
+ <p className="maintenance-text">{translate('maintenance.upgrade_database.2')}</p>
+ <p className="maintenance-text">{translate('maintenance.upgrade_database.3')}</p>
+ <div className="maintenance-spinner">
+ <button id="start-migration" onClick={this.handleMigrateClick} type="button">
+ {translate('maintenance.upgrade')}
+ </button>
+ </div>
+ </>
+ )}
+
+ {state === 'NOT_SUPPORTED' && (
+ <>
+ <h1 className="maintenance-title text-danger">
+ {translate('maintenance.migration_not_supported')}
+ </h1>
+ <p>{translate('maintenance.migration_not_supported.text')}</p>
+ </>
+ )}
+
+ {state === 'MIGRATION_RUNNING' && (
+ <>
+ <h1 className="maintenance-title">{translate('maintenance.database_migration')}</h1>
+ {this.state.message && (
+ <p className="maintenance-text text-center">{this.state.message}</p>
+ )}
+ {this.state.startedAt && (
+ <p className="maintenance-text text-center">
+ {translate('background_tasks.table.started')}{' '}
+ <DateFromNow date={this.state.startedAt} />
+ <br />
+ <small className="text-muted">
+ <TimeFormatter date={this.state.startedAt} long={true} />
+ </small>
+ </p>
+ )}
+ <p className="maintenance-spinner">
+ <i className="spinner" />
+ </p>
+ </>
+ )}
+
+ {state === 'MIGRATION_SUCCEEDED' && (
+ <>
+ <h1 className="maintenance-title text-success">
+ {translate('maintenance.database_is_up_to_date')}
+ </h1>
+ <p className="maintenance-text text-center">
+ <a href={getBaseUrl() + '/'}>{translate('layout.home')}</a>
+ </p>
+ </>
+ )}
+
+ {state === 'MIGRATION_FAILED' && (
+ <>
+ <h1 className="maintenance-title text-danger">
+ {translate('maintenance.upgrade_failed')}
+ </h1>
+ <p className="maintenance-text">{translate('maintenance.upgrade_failed.text')}</p>
+ </>
+ )}
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/MaintenanceAppContainer.tsx b/server/sonar-web/src/main/js/apps/maintenance/components/MaintenanceAppContainer.tsx
index 38be383b830..1a84ccdcd6d 100644
--- a/server/sonar-web/src/main/js/apps/maintenance/components/MaintenanceAppContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/maintenance/components/MaintenanceAppContainer.tsx
@@ -18,19 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import init from '../init';
-import '../styles.css';
+import App from './App';
interface Props {
+ // eslint-disable-next-line camelcase
location: { query: { return_to: string } };
}
-export default class MaintenanceAppContainer extends React.PureComponent<Props> {
- componentDidMount() {
- init(this.refs.container, false, this.props.location.query['return_to']);
- }
-
- render() {
- return <div ref="container" />;
- }
+export default function MaintenanceAppContainer(props: Props) {
+ return <App setup={false} {...props} />;
}
diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/SetupAppContainer.tsx b/server/sonar-web/src/main/js/apps/maintenance/components/SetupAppContainer.tsx
index 46d68176189..4cd1e8fe5b5 100644
--- a/server/sonar-web/src/main/js/apps/maintenance/components/SetupAppContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/maintenance/components/SetupAppContainer.tsx
@@ -18,19 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import init from '../init';
-import '../styles.css';
+import App from './App';
interface Props {
+ // eslint-disable-next-line camelcase
location: { query: { return_to: string } };
}
-export default class SetupAppContainer extends React.PureComponent<Props> {
- componentDidMount() {
- init(this.refs.container, true, this.props.location.query['return_to']);
- }
-
- render() {
- return <div ref="container" />;
- }
+export default function MaintenanceAppContainer(props: Props) {
+ return <App setup={true} {...props} />;
}
diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/App-test.tsx
new file mode 100644
index 00000000000..8d4511d09e7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/App-test.tsx
@@ -0,0 +1,134 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+/* eslint-disable import/order */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import App from '../App';
+import { click, waitAndUpdate } from '../../../../helpers/testUtils';
+
+jest.mock('../../../../api/system', () => ({
+ getMigrationStatus: jest.fn(),
+ getSystemStatus: jest.fn(),
+ migrateDatabase: jest.fn()
+}));
+
+jest.useFakeTimers();
+
+const getMigrationStatus = require('../../../../api/system').getMigrationStatus as jest.Mock;
+const getSystemStatus = require('../../../../api/system').getSystemStatus as jest.Mock;
+const migrateDatabase = require('../../../../api/system').migrateDatabase as jest.Mock;
+
+const location = { query: {} };
+
+beforeEach(() => {
+ getMigrationStatus.mockClear();
+ getSystemStatus.mockClear();
+ migrateDatabase.mockClear();
+});
+
+afterEach(() => {
+ jest.clearAllTimers();
+});
+
+describe('Maintenance Page', () => {
+ ['UP', 'DOWN', 'STARTING', 'DB_MIGRATION_NEEDED', 'DB_MIGRATION_RUNNING'].forEach(status => {
+ it(`should render ${status} status`, async () => {
+ getSystemStatus.mockImplementationOnce(() => Promise.resolve({ status }));
+ await checkApp(false);
+ });
+ });
+
+ it('should render OFFLINE status', async () => {
+ getSystemStatus.mockImplementationOnce(() => Promise.reject(undefined));
+ await checkApp(false);
+ });
+
+ it('should poll status', async () => {
+ getSystemStatus.mockImplementationOnce(() =>
+ Promise.resolve({ status: 'DB_MIGRATION_RUNNING' })
+ );
+ const wrapper = shallow(<App location={location} setup={false} />);
+ await waitAndUpdate(wrapper);
+ expect(getSystemStatus).toBeCalled();
+
+ getSystemStatus.mockClear();
+ getSystemStatus.mockImplementationOnce(() =>
+ Promise.resolve({ status: 'DB_MIGRATION_RUNNING' })
+ );
+ jest.runOnlyPendingTimers();
+ await waitAndUpdate(wrapper);
+ expect(getSystemStatus).toBeCalled();
+
+ getSystemStatus.mockClear();
+ getSystemStatus.mockImplementationOnce(() =>
+ Promise.resolve({ status: 'DB_MIGRATION_RUNNING' })
+ );
+ jest.runOnlyPendingTimers();
+ await waitAndUpdate(wrapper);
+ expect(getSystemStatus).toBeCalled();
+ });
+
+ it('should open previous page', async () => {
+ getSystemStatus.mockImplementationOnce(() => Promise.resolve({ status: 'STARTING' }));
+ const wrapper = shallow(<App location={location} setup={false} />);
+ const loadPreviousPage = jest.fn();
+ (wrapper.instance() as App).loadPreviousPage = loadPreviousPage;
+ await waitAndUpdate(wrapper);
+
+ getSystemStatus.mockImplementationOnce(() => Promise.resolve({ status: 'UP' }));
+ jest.runOnlyPendingTimers();
+ await waitAndUpdate(wrapper);
+ expect(loadPreviousPage).toBeCalled();
+ });
+});
+
+describe('Setup Page', () => {
+ ['NO_MIGRATION', 'NOT_SUPPORTED', 'MIGRATION_SUCCEEDED', 'MIGRATION_FAILED'].forEach(state => {
+ it(`should render ${state} state`, async () => {
+ getMigrationStatus.mockImplementationOnce(() =>
+ Promise.resolve({ message: 'message', startedAt: '2017-01-02T00:00:00.000Z', state })
+ );
+ await checkApp(true);
+ });
+ });
+
+ it('should start migration', async () => {
+ getMigrationStatus.mockImplementationOnce(() =>
+ Promise.resolve({ state: 'MIGRATION_REQUIRED' })
+ );
+ migrateDatabase.mockImplementationOnce(() =>
+ Promise.resolve({ startedAt: '2017-01-02T00:00:00.000Z', state: 'MIGRATION_RUNNING' })
+ );
+ const wrapper = shallow(<App location={location} setup={true} />);
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+
+ click(wrapper.find('button'));
+ expect(migrateDatabase).toBeCalled();
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+ });
+});
+
+async function checkApp(setup: boolean) {
+ const wrapper = shallow(<App location={location} setup={setup} />);
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+}
diff --git a/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap
new file mode 100644
index 00000000000..784a8eb1969
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/maintenance/components/__tests__/__snapshots__/App-test.tsx.snap
@@ -0,0 +1,399 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Maintenance Page should render DB_MIGRATION_NEEDED status 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title"
+ >
+ maintenance.sonarqube_is_under_maintenance
+ </h1>
+ <p
+ className="maintenance-text"
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "maintenance.sonarqube_is_under_maintenance.1",
+ }
+ }
+ />
+ <p
+ className="maintenance-text"
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "maintenance.sonarqube_is_under_maintenance.2",
+ }
+ }
+ />
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Maintenance Page should render DB_MIGRATION_RUNNING status 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title"
+ >
+ maintenance.sonarqube_is_under_maintenance
+ </h1>
+ <p
+ className="maintenance-text"
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "maintenance.sonarqube_is_under_maintenance.1",
+ }
+ }
+ />
+ <p
+ className="maintenance-text"
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "maintenance.sonarqube_is_under_maintenance.2",
+ }
+ }
+ />
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Maintenance Page should render DOWN status 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title text-danger"
+ >
+ maintenance.sonarqube_is_down
+ </h1>
+ <p
+ className="maintenance-text"
+ >
+ maintenance.sonarqube_is_down.text
+ </p>
+ <p
+ className="maintenance-text text-center"
+ >
+ <a
+ href="/"
+ >
+ maintenance.try_again
+ </a>
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Maintenance Page should render OFFLINE status 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title text-danger"
+ >
+ maintenance.sonarqube_is_offline
+ </h1>
+ <p
+ className="maintenance-text"
+ >
+ maintenance.sonarqube_is_offline.text
+ </p>
+ <p
+ className="maintenance-text text-center"
+ >
+ <a
+ href="/"
+ >
+ maintenance.try_again
+ </a>
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Maintenance Page should render STARTING status 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title"
+ >
+ maintenance.sonarqube_is_starting
+ </h1>
+ <p
+ className="maintenance-spinner"
+ >
+ <i
+ className="spinner"
+ />
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Maintenance Page should render UP status 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title"
+ >
+ maintenance.sonarqube_is_up
+ </h1>
+ <p
+ className="maintenance-text text-center"
+ >
+ maintenance.all_systems_opetational
+ </p>
+ <p
+ className="maintenance-text text-center"
+ >
+ <a
+ href="/"
+ >
+ layout.home
+ </a>
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Setup Page should render MIGRATION_FAILED state 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title text-danger"
+ >
+ maintenance.upgrade_failed
+ </h1>
+ <p
+ className="maintenance-text"
+ >
+ maintenance.upgrade_failed.text
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Setup Page should render MIGRATION_SUCCEEDED state 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title text-success"
+ >
+ maintenance.database_is_up_to_date
+ </h1>
+ <p
+ className="maintenance-text text-center"
+ >
+ <a
+ href="/"
+ >
+ layout.home
+ </a>
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Setup Page should render NO_MIGRATION state 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title"
+ >
+ maintenance.database_is_up_to_date
+ </h1>
+ <p
+ className="maintenance-text text-center"
+ >
+ <a
+ href="/"
+ >
+ layout.home
+ </a>
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Setup Page should render NOT_SUPPORTED state 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title text-danger"
+ >
+ maintenance.migration_not_supported
+ </h1>
+ <p>
+ maintenance.migration_not_supported.text
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Setup Page should start migration 1`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple panel-warning"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title"
+ >
+ maintenance.upgrade_database
+ </h1>
+ <p
+ className="maintenance-text"
+ >
+ maintenance.upgrade_database.1
+ </p>
+ <p
+ className="maintenance-text"
+ >
+ maintenance.upgrade_database.2
+ </p>
+ <p
+ className="maintenance-text"
+ >
+ maintenance.upgrade_database.3
+ </p>
+ <div
+ className="maintenance-spinner"
+ >
+ <button
+ id="start-migration"
+ onClick={[Function]}
+ type="button"
+ >
+ maintenance.upgrade
+ </button>
+ </div>
+ </React.Fragment>
+ </div>
+</div>
+`;
+
+exports[`Setup Page should start migration 2`] = `
+<div
+ className="page-wrapper-simple"
+ id="bd"
+>
+ <div
+ className="page-simple"
+ id="nonav"
+ >
+ <React.Fragment>
+ <h1
+ className="maintenance-title"
+ >
+ maintenance.database_migration
+ </h1>
+ <p
+ className="maintenance-text text-center"
+ >
+ background_tasks.table.started
+
+ <DateFromNow
+ date="2017-01-02T00:00:00.000Z"
+ />
+ <br />
+ <small
+ className="text-muted"
+ >
+ <TimeFormatter
+ date="2017-01-02T00:00:00.000Z"
+ long={true}
+ />
+ </small>
+ </p>
+ <p
+ className="maintenance-spinner"
+ >
+ <i
+ className="spinner"
+ />
+ </p>
+ </React.Fragment>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/maintenance/init.js b/server/sonar-web/src/main/js/apps/maintenance/init.js
deleted file mode 100644
index 15c84c25ed9..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/init.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 Backbone from 'backbone';
-import Marionette from 'backbone.marionette';
-import MainView from './main-view';
-
-const App = new Marionette.Application();
-
-App.on('start', options => {
- const viewOptions = {
- ...options,
- model: new Backbone.Model()
- };
- const mainView = new MainView(viewOptions);
- mainView.render().refresh();
-});
-
-export default function(el, setup, returnTo) {
- App.start({ el, setup, returnTo });
-}
diff --git a/server/sonar-web/src/main/js/apps/maintenance/main-view.js b/server/sonar-web/src/main/js/apps/maintenance/main-view.js
deleted file mode 100644
index da558206ba2..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/main-view.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 Marionette from 'backbone.marionette';
-import Template from './templates/maintenance-main.hbs';
-import { getSystemStatus, getMigrationStatus, migrateDatabase } from '../../api/system';
-import { getBaseUrl } from '../../helpers/urls';
-
-export default Marionette.ItemView.extend({
- template: Template,
-
- events: {
- 'click #start-migration': 'startMigration'
- },
-
- initialize() {
- this.pollingInternal = setInterval(() => {
- this.refresh();
- }, 5000);
- this.wasStarting = false;
- },
-
- getStatus() {
- return this.options.setup ? getMigrationStatus() : getSystemStatus();
- },
-
- refresh() {
- return this.getStatus().then(
- r => {
- if (r.status === 'STARTING') {
- this.wasStarting = true;
- }
- // unset `status` in case if was `OFFLINE` previously
- this.model.set({ status: undefined, ...r });
- this.render();
- if (this.model.get('status') === 'UP' || this.model.get('state') === 'NO_MIGRATION') {
- this.stopPolling();
- }
- if (this.model.get('status') === 'UP' && this.wasStarting) {
- this.loadPreviousPage();
- }
- if (this.model.get('state') === 'MIGRATION_SUCCEEDED') {
- this.loadPreviousPage();
- }
- },
- () => {
- this.model.set({ status: 'OFFLINE' });
- this.render();
- }
- );
- },
-
- stopPolling() {
- clearInterval(this.pollingInternal);
- },
-
- startMigration() {
- migrateDatabase().then(
- r => {
- this.model.set(r);
- this.render();
- },
- () => {}
- );
- },
-
- loadPreviousPage() {
- setInterval(() => {
- window.location = this.options.returnTo || getBaseUrl();
- }, 2500);
- },
-
- serializeData() {
- return {
- ...Marionette.ItemView.prototype.serializeData.apply(this, arguments),
- setup: this.options.setup
- };
- }
-});
diff --git a/server/sonar-web/src/main/js/apps/maintenance/routes.tsx b/server/sonar-web/src/main/js/apps/maintenance/routes.tsx
index 7ef353bbe4a..ac4c46ec21c 100644
--- a/server/sonar-web/src/main/js/apps/maintenance/routes.tsx
+++ b/server/sonar-web/src/main/js/apps/maintenance/routes.tsx
@@ -19,9 +19,12 @@
*/
import * as React from 'react';
import { IndexRoute } from 'react-router';
-import MaintenanceAppContainer from './components/MaintenanceAppContainer';
-import SetupAppContainer from './components/SetupAppContainer';
+import { lazyLoad } from '../../components/lazyLoad';
-export const maintenanceRoutes = <IndexRoute component={MaintenanceAppContainer} />;
+export const maintenanceRoutes = (
+ <IndexRoute component={lazyLoad(() => import('./components/MaintenanceAppContainer'))} />
+);
-export const setupRoutes = <IndexRoute component={SetupAppContainer} />;
+export const setupRoutes = (
+ <IndexRoute component={lazyLoad(() => import('./components/SetupAppContainer'))} />
+);
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-failed.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-failed.hbs
deleted file mode 100644
index ab1421b6b7e..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-failed.hbs
+++ /dev/null
@@ -1,2 +0,0 @@
-<h1 class="maintenance-title text-danger">{{t 'maintenance.upgrade_failed'}}</h1>
-<p class="maintenance-text">{{t 'maintenance.upgrade_failed.text'}}</p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-not-supported.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-not-supported.hbs
deleted file mode 100644
index 6d023044313..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-not-supported.hbs
+++ /dev/null
@@ -1,2 +0,0 @@
-<h1 class="maintenance-title text-danger">{{t 'maintenance.migration_not_supported'}}</h1>
-<p>{{t 'maintenance.migration_not_supported.text'}}</p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-required.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-required.hbs
deleted file mode 100644
index 3236ff5a80f..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-required.hbs
+++ /dev/null
@@ -1,7 +0,0 @@
-<h1 class="maintenance-title">{{t 'maintenance.upgrade_database'}}</h1>
-<p class="maintenance-text">{{t 'maintenance.upgrade_database.1'}}</p>
-<p class="maintenance-text">{{t 'maintenance.upgrade_database.2'}}</p>
-<p class="maintenance-text">{{t 'maintenance.upgrade_database.3'}}</p>
-<div class="maintenance-spinner">
- <button id="start-migration">{{t 'maintenance.upgrade'}}</button>
-</div>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-running.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-running.hbs
deleted file mode 100644
index c69baef7434..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-running.hbs
+++ /dev/null
@@ -1,11 +0,0 @@
-<h1 class="maintenance-title">{{t 'maintenance.database_migration'}}</h1>
-{{#if message}}
- <p class="maintenance-text text-center">{{message}}</p>
-{{/if}}
-{{#if startedAt}}
- <p class="maintenance-text text-center">
- {{t 'background_tasks.table.started'}} {{fromNow startedAt}}<br>
- <small class="text-muted">{{dt startedAt}}</small>
- </p>
-{{/if}}
-<p class="maintenance-spinner"><i class="spinner"></i></p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-succeeded.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-succeeded.hbs
deleted file mode 100644
index 7e70e68cf30..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-migration-succeeded.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-<h1 class="maintenance-title text-success">{{t 'maintenance.database_is_up_to_date'}}</h1>
-<p class="maintenance-text text-center">
- <a href="{{link '/'}}">{{t 'layout.home'}}</a>
-</p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-no-migration.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-no-migration.hbs
deleted file mode 100644
index 6c30631c9a5..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-state-no-migration.hbs
+++ /dev/null
@@ -1,4 +0,0 @@
-<h1 class="maintenance-title">{{t 'maintenance.database_is_up_to_date'}}</h1>
-<p class="maintenance-text text-center">
- <a href="{{link '/'}}">{{t 'layout.home'}}</a>
-</p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-down.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-down.hbs
deleted file mode 100644
index 811aa16ef5b..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-down.hbs
+++ /dev/null
@@ -1,5 +0,0 @@
-<h1 class="maintenance-title text-danger">{{t 'maintenance.sonarqube_is_down'}}</h1>
-<p class="maintenance-text">{{t 'maintenance.sonarqube_is_down.text'}}</p>
-<p class="maintenance-text text-center">
- <a href="{{link '/'}}">{{t 'maintenance.try_again'}}</a>
-</p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-migration.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-migration.hbs
deleted file mode 100644
index f51f4c72475..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-migration.hbs
+++ /dev/null
@@ -1,3 +0,0 @@
-<h1 class="maintenance-title">{{t 'maintenance.sonarqube_is_under_maintenance'}}</h1>
-<p class="maintenance-text">{{{t 'maintenance.sonarqube_is_under_maintenance.1'}}}</p>
-<p class="maintenance-text">{{{t 'maintenance.sonarqube_is_under_maintenance.2'}}}</p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-offline.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-offline.hbs
deleted file mode 100644
index 6e7bac35c3f..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-offline.hbs
+++ /dev/null
@@ -1,5 +0,0 @@
-<h1 class="maintenance-title text-danger">{{t 'maintenance.sonarqube_is_offline'}}</h1>
-<p class="maintenance-text">{{t 'maintenance.sonarqube_is_offline.text'}}</p>
-<p class="maintenance-text text-center">
- <a href="{{link '/'}}">{{t 'maintenance.try_again'}}</a>
-</p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-starting.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-starting.hbs
deleted file mode 100644
index 96f7d89f122..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-starting.hbs
+++ /dev/null
@@ -1,2 +0,0 @@
-<h1 class="maintenance-title">{{t 'maintenance.sonarqube_is_starting'}}</h1>
-<p class="maintenance-spinner"><i class="spinner"></i></p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-up.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-up.hbs
deleted file mode 100644
index e97b550f342..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/_maintenance-status-up.hbs
+++ /dev/null
@@ -1,5 +0,0 @@
-<h1 class="maintenance-title">{{t 'maintenance.sonarqube_is_up'}}</h1>
-<p class="maintenance-text text-center">{{t 'maintenance.all_systems_opetational'}}</p>
-<p class="maintenance-text text-center">
- <a href="{{link '/'}}">{{t 'layout.home'}}</a>
-</p>
diff --git a/server/sonar-web/src/main/js/apps/maintenance/templates/maintenance-main.hbs b/server/sonar-web/src/main/js/apps/maintenance/templates/maintenance-main.hbs
deleted file mode 100644
index 44a4bc8798d..00000000000
--- a/server/sonar-web/src/main/js/apps/maintenance/templates/maintenance-main.hbs
+++ /dev/null
@@ -1,32 +0,0 @@
-<div id="bd" class="page-wrapper-simple">
- <div id="nonav" class="page-simple {{#eq state 'MIGRATION_REQUIRED'}}panel-warning{{/eq}}">
-
- {{#eq status 'OFFLINE'}}
-
- {{> '_maintenance-status-offline'}}
-
- {{else}}
-
- {{#unless setup}}
-
- {{#eq status 'UP'}}{{> '_maintenance-status-up'}}{{/eq}}
- {{#eq status 'STARTING'}}{{> '_maintenance-status-starting'}}{{/eq}}
- {{#eq status 'DOWN'}}{{> '_maintenance-status-down'}}{{/eq}}
- {{#eq status 'DB_MIGRATION_NEEDED'}}{{> '_maintenance-status-migration'}}{{/eq}}
- {{#eq status 'DB_MIGRATION_RUNNING'}}{{> '_maintenance-status-migration'}}{{/eq}}
-
- {{else}}
-
- {{#eq state 'NO_MIGRATION'}}{{> '_maintenance-state-no-migration'}}{{/eq}}
- {{#eq state 'MIGRATION_REQUIRED'}}{{> '_maintenance-state-migration-required'}}{{/eq}}
- {{#eq state 'NOT_SUPPORTED'}}{{> '_maintenance-state-migration-not-supported'}}{{/eq}}
- {{#eq state 'MIGRATION_RUNNING'}}{{> '_maintenance-state-migration-running'}}{{/eq}}
- {{#eq state 'MIGRATION_SUCCEEDED'}}{{> '_maintenance-state-migration-succeeded'}}{{/eq}}
- {{#eq state 'MIGRATION_FAILED'}}{{> '_maintenance-state-migration-failed'}}{{/eq}}
-
- {{/unless}}
-
- {{/eq}}
-
- </div>
-</div>