From cc18c716b7b930b7b93c94218e8947755b2dbe87 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Wed, 18 Oct 2017 16:46:15 +0200 Subject: [PATCH] SONAR-10001 Add dialog box to uninstall and edition --- .../sonar-web/src/main/js/api/marketplace.ts | 7 +- .../main/js/apps/marketplace/EditionBoxes.tsx | 56 +++++++--- .../__snapshots__/EditionBoxes-test.tsx.snap | 2 + .../marketplace/components/EditionBox.tsx | 6 +- .../components/EditionsStatusNotif.tsx | 60 +++++----- .../components/UninstallEditionForm.tsx | 103 ++++++++++++++++++ .../components/__tests__/EditionBox-test.tsx | 1 + .../__tests__/UninstallEditionForm-test.tsx | 71 ++++++++++++ .../__snapshots__/EditionBox-test.tsx.snap | 3 + .../UninstallEditionForm-test.tsx.snap | 50 +++++++++ .../resources/org/sonar/l10n/core.properties | 3 + 11 files changed, 314 insertions(+), 48 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx create mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap 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 { @@ -67,3 +68,7 @@ export function getLicensePreview(data: { export function applyLicense(data: { license: string }): Promise { return postJSON('/api/editions/apply_license', data).catch(throwGlobalError); } + +export function uninstallEdition(): Promise { + 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 { 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 { 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 ; } - return ( -
- {!editions || editionsError ? ( + + if (!editions || editionsError) { + return ( +
{ }} /> - ) : ( - editions.map(edition => ( - - )) - )} +
+ ); + } - {editions && - installEdition && ( + return ( +
+ {editions.map(edition => ( + + ))} + + {installEdition && ( { updateEditionStatus={this.props.updateEditionStatus} /> )} + + {openUninstallForm && + editionStatus && + editionStatus.currentEditionKey && ( + edition.key === editionStatus.currentEditionKey)} + editionStatus={editionStatus} + onClose={this.handleCloseUninstallForm} + updateEditionStatus={this.props.updateEditionStatus} + /> + )}
); } 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]} />
`; 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 { @@ -68,7 +69,10 @@ export default class EditionBox extends React.PureComponent { )} {isInstalled && ( - )} 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 this.setState({ openRestart: false }); render() { - const { editionStatus } = this.props; - if (editionStatus.installationStatus === 'AUTOMATIC_IN_PROGRESS') { - return ( -
- - {translate('marketplace.status.AUTOMATIC_IN_PROGRESS')} -
- ); - } else if (editionStatus.installationStatus === 'AUTOMATIC_READY') { - return ( -
- {translate('marketplace.status.AUTOMATIC_READY')} - - {this.state.openRestart && } -
- ); - } else if ( - ['MANUAL_IN_PROGRESS', 'AUTOMATIC_FAILURE'].includes(editionStatus.installationStatus) - ) { - return ( -
- {translate('marketplace.status', editionStatus.installationStatus)} - - {translate('marketplace.how_to_install')} - -
- ); + const { installationStatus } = this.props.editionStatus; + + switch (installationStatus) { + case 'AUTOMATIC_IN_PROGRESS': + return ( +
+ + {translate('marketplace.status.AUTOMATIC_IN_PROGRESS')} +
+ ); + case 'AUTOMATIC_READY': + case 'UNINSTALL_IN_PROGRESS': + return ( +
+ {translate('marketplace.status', installationStatus)} + + {this.state.openRestart && } +
+ ); + case 'MANUAL_IN_PROGRESS': + case 'AUTOMATIC_FAILURE': + return ( +
+ {translate('marketplace.status', installationStatus)} + + {translate('marketplace.how_to_install')} + +
+ ); } 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 { + mounted: boolean; + state: State = { loading: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + handleCancelClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + this.props.onClose(); + }; + + handleConfirmClick = (event: React.SyntheticEvent) => { + 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 ( + +
+

{header}

+
+ +
+

{translateWithParameters('marketplace.uninstall_x_confirmation', currentEdition)}

+
+ + +
+ ); + } +} 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; + +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( + + ); +} 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`] = ` @@ -149,6 +150,7 @@ exports[`should display installed badge 1`] = ` @@ -187,6 +189,7 @@ exports[`should display installing badge 1`] = ` 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`] = ` + +
+

+ marketplace.uninstall_x.Foo +

+
+
+

+ marketplace.uninstall_x_confirmation.Foo +

+
+ +
+`; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 7a09e8ffc2b..39a5c31bfdf 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2071,6 +2071,8 @@ marketplace.install=Install marketplace.install_x=Install {0} marketplace.installed=Installed marketplace.installing=Installing... +marketplace.uninstall_x=Uninstall {0} +marketplace.uninstall_x_confirmation=Are you sure you want to uninstall {0}? marketplace.pending=Pending... marketplace._installed=installed marketplace.available_under_commercial_license=Available under our commercial editions @@ -2094,6 +2096,7 @@ marketplace.installing_this_plugin_will_also_install_x=Installing this plugin wi marketplace.update_to_x=Update to {0} marketplace.uninstall=Uninstall marketplace.i_accept_the=I accept the +marketplace.commercial_edition=Commercial Edition marketplace.terms_and_conditions=Terms and Conditions marketplace.editions_unavailable=Explore our Commercial Editions: advanced feature packs brought to you by SonarSource on {url} marketplace.status.AUTOMATIC_IN_PROGRESS=Updating your installation... Please wait... -- 2.39.5