diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-10-19 11:48:26 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-10-23 08:01:13 -0700 |
commit | f03c5e9858dd86ecd78e6278aa6f95d146ada270 (patch) | |
tree | fa37c7b6cf0fb172449b98cc1d5647e4d8465f5e /server | |
parent | 6e71b7b4a1d26c0de2a2c8bde2b0274de90c08e0 (diff) | |
download | sonarqube-f03c5e9858dd86ecd78e6278aa6f95d146ada270.tar.gz sonarqube-f03c5e9858dd86ecd78e6278aa6f95d146ada270.zip |
SONAR-9947 Update handling of manual installation of commercial editions
Diffstat (limited to 'server')
11 files changed, 150 insertions, 116 deletions
diff --git a/server/sonar-web/src/main/js/apps/marketplace/App.tsx b/server/sonar-web/src/main/js/apps/marketplace/App.tsx index 37cc3b1d72a..0c008e09b7c 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -36,10 +36,10 @@ import { Plugin, PluginPending } from '../../api/plugins'; -import { EditionStatus, getEditionStatus } from '../../api/marketplace'; +import { Edition, EditionStatus, getEditionsList, getEditionStatus } from '../../api/marketplace'; import { RawQuery } from '../../helpers/query'; import { translate } from '../../helpers/l10n'; -import { filterPlugins, parseQuery, Query, serializeQuery } from './utils'; +import { getEditionsForVersion, filterPlugins, parseQuery, Query, serializeQuery } from './utils'; export interface Props { editionsUrl: string; @@ -49,8 +49,10 @@ export interface Props { } interface State { + editions?: Edition[]; editionStatus?: EditionStatus; - loading: boolean; + loadingEditions: boolean; + loadingPlugins: boolean; pending: { installing: PluginPending[]; updating: PluginPending[]; @@ -69,7 +71,8 @@ export default class App extends React.PureComponent<Props, State> { constructor(props: Props) { super(props); this.state = { - loading: true, + loadingEditions: true, + loadingPlugins: true, pending: { installing: [], updating: [], @@ -81,6 +84,7 @@ export default class App extends React.PureComponent<Props, State> { componentDidMount() { this.mounted = true; + this.fetchEditions(); this.fetchPendingPlugins(); this.fetchEditionStatus(); this.fetchQueryPlugins(); @@ -106,35 +110,35 @@ export default class App extends React.PureComponent<Props, State> { }; fetchAllPlugins = () => { - this.setState({ loading: true }); + this.setState({ loadingPlugins: true }); Promise.all([getInstalledPluginsWithUpdates(), getAvailablePlugins()]).then( ([installed, available]) => { if (this.mounted) { this.setState({ - loading: false, + loadingPlugins: false, plugins: sortBy(uniqBy([...installed, ...available.plugins], 'key'), 'name') }); } }, () => { if (this.mounted) { - this.setState({ loading: false }); + this.setState({ loadingPlugins: false }); } } ); }; fetchUpdatesOnly = () => { - this.setState({ loading: true }); + this.setState({ loadingPlugins: true }); getPluginUpdates().then( plugins => { if (this.mounted) { - this.setState({ loading: false, plugins }); + this.setState({ loadingPlugins: false, plugins }); } }, () => { if (this.mounted) { - this.setState({ loading: false }); + this.setState({ loadingPlugins: false }); } } ); @@ -161,6 +165,25 @@ export default class App extends React.PureComponent<Props, State> { () => {} ); + fetchEditions = () => { + this.setState({ loadingEditions: true }); + getEditionsList(this.props.editionsUrl).then( + editionsPerVersion => { + if (this.mounted) { + this.setState({ + editions: getEditionsForVersion(editionsPerVersion, this.props.sonarqubeVersion), + loadingEditions: false + }); + } + }, + () => { + if (this.mounted) { + this.setState({ loadingEditions: false }); + } + } + ); + }; + updateEditionStatus = (editionStatus: EditionStatus) => this.setState({ editionStatus: editionStatus }); @@ -170,15 +193,17 @@ export default class App extends React.PureComponent<Props, State> { }; render() { - const { editionStatus, loading, plugins, pending } = this.state; + const { editions, editionStatus, loadingPlugins, plugins, pending } = this.state; const query = parseQuery(this.props.location.query); const filteredPlugins = query.search ? filterPlugins(plugins, query.search) : plugins; + return ( <div className="page page-limited" id="marketplace-page"> <Helmet title={translate('marketplace.page')} /> <div className="marketplace-notifs"> {editionStatus && ( <EditionsStatusNotif + editions={editions} editionStatus={editionStatus} updateEditionStatus={this.updateEditionStatus} /> @@ -187,6 +212,8 @@ export default class App extends React.PureComponent<Props, State> { </div> <Header /> <EditionBoxes + editions={editions} + loading={this.state.loadingEditions} editionStatus={editionStatus} editionsUrl={this.props.editionsUrl} sonarqubeVersion={this.props.sonarqubeVersion} @@ -198,8 +225,8 @@ export default class App extends React.PureComponent<Props, State> { updateCenterActive={this.props.updateCenterActive} updateQuery={this.updateQuery} /> - {loading && <i className="spinner" />} - {!loading && ( + {loadingPlugins && <i className="spinner" />} + {!loadingPlugins && ( <PluginsList plugins={filteredPlugins} pending={pending} @@ -207,7 +234,7 @@ export default class App extends React.PureComponent<Props, State> { updateQuery={this.updateQuery} /> )} - {!loading && <Footer total={filteredPlugins.length} />} + {!loadingPlugins && <Footer total={filteredPlugins.length} />} </div> ); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx index 909f284fbdf..abf67e2c3d8 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx @@ -25,7 +25,8 @@ import './style.css'; const mapStateToProps = (state: any) => ({ editionsUrl: (getGlobalSettingValue(state, 'sonar.editions.jsonUrl') || {}).value, sonarqubeVersion: getAppState(state).version, - updateCenterActive: (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value + updateCenterActive: + (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value === 'true' }); export default connect(mapStateToProps)(App as any); 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 112205c8d36..95a3c96d109 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx @@ -22,58 +22,26 @@ 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 { Edition, EditionStatus } from '../../api/marketplace'; import { translate } from '../../helpers/l10n'; export interface Props { + editions?: Edition[]; editionStatus?: EditionStatus; editionsUrl: string; + loading: boolean; sonarqubeVersion: string; updateCenterActive: boolean; updateEditionStatus: (editionStatus: EditionStatus) => void; } interface State { - editions?: Edition[]; - 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, openUninstallForm: false }; - - componentDidMount() { - this.mounted = true; - this.fetchEditions(); - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchEditions = () => { - this.setState({ loading: true }); - getEditionsList(this.props.editionsUrl).then( - editionsPerVersion => { - if (this.mounted) { - this.setState({ - loading: false, - editions: getEditionsForVersion(editionsPerVersion, this.props.sonarqubeVersion), - editionsError: false - }); - } - }, - () => { - if (this.mounted) { - this.setState({ editionsError: true, loading: false }); - } - } - ); - }; + state: State = { openUninstallForm: false }; handleOpenLicenseForm = (edition: Edition) => this.setState({ installEdition: edition }); handleCloseLicenseForm = () => this.setState({ installEdition: undefined }); @@ -82,13 +50,13 @@ export default class EditionBoxes extends React.PureComponent<Props, State> { handleCloseUninstallForm = () => this.setState({ openUninstallForm: false }); render() { - const { editionStatus } = this.props; - const { editions, editionsError, loading, installEdition, openUninstallForm } = this.state; + const { editions, editionStatus, loading } = this.props; + const { installEdition, openUninstallForm } = this.state; if (loading) { return <i className="big-spacer-bottom spinner" />; } - if (!editions || editionsError) { + if (!editions) { return ( <div className="spacer-bottom marketplace-editions"> <span className="alert alert-info"> diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx index e64c714577f..0df72ac7f89 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx @@ -48,27 +48,19 @@ const DEFAULT_EDITIONS = [ ]; it('should display the edition boxes', () => { - const wrapper = getWrapper(); + const wrapper = getWrapper({ editions: DEFAULT_EDITIONS, loading: true }); expect(wrapper).toMatchSnapshot(); - wrapper.setState({ - editions: DEFAULT_EDITIONS, - loading: false - }); + wrapper.setProps({ loading: false }); expect(wrapper).toMatchSnapshot(); }); it('should display an error message', () => { const wrapper = getWrapper(); - wrapper.setState({ loading: false, editionsError: true }); expect(wrapper).toMatchSnapshot(); }); it('should open the license form', () => { - const wrapper = getWrapper(); - wrapper.setState({ - editions: DEFAULT_EDITIONS, - loading: false - }); + const wrapper = getWrapper({ editions: DEFAULT_EDITIONS }); (wrapper.instance() as EditionBoxes).handleOpenLicenseForm(DEFAULT_EDITIONS[0]); expect(wrapper.find('LicenseEditionForm').exists()).toBeTruthy(); }); @@ -76,6 +68,7 @@ it('should open the license form', () => { function getWrapper(props = {}) { return shallow( <EditionBoxes + loading={false} editionStatus={DEFAULT_STATUS} editionsUrl="" sonarqubeVersion="6.7.5" 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 1afc9931bf6..672a0b7a28b 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 @@ -20,10 +20,11 @@ import * as React from 'react'; import RestartForm from '../../../components/common/RestartForm'; import CloseIcon from '../../../components/icons-components/CloseIcon'; -import { dismissErrorMessage, EditionStatus } from '../../../api/marketplace'; +import { dismissErrorMessage, Edition, EditionStatus } from '../../../api/marketplace'; import { translate } from '../../../helpers/l10n'; interface Props { + editions?: Edition[]; editionStatus: EditionStatus; updateEditionStatus: (editionStatus: EditionStatus) => void; } @@ -48,7 +49,10 @@ export default class EditionsStatusNotif extends React.PureComponent<Props, Stat }; renderStatusAlert() { - const { installationStatus } = this.props.editionStatus; + const { installationStatus, nextEditionKey } = this.props.editionStatus; + const nextEdition = + this.props.editions && this.props.editions.find(edition => edition.key === nextEditionKey); + switch (installationStatus) { case 'AUTOMATIC_IN_PROGRESS': return ( @@ -61,7 +65,13 @@ export default class EditionsStatusNotif extends React.PureComponent<Props, Stat case 'UNINSTALL_IN_PROGRESS': return ( <div className="alert alert-success"> - <span>{translate('marketplace.status', installationStatus)}</span> + <span> + {nextEdition ? ( + translate('marketplace.status_x.' + installationStatus, nextEdition.name) + ) : ( + translate('marketplace.status', installationStatus) + )} + </span> <button className="js-restart spacer-left" onClick={this.handleOpenRestart}> {translate('marketplace.restart')} </button> @@ -71,7 +81,27 @@ export default class EditionsStatusNotif extends React.PureComponent<Props, Stat case 'MANUAL_IN_PROGRESS': return ( <div className="alert alert-danger"> - {translate('marketplace.status', installationStatus)} + {nextEdition ? ( + translate('marketplace.status_x.' + installationStatus, nextEdition.name) + ) : ( + translate('marketplace.status', installationStatus) + )} + <p className="spacer-left"> + {nextEdition && ( + <a + className="button spacer-right" + download={`sonarqube-${nextEdition.name}.zip`} + href={nextEdition.download_link} + target="_blank"> + {translate('marketplace.download_package')} + </a> + )} + <a + href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html" + target="_blank"> + {translate('marketplace.how_to_install')} + </a> + </p> <a className="little-spacer-left" href="https://www.sonarsource.com" target="_blank"> {translate('marketplace.how_to_install')} </a> @@ -96,7 +126,7 @@ export default class EditionsStatusNotif extends React.PureComponent<Props, Stat </a> </div> )} - {this.renderStatusAlert} + {this.renderStatusAlert()} </div> ); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx index 6971889135c..e099465dc43 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx @@ -62,7 +62,7 @@ export default class LicenseEditionForm extends React.PureComponent<Props, State handleConfirmClick = (event: React.SyntheticEvent<HTMLButtonElement>) => { event.preventDefault(); const { license, status } = this.state; - if (license && status && ['AUTOMATIC_INSTALL', 'NO_INSTALL'].includes(status)) { + if (license && status) { this.setState({ submitting: true }); applyLicense({ license }).then( editionStatus => { @@ -102,10 +102,13 @@ export default class LicenseEditionForm extends React.PureComponent<Props, State <footer className="modal-foot"> {submitting && <i className="spinner spacer-right" />} - {status && - ['NO_INSTALL', 'AUTOMATIC_INSTALL'].includes(status) && ( + {status && ( <button className="js-confirm" onClick={this.handleConfirmClick} disabled={submitting}> - {status === 'NO_INSTALL' ? translate('save') : translate('marketplace.install')} + {status === 'AUTOMATIC_INSTALL' ? ( + translate('marketplace.install') + ) : ( + translate('save') + )} </button> )} <a className="js-modal-close" href="#" onClick={this.handleCancelClick}> diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx index 86c29fc65e9..496c5fe988d 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx @@ -137,8 +137,7 @@ export default class LicenseEditionSet extends React.PureComponent<Props, State> rows={6} value={license} /> - {previewStatus && - licenseEdition && ( + {previewStatus && ( <p className={classNames('alert spacer-top', { 'alert-warning': previewStatus === 'AUTOMATIC_INSTALL', @@ -147,24 +146,7 @@ export default class LicenseEditionSet extends React.PureComponent<Props, State> })}> {translateWithParameters( 'marketplace.license_preview_status.' + previewStatus, - licenseEdition.name - )} - {previewStatus === 'MANUAL_INSTALL' && ( - <p className="spacer-top"> - <a - className="button" - download={`sonarqube-${licenseEdition.name}.zip`} - href={licenseEdition.download_link} - target="_blank"> - {translate('marketplace.download_package')} - </a> - <a - className="spacer-left" - href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html" - target="_blank"> - {translate('marketplace.how_to_install')} - </a> - </p> + licenseEdition ? licenseEdition.name : translate('marketplace.commercial_edition') )} </p> )} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx index f1b0952650d..2beffa70f82 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx @@ -55,7 +55,7 @@ it('should correctly change the button based on the status', () => { wrapper.setState({ status: 'AUTOMATIC_INSTALL' }); expect(wrapper.find('button')).toMatchSnapshot(); wrapper.setState({ status: 'MANUAL_INSTALL' }); - expect(wrapper.find('button').exists()).toBeFalsy(); + expect(wrapper.find('button')).toMatchSnapshot(); }); it('should update the edition status after install', async () => { diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap index 0f79649b873..70b4ce43ef0 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionsStatusNotif-test.tsx.snap @@ -1,10 +1,39 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should display a ready notification 1`] = `<div />`; +exports[`should display a ready notification 1`] = ` +<div> + <div + className="alert alert-success" + > + <span> + marketplace.status.AUTOMATIC_READY + </span> + <button + className="js-restart spacer-left" + onClick={[Function]} + > + marketplace.restart + </button> + </div> +</div> +`; exports[`should display an error notification 1`] = `<div />`; -exports[`should display an in progress notif 1`] = `<div />`; +exports[`should display an in progress notif 1`] = ` +<div> + <div + className="alert alert-info" + > + <i + className="spinner spacer-right text-bottom" + /> + <span> + marketplace.status.AUTOMATIC_IN_PROGRESS + </span> + </div> +</div> +`; exports[`should display install errors 1`] = ` <div> @@ -20,5 +49,15 @@ exports[`should display install errors 1`] = ` <CloseIcon /> </a> </div> + <div + className="alert alert-info" + > + <i + className="spinner spacer-right text-bottom" + /> + <span> + marketplace.status.AUTOMATIC_IN_PROGRESS + </span> + </div> </div> `; diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap index e657d2c1ee2..e3951bfeaff 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap @@ -20,6 +20,16 @@ exports[`should correctly change the button based on the status 2`] = ` </button> `; +exports[`should correctly change the button based on the status 3`] = ` +<button + className="js-confirm" + disabled={false} + onClick={[Function]} +> + save +</button> +`; + exports[`should display correctly 1`] = ` <Modal ariaHideApp={true} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap index 48c4cdecbf9..2e49983f7d3 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap @@ -21,25 +21,6 @@ exports[`should correctly display status message after checking license 3`] = ` className="alert spacer-top alert-danger" > marketplace.license_preview_status.MANUAL_INSTALL.Foo - <p - className="spacer-top" - > - <a - className="button" - download="sonarqube-Foo.zip" - href="download_url" - target="_blank" - > - marketplace.download_package - </a> - <a - className="spacer-left" - href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html" - target="_blank" - > - marketplace.how_to_install - </a> - </p> </p> `; |