aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-10-18 16:46:15 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-10-23 08:01:13 -0700
commitcc18c716b7b930b7b93c94218e8947755b2dbe87 (patch)
tree0db2d6bd4561c8c7d272f5ef08486c187d25ba8b /server
parent13b8590acb21f58d0485729425cff724dee3d999 (diff)
downloadsonarqube-cc18c716b7b930b7b93c94218e8947755b2dbe87.tar.gz
sonarqube-cc18c716b7b930b7b93c94218e8947755b2dbe87.zip
SONAR-10001 Add dialog box to uninstall and edition
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/api/marketplace.ts7
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx56
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx60
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx103
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx71
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap3
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap50
10 files changed, 311 insertions, 48 deletions
diff --git a/server/sonar-web/src/main/js/api/marketplace.ts b/server/sonar-web/src/main/js/api/marketplace.ts
index 4cff1587d94..30c801ae0f2 100644
--- a/server/sonar-web/src/main/js/api/marketplace.ts
+++ b/server/sonar-web/src/main/js/api/marketplace.ts
@@ -41,7 +41,8 @@ export interface EditionStatus {
| 'AUTOMATIC_IN_PROGRESS'
| 'MANUAL_IN_PROGRESS'
| 'AUTOMATIC_READY'
- | 'AUTOMATIC_FAILURE';
+ | 'AUTOMATIC_FAILURE'
+ | 'UNINSTALL_IN_PROGRESS';
}
export function getEditionStatus(): Promise<EditionStatus> {
@@ -67,3 +68,7 @@ export function getLicensePreview(data: {
export function applyLicense(data: { license: string }): Promise<EditionStatus> {
return postJSON('/api/editions/apply_license', data).catch(throwGlobalError);
}
+
+export function uninstallEdition(): Promise<void | Response> {
+ return postJSON('/api/editions/uninstall').catch(throwGlobalError);
+}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
index 88fc6f4cebc..112205c8d36 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
@@ -21,6 +21,7 @@ import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import EditionBox from './components/EditionBox';
import LicenseEditionForm from './components/LicenseEditionForm';
+import UninstallEditionForm from './components/UninstallEditionForm';
import { Edition, EditionStatus, getEditionsList } from '../../api/marketplace';
import { getEditionsForVersion } from './utils';
import { translate } from '../../helpers/l10n';
@@ -38,11 +39,12 @@ interface State {
editionsError: boolean;
loading: boolean;
installEdition?: Edition;
+ openUninstallForm: boolean;
}
export default class EditionBoxes extends React.PureComponent<Props, State> {
mounted: boolean;
- state: State = { editionsError: false, loading: true };
+ state: State = { editionsError: false, loading: true, openUninstallForm: false };
componentDidMount() {
this.mounted = true;
@@ -76,14 +78,19 @@ export default class EditionBoxes extends React.PureComponent<Props, State> {
handleOpenLicenseForm = (edition: Edition) => this.setState({ installEdition: edition });
handleCloseLicenseForm = () => this.setState({ installEdition: undefined });
+ handleOpenUninstallForm = () => this.setState({ openUninstallForm: true });
+ handleCloseUninstallForm = () => this.setState({ openUninstallForm: false });
+
render() {
- const { editions, editionsError, loading, installEdition } = this.state;
+ const { editionStatus } = this.props;
+ const { editions, editionsError, loading, installEdition, openUninstallForm } = this.state;
if (loading) {
return <i className="big-spacer-bottom spinner" />;
}
- return (
- <div className="spacer-bottom marketplace-editions">
- {!editions || editionsError ? (
+
+ if (!editions || editionsError) {
+ return (
+ <div className="spacer-bottom marketplace-editions">
<span className="alert alert-info">
<FormattedMessage
defaultMessage={translate('marketplace.editions_unavailable')}
@@ -97,19 +104,23 @@ export default class EditionBoxes extends React.PureComponent<Props, State> {
}}
/>
</span>
- ) : (
- editions.map(edition => (
- <EditionBox
- edition={edition}
- editionStatus={this.props.editionStatus}
- key={edition.key}
- onInstall={this.handleOpenLicenseForm}
- />
- ))
- )}
+ </div>
+ );
+ }
- {editions &&
- installEdition && (
+ return (
+ <div className="spacer-bottom marketplace-editions">
+ {editions.map(edition => (
+ <EditionBox
+ edition={edition}
+ editionStatus={editionStatus}
+ key={edition.key}
+ onInstall={this.handleOpenLicenseForm}
+ onUninstall={this.handleOpenUninstallForm}
+ />
+ ))}
+
+ {installEdition && (
<LicenseEditionForm
edition={installEdition}
editions={editions}
@@ -117,6 +128,17 @@ export default class EditionBoxes extends React.PureComponent<Props, State> {
updateEditionStatus={this.props.updateEditionStatus}
/>
)}
+
+ {openUninstallForm &&
+ editionStatus &&
+ editionStatus.currentEditionKey && (
+ <UninstallEditionForm
+ edition={editions.find(edition => edition.key === editionStatus.currentEditionKey)}
+ editionStatus={editionStatus}
+ onClose={this.handleCloseUninstallForm}
+ updateEditionStatus={this.props.updateEditionStatus}
+ />
+ )}
</div>
);
}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap
index f6b1475c0e6..59582a7e13d 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap
@@ -54,6 +54,7 @@ exports[`should display the edition boxes 2`] = `
}
}
onInstall={[Function]}
+ onUninstall={[Function]}
/>
<EditionBox
edition={
@@ -74,6 +75,7 @@ exports[`should display the edition boxes 2`] = `
}
}
onInstall={[Function]}
+ onUninstall={[Function]}
/>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
index 91142a45cf7..8ca571c0bfc 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
@@ -26,6 +26,7 @@ interface Props {
edition: Edition;
editionStatus?: EditionStatus;
onInstall: (edition: Edition) => void;
+ onUninstall: () => void;
}
export default class EditionBox extends React.PureComponent<Props> {
@@ -68,7 +69,10 @@ export default class EditionBox extends React.PureComponent<Props> {
</button>
)}
{isInstalled && (
- <button className="button-red" disabled={installInProgress}>
+ <button
+ className="button-red"
+ disabled={installInProgress}
+ onClick={this.props.onUninstall}>
{translate('marketplace.uninstall')}
</button>
)}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx
index 312f0360afd..000eb5fb85e 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionsStatusNotif.tsx
@@ -37,35 +37,37 @@ export default class EditionsStatusNotif extends React.PureComponent<Props, Stat
hanleCloseRestart = () => this.setState({ openRestart: false });
render() {
- const { editionStatus } = this.props;
- if (editionStatus.installationStatus === 'AUTOMATIC_IN_PROGRESS') {
- return (
- <div className="alert alert-info">
- <i className="spinner spacer-right text-bottom" />
- <span>{translate('marketplace.status.AUTOMATIC_IN_PROGRESS')}</span>
- </div>
- );
- } else if (editionStatus.installationStatus === 'AUTOMATIC_READY') {
- return (
- <div className="alert alert-success">
- <span>{translate('marketplace.status.AUTOMATIC_READY')}</span>
- <button className="js-restart spacer-left" onClick={this.handleOpenRestart}>
- {translate('marketplace.restart')}
- </button>
- {this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />}
- </div>
- );
- } else if (
- ['MANUAL_IN_PROGRESS', 'AUTOMATIC_FAILURE'].includes(editionStatus.installationStatus)
- ) {
- return (
- <div className="alert alert-danger">
- {translate('marketplace.status', editionStatus.installationStatus)}
- <a className="little-spacer-left" href="https://www.sonarsource.com" target="_blank">
- {translate('marketplace.how_to_install')}
- </a>
- </div>
- );
+ const { installationStatus } = this.props.editionStatus;
+
+ switch (installationStatus) {
+ case 'AUTOMATIC_IN_PROGRESS':
+ return (
+ <div className="alert alert-info">
+ <i className="spinner spacer-right text-bottom" />
+ <span>{translate('marketplace.status.AUTOMATIC_IN_PROGRESS')}</span>
+ </div>
+ );
+ case 'AUTOMATIC_READY':
+ case 'UNINSTALL_IN_PROGRESS':
+ return (
+ <div className="alert alert-success">
+ <span>{translate('marketplace.status', installationStatus)}</span>
+ <button className="js-restart spacer-left" onClick={this.handleOpenRestart}>
+ {translate('marketplace.restart')}
+ </button>
+ {this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />}
+ </div>
+ );
+ case 'MANUAL_IN_PROGRESS':
+ case 'AUTOMATIC_FAILURE':
+ return (
+ <div className="alert alert-danger">
+ {translate('marketplace.status', installationStatus)}
+ <a className="little-spacer-left" href="https://www.sonarsource.com" target="_blank">
+ {translate('marketplace.how_to_install')}
+ </a>
+ </div>
+ );
}
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx
new file mode 100644
index 00000000000..705d02f3907
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx
@@ -0,0 +1,103 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Modal from 'react-modal';
+import { Edition, EditionStatus, uninstallEdition } from '../../../api/marketplace';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+
+export interface Props {
+ edition?: Edition;
+ editionStatus: EditionStatus;
+ onClose: () => void;
+ updateEditionStatus: (editionStatus: EditionStatus) => void;
+}
+
+interface State {
+ loading: boolean;
+}
+
+export default class UninstallEditionForm extends React.PureComponent<Props, State> {
+ mounted: boolean;
+ state: State = { loading: false };
+
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ handleCancelClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
+ this.props.onClose();
+ };
+
+ handleConfirmClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ this.setState({ loading: true });
+ uninstallEdition()
+ .then(() => {
+ this.props.updateEditionStatus({
+ ...this.props.editionStatus,
+ installationStatus: 'UNINSTALL_IN_PROGRESS'
+ });
+ this.props.onClose();
+ })
+ .catch(() => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ });
+ };
+
+ render() {
+ const { edition } = this.props;
+ const { loading } = this.state;
+ const currentEdition = edition ? edition.name : translate('marketplace.commercial_edition');
+ const header = translateWithParameters('marketplace.uninstall_x', currentEdition);
+ return (
+ <Modal
+ isOpen={true}
+ contentLabel={header}
+ className="modal"
+ overlayClassName="modal-overlay"
+ onRequestClose={this.props.onClose}>
+ <header className="modal-head">
+ <h2>{header}</h2>
+ </header>
+
+ <div className="modal-body">
+ <p>{translateWithParameters('marketplace.uninstall_x_confirmation', currentEdition)}</p>
+ </div>
+
+ <footer className="modal-foot">
+ {loading && <i className="spinner spacer-right" />}
+ <button className="button-red" disabled={loading} onClick={this.handleConfirmClick}>
+ {translate('marketplace.uninstall')}
+ </button>
+ <a className="js-modal-close" href="#" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </footer>
+ </Modal>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx
index c1a000d33ad..0eb4f92f95d 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx
@@ -116,6 +116,7 @@ function getWrapper(props = {}) {
edition={DEFAULT_EDITION}
editionStatus={DEFAULT_STATUS}
onInstall={jest.fn()}
+ onUninstall={jest.fn()}
{...props}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx
new file mode 100644
index 00000000000..037c2d33cd2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx
@@ -0,0 +1,71 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { shallow } from 'enzyme';
+import { click } from '../../../../helpers/testUtils';
+import UninstallEditionForm from '../UninstallEditionForm';
+
+jest.mock('../../../../api/marketplace', () => ({
+ uninstallEdition: jest.fn(() => Promise.resolve())
+}));
+
+const uninstallEdition = require('../../../../api/marketplace').uninstallEdition as jest.Mock<any>;
+
+const DEFAULT_EDITION = {
+ key: 'foo',
+ name: 'Foo',
+ desc: 'Foo desc',
+ download_link: 'download_url',
+ more_link: 'more_url',
+ request_license_link: 'license_url'
+};
+
+beforeEach(() => {
+ uninstallEdition.mockClear();
+});
+
+it('should display correctly', () => {
+ expect(getWrapper()).toMatchSnapshot();
+});
+
+it('should update the edition status after uninstall', async () => {
+ const updateEditionStatus = jest.fn();
+ const wrapper = getWrapper({ updateEditionStatus });
+ (wrapper.instance() as UninstallEditionForm).mounted = true;
+ click(wrapper.find('button'));
+ expect(uninstallEdition).toHaveBeenCalled();
+ await new Promise(setImmediate);
+ expect(updateEditionStatus).toHaveBeenCalledWith({
+ currentEditionKey: 'foo',
+ installationStatus: 'UNINSTALL_IN_PROGRESS'
+ });
+});
+
+function getWrapper(props = {}) {
+ return shallow(
+ <UninstallEditionForm
+ edition={DEFAULT_EDITION}
+ editionStatus={{ currentEditionKey: 'foo', installationStatus: 'NONE' }}
+ onClose={jest.fn()}
+ updateEditionStatus={jest.fn()}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
index 6aabd158c3a..7b8a8eaa902 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
@@ -107,6 +107,7 @@ exports[`should disable uninstall button 1`] = `
<button
className="button-red"
disabled={true}
+ onClick={[Function]}
>
marketplace.uninstall
</button>
@@ -149,6 +150,7 @@ exports[`should display installed badge 1`] = `
<button
className="button-red"
disabled={false}
+ onClick={[Function]}
>
marketplace.uninstall
</button>
@@ -187,6 +189,7 @@ exports[`should display installing badge 1`] = `
<button
className="button-red"
disabled={true}
+ onClick={[Function]}
>
marketplace.uninstall
</button>
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap
new file mode 100644
index 00000000000..507f7e4fe73
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap
@@ -0,0 +1,50 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display correctly 1`] = `
+<Modal
+ ariaHideApp={true}
+ bodyOpenClassName="ReactModal__Body--open"
+ className="modal"
+ closeTimeoutMS={0}
+ contentLabel="marketplace.uninstall_x.Foo"
+ isOpen={true}
+ onRequestClose={[Function]}
+ overlayClassName="modal-overlay"
+ parentSelector={[Function]}
+ portalClassName="ReactModalPortal"
+ shouldCloseOnOverlayClick={true}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ marketplace.uninstall_x.Foo
+ </h2>
+ </header>
+ <div
+ className="modal-body"
+ >
+ <p>
+ marketplace.uninstall_x_confirmation.Foo
+ </p>
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ className="button-red"
+ disabled={false}
+ onClick={[Function]}
+ >
+ marketplace.uninstall
+ </button>
+ <a
+ className="js-modal-close"
+ href="#"
+ onClick={[Function]}
+ >
+ cancel
+ </a>
+ </footer>
+</Modal>
+`;