aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-05-28 13:10:23 +0200
committerSonarTech <sonartech@sonarsource.com>2018-06-12 20:20:58 +0200
commitbdde08fff517a28f6f639e724b810a3679fdc452 (patch)
tree88cd623b8cc7518c677327ffdacf8e3ed5014643 /server/sonar-web
parent1dc4a86ad69635505e99f3cefe16e6542a7d4dfb (diff)
downloadsonarqube-bdde08fff517a28f6f639e724b810a3679fdc452.tar.gz
sonarqube-bdde08fff517a28f6f639e724b810a3679fdc452.zip
SONAR-10696 Remove ability to upgrade/downgrade an edition from Marketplace (#269)
* SONAR-10699 Remove upgrade/downgrade buttons * SONAR-10697 Drop edition.json support in the marketplace * SONAR-10717 Drop 'sonar.editions.jsonUrl' property * SONAR-10702 Edition's "Learn more" links redirect to the form page with arguments * SONAR-10698 Get Edition data from the doc * SONAR-10700 Remove LicenseEditionForm and LicenseEditionSet from Marketplace
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/api/marketplace.ts38
-rw-r--r--server/sonar-web/src/main/js/app/components/AdminContainer.tsx47
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx179
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx49
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx41
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx130
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx25
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap62
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap21
-rw-r--r--server/sonar-web/src/main/js/app/styles/components/alerts.css6
-rw-r--r--server/sonar-web/src/main/js/app/utils/exposeLibraries.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/App.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx157
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx108
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap76
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx60
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx62
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx114
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx269
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx94
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx36
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx81
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx98
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx126
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx72
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap106
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap29
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap81
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap105
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap39
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/style.css5
-rw-r--r--server/sonar-web/src/main/js/apps/marketplace/utils.ts67
-rw-r--r--server/sonar-web/src/main/js/store/marketplace/actions.ts69
-rw-r--r--server/sonar-web/src/main/js/store/marketplace/reducer.ts34
-rw-r--r--server/sonar-web/src/main/js/store/marketplace/utils.ts49
-rw-r--r--server/sonar-web/src/main/js/store/rootReducer.js6
38 files changed, 291 insertions, 2315 deletions
diff --git a/server/sonar-web/src/main/js/api/marketplace.ts b/server/sonar-web/src/main/js/api/marketplace.ts
index c04fe5160ae..238efa8b3a3 100644
--- a/server/sonar-web/src/main/js/api/marketplace.ts
+++ b/server/sonar-web/src/main/js/api/marketplace.ts
@@ -17,45 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { checkStatus, corsRequest, getJSON, parseJSON, post, postJSON } from '../helpers/request';
+import { getJSON, postJSON } from '../helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';
-export interface Edition {
- key: string;
- name: string;
- textDescription: string;
- homeUrl: string;
- licenseRequestUrl: string;
- downloadUrl: string;
-}
-
-export interface EditionsPerVersion {
- [version: string]: Edition[];
-}
-
export interface EditionStatus {
currentEditionKey?: string;
- nextEditionKey?: string;
- installError?: string;
- installationStatus:
- | 'NONE'
- | 'AUTOMATIC_IN_PROGRESS'
- | 'MANUAL_IN_PROGRESS'
- | 'AUTOMATIC_READY'
- | 'UNINSTALL_IN_PROGRESS';
}
export function getEditionStatus(): Promise<EditionStatus> {
return getJSON('/api/editions/status');
}
-export function getEditionsList(url: string): Promise<EditionsPerVersion> {
- return corsRequest(url)
- .submit()
- .then(checkStatus)
- .then(parseJSON);
-}
-
export function getLicensePreview(data: {
license: string;
}): Promise<{
@@ -72,11 +44,3 @@ export function getFormData(): Promise<{ serverId: string; ncloc: number }> {
export function applyLicense(data: { license: string }): Promise<EditionStatus> {
return postJSON('/api/editions/apply_license', data).catch(throwGlobalError);
}
-
-export function uninstallEdition(): Promise<void | Response> {
- return post('/api/editions/uninstall').catch(throwGlobalError);
-}
-
-export function dismissErrorMessage(): Promise<void | Response> {
- return post('/api/editions/clear_error_message').catch(throwGlobalError);
-}
diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
index c81e450022d..3347e3e922f 100644
--- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
@@ -22,20 +22,10 @@ import * as PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import SettingsNav from './nav/settings/SettingsNav';
-import {
- getAppState,
- getGlobalSettingValue,
- getMarketplaceEditionStatus,
- getMarketplacePendingPlugins
-} from '../../store/rootReducer';
+import { getAppState, getMarketplacePendingPlugins } from '../../store/rootReducer';
import { getSettingsNavigation } from '../../api/nav';
-import { EditionStatus, getEditionStatus } from '../../api/marketplace';
import { setAdminPages } from '../../store/appState/duck';
-import {
- fetchEditions,
- setEditionStatus,
- fetchPendingPlugins
-} from '../../store/marketplace/actions';
+import { fetchCurrentEdition, fetchPendingPlugins } from '../../store/marketplace/actions';
import { translate } from '../../helpers/l10n';
import { Extension } from '../types';
import { PluginPendingResult } from '../../api/plugins';
@@ -47,23 +37,22 @@ interface StateProps {
organizationsEnabled: boolean;
version: string;
};
- editionStatus?: EditionStatus;
- editionsUrl: string;
pendingPlugins: PluginPendingResult;
}
-interface DispatchProps {
- fetchEditions: (url: string, version: string) => void;
+interface DispatchToProps {
fetchPendingPlugins: () => void;
+ fetchCurrentEdition: () => void;
setAdminPages: (adminPages: Extension[]) => void;
- setEditionStatus: (editionStatus: EditionStatus) => void;
}
interface OwnProps {
location: {};
}
-class AdminContainer extends React.PureComponent<StateProps & DispatchProps & OwnProps> {
+type Props = StateProps & DispatchToProps & OwnProps;
+
+class AdminContainer extends React.PureComponent<Props> {
static contextTypes = {
canAdmin: PropTypes.bool.isRequired
};
@@ -73,17 +62,13 @@ class AdminContainer extends React.PureComponent<StateProps & DispatchProps & Ow
handleRequiredAuthorization();
} else {
this.fetchNavigationSettings();
- this.props.fetchEditions(this.props.editionsUrl, this.props.appState.version);
- this.fetchEditionStatus();
+ this.props.fetchCurrentEdition();
}
}
fetchNavigationSettings = () =>
getSettingsNavigation().then(r => this.props.setAdminPages(r.extensions), () => {});
- fetchEditionStatus = () =>
- getEditionStatus().then(editionStatus => this.props.setEditionStatus(editionStatus), () => {});
-
render() {
const { adminPages, organizationsEnabled } = this.props.appState;
@@ -98,7 +83,6 @@ class AdminContainer extends React.PureComponent<StateProps & DispatchProps & Ow
<div>
<Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />
<SettingsNav
- editionStatus={this.props.editionStatus}
extensions={adminPages}
fetchPendingPlugins={this.props.fetchPendingPlugins}
location={this.props.location}
@@ -113,16 +97,15 @@ class AdminContainer extends React.PureComponent<StateProps & DispatchProps & Ow
const mapStateToProps = (state: any): StateProps => ({
appState: getAppState(state),
- editionStatus: getMarketplaceEditionStatus(state),
- editionsUrl: (getGlobalSettingValue(state, 'sonar.editions.jsonUrl') || {}).value,
pendingPlugins: getMarketplacePendingPlugins(state)
});
-const mapDispatchToProps: DispatchProps = {
- setAdminPages,
- setEditionStatus,
- fetchEditions,
- fetchPendingPlugins
+const mapDispatchToProps: DispatchToProps = {
+ fetchCurrentEdition,
+ fetchPendingPlugins,
+ setAdminPages
};
-export default connect(mapStateToProps, mapDispatchToProps)(AdminContainer);
+export default connect<StateProps, DispatchToProps, OwnProps>(mapStateToProps, mapDispatchToProps)(
+ AdminContainer
+);
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
index f5ca55b6f83..8f9d34a94fb 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
@@ -81,7 +81,7 @@ export default class ComponentNav extends React.PureComponent<Props> {
}
return (
<ContextNavBar
- height={notifComponent ? theme.contextNavHeightRaw + 20 : theme.contextNavHeightRaw}
+ height={notifComponent ? theme.contextNavHeightRaw + 30 : theme.contextNavHeightRaw}
id="context-navigation"
notif={notifComponent}>
<div className="navbar-context-justified">
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx
deleted file mode 100644
index c4f1af5e32b..00000000000
--- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx
+++ /dev/null
@@ -1,179 +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 * as React from 'react';
-import { FormattedMessage } from 'react-intl';
-import NavBarNotif from '../../../../components/nav/NavBarNotif';
-import RestartForm from '../../../../components/common/RestartForm';
-import { Button } from '../../../../components/ui/buttons';
-import { dismissErrorMessage, Edition, EditionStatus } from '../../../../api/marketplace';
-import { translate, translateWithParameters } from '../../../../helpers/l10n';
-
-interface Props {
- editions?: Edition[];
- editionStatus: EditionStatus;
- preventRestart: boolean;
- setEditionStatus: (editionStatus: EditionStatus) => void;
-}
-
-interface State {
- openRestart: boolean;
-}
-
-export default class SettingsEditionsNotif extends React.PureComponent<Props, State> {
- state: State = { openRestart: false };
-
- handleOpenRestart = () => this.setState({ openRestart: true });
- hanleCloseRestart = () => this.setState({ openRestart: false });
-
- handleDismissError = () =>
- dismissErrorMessage().then(
- () => this.props.setEditionStatus({ ...this.props.editionStatus, installError: undefined }),
- () => {}
- );
-
- renderStatusMsg(edition?: Edition) {
- const { editionStatus } = this.props;
- return (
- <NavBarNotif className="alert alert-info">
- <i className="spinner spacer-right" />
- <span>
- {edition
- ? translateWithParameters(
- 'marketplace.edition_status_x.' + editionStatus.installationStatus,
- edition.name
- )
- : translate('marketplace.edition_status', editionStatus.installationStatus)}
- </span>
- </NavBarNotif>
- );
- }
-
- renderRestartMsg(edition?: Edition) {
- const { editionStatus, preventRestart } = this.props;
- return (
- <NavBarNotif className="alert alert-success">
- <span>
- {edition
- ? translateWithParameters(
- 'marketplace.edition_status_x.' + editionStatus.installationStatus,
- edition.name
- )
- : translate('marketplace.edition_status', editionStatus.installationStatus)}
- </span>
- {edition &&
- edition.key === 'datacenter' && (
- <span className="little-spacer-left">
- <FormattedMessage
- defaultMessage={translate('marketplace.see_documentation_to_enable_cluster')}
- id="marketplace.see_documentation_to_enable_cluster"
- values={{
- url: (
- <a
- href="https://redirect.sonarsource.com/doc/data-center-edition.html"
- rel="noopener noreferrer"
- target="_blank">
- {edition.name}
- </a>
- )
- }}
- />
- </span>
- )}
- {!preventRestart && (
- <Button className="js-restart spacer-left" onClick={this.handleOpenRestart}>
- {translate('marketplace.restart')}
- </Button>
- )}
- {!preventRestart &&
- this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />}
- </NavBarNotif>
- );
- }
-
- renderManualMsg(edition?: Edition) {
- const { editionStatus } = this.props;
- return (
- <NavBarNotif className="alert alert-danger">
- {edition
- ? translateWithParameters(
- 'marketplace.edition_status_x.' + editionStatus.installationStatus,
- edition.name
- )
- : translate('marketplace.edition_status', editionStatus.installationStatus)}
- <a
- className="spacer-left"
- href={
- edition && edition.key === 'datacenter'
- ? 'https://redirect.sonarsource.com/doc/data-center-edition.html'
- : 'https://redirect.sonarsource.com/doc/how-to-install-an-edition.html'
- }
- target="_blank">
- {translate('marketplace.how_to_install')}
- </a>
- {edition && (
- <a
- className="button spacer-left"
- download={`sonarqube-${edition.name}.zip`}
- href={edition.downloadUrl}
- target="_blank">
- {translate('marketplace.download_package')}
- </a>
- )}
- </NavBarNotif>
- );
- }
-
- renderStatusAlert() {
- const { currentEditionKey, installationStatus, nextEditionKey } = this.props.editionStatus;
- const nextEdition =
- this.props.editions && this.props.editions.find(edition => edition.key === nextEditionKey);
- const currentEdition =
- this.props.editions &&
- this.props.editions.find(
- edition =>
- edition.key === currentEditionKey || (!currentEditionKey && edition.key === 'community')
- );
-
- switch (installationStatus) {
- case 'AUTOMATIC_IN_PROGRESS':
- return this.renderStatusMsg(nextEdition);
- case 'AUTOMATIC_READY':
- return this.renderRestartMsg(nextEdition);
- case 'UNINSTALL_IN_PROGRESS':
- return this.renderRestartMsg(currentEdition);
- case 'MANUAL_IN_PROGRESS':
- return this.renderManualMsg(nextEdition);
- }
- return null;
- }
-
- render() {
- const { installError } = this.props.editionStatus;
- if (installError) {
- return (
- <NavBarNotif className="alert alert-danger" onCancel={this.handleDismissError}>
- {installError}
- </NavBarNotif>
- );
- }
-
- return this.renderStatusAlert();
- }
-}
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx
deleted file mode 100644
index 8173ffdb62a..00000000000
--- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotifContainer.tsx
+++ /dev/null
@@ -1,49 +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 { connect } from 'react-redux';
-import SettingsEditionsNotif from './SettingsEditionsNotif';
-import { getAppState, getMarketplaceEditions } from '../../../../store/rootReducer';
-import { Edition, EditionStatus } from '../../../../api/marketplace';
-import { setEditionStatus } from '../../../../store/marketplace/actions';
-
-interface OwnProps {
- editionStatus: EditionStatus;
-}
-
-interface StateToProps {
- editions?: Edition[];
- preventRestart: boolean;
-}
-
-interface DispatchToProps {
- setEditionStatus: (editionStatus: EditionStatus) => void;
-}
-
-const mapStateToProps = (state: any): StateToProps => ({
- editions: getMarketplaceEditions(state),
- preventRestart: !getAppState(state).standalone
-});
-
-const mapDispatchToProps = { setEditionStatus };
-
-export default connect<StateToProps, DispatchToProps, OwnProps>(
- mapStateToProps,
- mapDispatchToProps
-)(SettingsEditionsNotif);
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx
index 397603fe68d..08b49b3099f 100644
--- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx
@@ -20,20 +20,17 @@
import * as React from 'react';
import * as classNames from 'classnames';
import { IndexLink, Link } from 'react-router';
-import SettingsEditionsNotifContainer from './SettingsEditionsNotifContainer';
import PendingPluginsActionNotif from './PendingPluginsActionNotif';
import * as theme from '../../../../app/theme';
import ContextNavBar from '../../../../components/nav/ContextNavBar';
+import Dropdown from '../../../../components/controls/Dropdown';
import NavBarTabs from '../../../../components/nav/NavBarTabs';
-import { EditionStatus } from '../../../../api/marketplace';
import { Extension } from '../../../types';
-import { translate } from '../../../../helpers/l10n';
-import Dropdown from '../../../../components/controls/Dropdown';
import { PluginPendingResult } from '../../../../api/plugins';
import DropdownIcon from '../../../../components/icons-components/DropdownIcon';
+import { translate } from '../../../../helpers/l10n';
interface Props {
- editionStatus?: EditionStatus;
extensions: Extension[];
fetchPendingPlugins: () => void;
location: {};
@@ -232,23 +229,16 @@ export default class SettingsNav extends React.PureComponent<Props> {
}
render() {
- const { editionStatus, extensions, pendingPlugins } = this.props;
+ const { extensions, pendingPlugins } = this.props;
const hasSupportExtension = extensions.find(extension => extension.key === 'license/support');
+ const totalPendingPlugins =
+ pendingPlugins.installing.length +
+ pendingPlugins.removing.length +
+ pendingPlugins.updating.length;
- const notifComponents = [];
- if (
- editionStatus &&
- (editionStatus.installError || editionStatus.installationStatus !== 'NONE')
- ) {
- notifComponents.push(<SettingsEditionsNotifContainer editionStatus={editionStatus} />);
- }
-
- if (
- pendingPlugins.installing.length > 0 ||
- pendingPlugins.removing.length > 0 ||
- pendingPlugins.updating.length > 0
- ) {
- notifComponents.push(
+ let notifComponent;
+ if (totalPendingPlugins > 0) {
+ notifComponent = (
<PendingPluginsActionNotif
pending={pendingPlugins}
refreshPending={this.props.fetchPendingPlugins}
@@ -256,18 +246,11 @@ export default class SettingsNav extends React.PureComponent<Props> {
);
}
- const notifContainer =
- notifComponents.length > 0 ? (
- <div className="alert-container">
- {notifComponents.map((element, index) => <div key={index}>{element}</div>)}
- </div>
- ) : null;
-
return (
<ContextNavBar
- height={theme.contextNavHeightRaw + 38 * notifComponents.length}
+ height={notifComponent ? theme.contextNavHeightRaw + 30 : theme.contextNavHeightRaw}
id="context-navigation"
- notif={notifContainer}>
+ notif={notifComponent}>
<header className="navbar-context-header">
<h1>{translate('layout.settings')}</h1>
</header>
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx
deleted file mode 100644
index 0f40d0bc59a..00000000000
--- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx
+++ /dev/null
@@ -1,130 +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.
- */
-/* eslint-disable import/order */
-import * as React from 'react';
-import { mount, shallow } from 'enzyme';
-import { click } from '../../../../../helpers/testUtils';
-import SettingsEditionsNotif from '../SettingsEditionsNotif';
-
-jest.mock('../../../../../api/marketplace', () => ({
- dismissErrorMessage: jest.fn(() => Promise.resolve())
-}));
-
-const dismissMsg = require('../../../../../api/marketplace').dismissErrorMessage as jest.Mock<any>;
-
-beforeEach(() => {
- dismissMsg.mockClear();
-});
-
-it('should display an in progress notif', () => {
- const wrapper = shallow(
- <SettingsEditionsNotif
- editionStatus={{ installationStatus: 'AUTOMATIC_IN_PROGRESS' }}
- preventRestart={false}
- setEditionStatus={jest.fn()}
- />
- );
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should display a ready notification', () => {
- const wrapper = shallow(
- <SettingsEditionsNotif
- editionStatus={{ installationStatus: 'AUTOMATIC_READY' }}
- preventRestart={false}
- setEditionStatus={jest.fn()}
- />
- );
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should display a manual installation notification', () => {
- const wrapper = shallow(
- <SettingsEditionsNotif
- editionStatus={{ installationStatus: 'MANUAL_IN_PROGRESS', nextEditionKey: 'foo' }}
- editions={[
- {
- key: 'foo',
- name: 'Foo',
- textDescription: 'Foo desc',
- downloadUrl: 'download_url',
- homeUrl: 'more_url',
- licenseRequestUrl: 'license_url'
- }
- ]}
- preventRestart={false}
- setEditionStatus={jest.fn()}
- />
- );
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should display install errors', () => {
- const wrapper = shallow(
- <SettingsEditionsNotif
- editionStatus={{ installationStatus: 'AUTOMATIC_IN_PROGRESS', installError: 'Foo error' }}
- preventRestart={false}
- setEditionStatus={jest.fn()}
- />
- );
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should allow to dismiss install errors', async () => {
- const setEditionStatus = jest.fn();
- const wrapper = mount(
- <SettingsEditionsNotif
- editionStatus={{ installationStatus: 'NONE', installError: 'Foo error' }}
- preventRestart={false}
- setEditionStatus={setEditionStatus}
- />
- );
- click(wrapper.find('button'));
- expect(dismissMsg).toHaveBeenCalled();
- await new Promise(setImmediate);
- expect(setEditionStatus).toHaveBeenCalledWith({
- installationStatus: 'NONE',
- installError: undefined
- });
-});
-
-it('should not display the restart button', () => {
- const wrapper = shallow(
- <SettingsEditionsNotif
- editionStatus={{ installationStatus: 'AUTOMATIC_READY' }}
- preventRestart={true}
- setEditionStatus={jest.fn()}
- />
- );
- expect(wrapper.find('button.js-restart').exists()).toBeFalsy();
-});
-
-it('should have a link to cluster documentation for datacenter edition', () => {
- const editions = [{ key: 'datacenter' }] as any;
- const wrapper = shallow(
- <SettingsEditionsNotif
- editions={editions}
- editionStatus={{ installationStatus: 'AUTOMATIC_READY', nextEditionKey: 'datacenter' }}
- preventRestart={false}
- setEditionStatus={jest.fn()}
- />
- );
- expect(wrapper.find('FormattedMessage').exists()).toBeTruthy();
-});
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx
index 256662531cb..71ca8fd83ed 100644
--- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx
@@ -35,3 +35,28 @@ it('should work with extensions', () => {
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('Dropdown')).toMatchSnapshot();
});
+
+it('should display a pending plugin notif', () => {
+ const extensions = [{ key: 'foo', name: 'Foo' }];
+ const wrapper = shallow(
+ <SettingsNav
+ extensions={extensions}
+ fetchPendingPlugins={() => {}}
+ location={{}}
+ organizationsEnabled={false}
+ pendingPlugins={{
+ installing: [
+ {
+ key: 'foo',
+ name: 'Foo',
+ version: '1.0',
+ implementationBuild: '1'
+ }
+ ],
+ removing: [],
+ updating: []
+ }}
+ />
+ );
+ expect(wrapper.find('ContextNavBar').prop('notif')).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap
deleted file mode 100644
index 051f00fce94..00000000000
--- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap
+++ /dev/null
@@ -1,62 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display a manual installation notification 1`] = `
-<NavBarNotif
- className="alert alert-danger"
->
- marketplace.edition_status_x.MANUAL_IN_PROGRESS.Foo
- <a
- className="spacer-left"
- href="https://redirect.sonarsource.com/doc/how-to-install-an-edition.html"
- target="_blank"
- >
- marketplace.how_to_install
- </a>
- <a
- className="button spacer-left"
- download="sonarqube-Foo.zip"
- href="download_url"
- target="_blank"
- >
- marketplace.download_package
- </a>
-</NavBarNotif>
-`;
-
-exports[`should display a ready notification 1`] = `
-<NavBarNotif
- className="alert alert-success"
->
- <span>
- marketplace.edition_status.AUTOMATIC_READY
- </span>
- <Button
- className="js-restart spacer-left"
- onClick={[Function]}
- >
- marketplace.restart
- </Button>
-</NavBarNotif>
-`;
-
-exports[`should display an in progress notif 1`] = `
-<NavBarNotif
- className="alert alert-info"
->
- <i
- className="spinner spacer-right"
- />
- <span>
- marketplace.edition_status.AUTOMATIC_IN_PROGRESS
- </span>
-</NavBarNotif>
-`;
-
-exports[`should display install errors 1`] = `
-<NavBarNotif
- className="alert alert-danger"
- onCancel={[Function]}
->
- Foo error
-</NavBarNotif>
-`;
diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap
index 29a88938c4b..56d25c693cf 100644
--- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap
+++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap
@@ -1,10 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`should display a pending plugin notif 1`] = `
+<PendingPluginsActionNotif
+ pending={
+ Object {
+ "installing": Array [
+ Object {
+ "implementationBuild": "1",
+ "key": "foo",
+ "name": "Foo",
+ "version": "1.0",
+ },
+ ],
+ "removing": Array [],
+ "updating": Array [],
+ }
+ }
+ refreshPending={[Function]}
+/>
+`;
+
exports[`should work with extensions 1`] = `
<ContextNavBar
height={72}
id="context-navigation"
- notif={null}
>
<header
className="navbar-context-header"
diff --git a/server/sonar-web/src/main/js/app/styles/components/alerts.css b/server/sonar-web/src/main/js/app/styles/components/alerts.css
index c8a9efd700c..6cbd7fb0896 100644
--- a/server/sonar-web/src/main/js/app/styles/components/alerts.css
+++ b/server/sonar-web/src/main/js/app/styles/components/alerts.css
@@ -17,12 +17,6 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-.alert-container .alert {
- margin-bottom: 0px;
- padding: 0 8px;
- line-height: 38px;
-}
-
.alert {
display: block;
margin-bottom: 8px;
diff --git a/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts b/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts
index 995a9b15aa7..ed1178b283a 100644
--- a/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts
+++ b/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts
@@ -27,7 +27,6 @@ import DateFromNow from '../../components/intl/DateFromNow';
import DateFormatter from '../../components/intl/DateFormatter';
import DateTimeFormatter from '../../components/intl/DateTimeFormatter';
import FavoriteContainer from '../../components/controls/FavoriteContainer';
-import LicenseEditionSet from '../../apps/marketplace/components/LicenseEditionSet';
import HomePageSelect from '../../components/controls/HomePageSelect';
import ListFooter from '../../components/controls/ListFooter';
import Modal from '../../components/controls/Modal';
@@ -82,7 +81,6 @@ const exposeLibraries = () => {
HelpTooltip,
HomePageSelect,
Level,
- LicenseEditionSet,
ListFooter,
LockIcon,
Modal,
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 8b151058a49..966ab400de0 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx
@@ -36,21 +36,16 @@ import {
PluginPendingResult,
getInstalledPlugins
} from '../../api/plugins';
-import { Edition, EditionStatus } from '../../api/marketplace';
import { RawQuery } from '../../helpers/query';
import { translate } from '../../helpers/l10n';
import './style.css';
export interface Props {
- editions?: Edition[];
- editionsReadOnly: boolean;
- editionStatus?: EditionStatus;
+ currentEdition?: string;
fetchPendingPlugins: () => void;
- loadingEditions: boolean;
location: { pathname: string; query: RawQuery };
pendingPlugins: PluginPendingResult;
standaloneMode: boolean;
- setEditionStatus: (editionStatus: EditionStatus) => void;
updateCenterActive: boolean;
}
@@ -66,13 +61,7 @@ export default class App extends React.PureComponent<Props, State> {
router: PropTypes.object.isRequired
};
- constructor(props: Props) {
- super(props);
- this.state = {
- loadingPlugins: true,
- plugins: []
- };
- }
+ state: State = { loadingPlugins: true, plugins: [] };
componentDidMount() {
this.mounted = true;
@@ -130,7 +119,7 @@ export default class App extends React.PureComponent<Props, State> {
};
render() {
- const { editions, editionStatus, standaloneMode, pendingPlugins } = this.props;
+ const { currentEdition, standaloneMode, pendingPlugins } = this.props;
const { loadingPlugins, plugins } = this.state;
const query = parseQuery(this.props.location.query);
const filteredPlugins = query.search ? filterPlugins(plugins, query.search) : plugins;
@@ -140,15 +129,7 @@ export default class App extends React.PureComponent<Props, State> {
<Suggestions suggestions="marketplace" />
<Helmet title={translate('marketplace.page')} />
<Header />
- <EditionBoxes
- canInstall={standaloneMode && !this.props.editionsReadOnly}
- canUninstall={standaloneMode}
- editionStatus={editionStatus}
- editions={editions}
- loading={this.props.loadingEditions}
- updateCenterActive={this.props.updateCenterActive}
- updateEditionStatus={this.props.setEditionStatus}
- />
+ <EditionBoxes currentEdition={currentEdition} />
<Search
query={query}
updateCenterActive={this.props.updateCenterActive}
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 73611432c0b..eabc536fda5 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
@@ -22,13 +22,10 @@ import App from './App';
import {
getAppState,
getGlobalSettingValue,
- getMarketplaceState,
- getMarketplaceEditions,
- getMarketplaceEditionStatus,
+ getMarketplaceCurrentEdition,
getMarketplacePendingPlugins
} from '../../store/rootReducer';
-import { Edition, EditionStatus } from '../../api/marketplace';
-import { setEditionStatus, fetchPendingPlugins } from '../../store/marketplace/actions';
+import { fetchPendingPlugins } from '../../store/marketplace/actions';
import { RawQuery } from '../../helpers/query';
import { PluginPendingResult } from '../../api/plugins';
@@ -37,32 +34,27 @@ interface OwnProps {
}
interface StateToProps {
- editions?: Edition[];
- editionsReadOnly: boolean;
- editionStatus?: EditionStatus;
- loadingEditions: boolean;
+ currentEdition?: string;
pendingPlugins: PluginPendingResult;
standaloneMode: boolean;
updateCenterActive: boolean;
}
interface DispatchToProps {
- setEditionStatus: (editionStatus: EditionStatus) => void;
fetchPendingPlugins: () => void;
}
-const mapStateToProps = (state: any) => ({
- editions: getMarketplaceEditions(state),
- editionsReadOnly: getMarketplaceState(state).readOnly,
- editionStatus: getMarketplaceEditionStatus(state),
- loadingEditions: getMarketplaceState(state).loading,
- pendingPlugins: getMarketplacePendingPlugins(state),
- standaloneMode: getAppState(state).standalone,
- updateCenterActive:
- (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value === 'true'
-});
+const mapStateToProps = (state: any) => {
+ return {
+ currentEdition: getMarketplaceCurrentEdition(state),
+ pendingPlugins: getMarketplacePendingPlugins(state),
+ standaloneMode: getAppState(state).standalone,
+ updateCenterActive:
+ (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value === 'true'
+ };
+};
-const mapDispatchToProps = { setEditionStatus, fetchPendingPlugins };
+const mapDispatchToProps = { fetchPendingPlugins };
export default connect<StateToProps, DispatchToProps, OwnProps>(
mapStateToProps,
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 d169e5b31a4..0b762d28f7e 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
+++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
@@ -17,144 +17,59 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+
import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
import EditionBox from './components/EditionBox';
-import LicenseEditionForm from './components/LicenseEditionForm';
-import UninstallEditionForm from './components/UninstallEditionForm';
-import { sortEditions } from './utils';
-import { Edition, EditionStatus } from '../../api/marketplace';
-import { translate } from '../../helpers/l10n';
+import { EDITIONS } from './utils';
+import { getFormData } from '../../api/marketplace';
export interface Props {
- canInstall: boolean;
- canUninstall: boolean;
- editions?: Edition[];
- editionStatus?: EditionStatus;
- loading: boolean;
- updateCenterActive: boolean;
- updateEditionStatus: (editionStatus: EditionStatus) => void;
+ currentEdition?: string;
}
interface State {
- installEdition?: Edition;
- openUninstallForm: boolean;
+ serverId?: string;
+ ncloc?: number;
}
export default class EditionBoxes extends React.PureComponent<Props, State> {
- state: State = { openUninstallForm: false };
-
- handleOpenLicenseForm = (edition: Edition) => this.setState({ installEdition: edition });
- handleCloseLicenseForm = () => this.setState({ installEdition: undefined });
-
- handleOpenUninstallForm = () => this.setState({ openUninstallForm: true });
- handleCloseUninstallForm = () => this.setState({ openUninstallForm: false });
-
- renderForms(sortedEditions: Edition[], installedIdx?: number) {
- const { canInstall, canUninstall, editionStatus } = this.props;
- const { installEdition, openUninstallForm } = this.state;
- const installEditionIdx =
- installEdition && sortedEditions.findIndex(edition => edition.key === installEdition.key);
+ mounted = false;
+ state: State = {};
- if (canInstall && installEdition) {
- return (
- <LicenseEditionForm
- edition={installEdition}
- editions={sortedEditions}
- isDowngrade={
- installedIdx !== undefined &&
- installEditionIdx !== undefined &&
- installEditionIdx < installedIdx
- }
- onClose={this.handleCloseLicenseForm}
- updateEditionStatus={this.props.updateEditionStatus}
- />
- );
- }
-
- if (canUninstall && openUninstallForm && editionStatus && editionStatus.currentEditionKey) {
- return (
- <UninstallEditionForm
- edition={sortedEditions.find(edition => edition.key === editionStatus.currentEditionKey)}
- editionStatus={editionStatus}
- onClose={this.handleCloseUninstallForm}
- updateEditionStatus={this.props.updateEditionStatus}
- />
- );
- }
-
- return null;
+ componentDidMount() {
+ this.mounted = true;
+ this.fetchFormData();
}
- render() {
- const { canInstall, canUninstall, editions, loading } = this.props;
-
- if (loading) {
- return <i className="big-spacer-bottom spinner" />;
- }
-
- if (!editions) {
- return (
- <div className="spacer-bottom marketplace-editions">
- <span className="alert alert-info">
- <FormattedMessage
- defaultMessage={translate('marketplace.editions_unavailable')}
- id="marketplace.editions_unavailable"
- values={{
- url: (
- <a href="https://redirect.sonarsource.com/editions/editions.html" target="_blank">
- SonarSource.com
- </a>
- )
- }}
- />
- </span>
- </div>
- );
- }
+ componentWillUnmount() {
+ this.mounted = false;
+ }
- const sortedEditions = sortEditions(editions);
- const status = this.props.editionStatus || { installationStatus: 'NONE' };
- const inProgressStatus = [
- 'AUTOMATIC_IN_PROGRESS',
- 'AUTOMATIC_READY',
- 'UNINSTALL_IN_PROGRESS'
- ].includes(status.installationStatus);
- const installedIdx = sortedEditions.findIndex(
- edition => edition.key === status.currentEditionKey
+ fetchFormData = () => {
+ getFormData().then(
+ formData => {
+ if (this.mounted) {
+ this.setState({ ...formData });
+ }
+ },
+ () => {}
);
- const nextIdx = sortedEditions.findIndex(edition => edition.key === status.nextEditionKey);
- const currentIdx = inProgressStatus ? nextIdx : installedIdx;
+ };
+
+ render() {
+ const { currentEdition } = this.props;
+ const { serverId, ncloc } = this.state;
return (
<div className="spacer-bottom marketplace-editions">
- <EditionBox
- actionLabel={translate('marketplace.downgrade')}
- disableAction={inProgressStatus}
- displayAction={canUninstall && currentIdx > 0}
- edition={sortedEditions[0]}
- editionStatus={status}
- key={sortedEditions[0].key}
- onAction={this.handleOpenUninstallForm}
- />
- {sortedEditions
- .slice(1)
- .map((edition, idx) => (
- <EditionBox
- actionLabel={
- currentIdx > idx + 1
- ? translate('marketplace.downgrade')
- : translate('marketplace.upgrade')
- }
- disableAction={inProgressStatus}
- displayAction={canInstall && currentIdx !== idx + 1}
- edition={edition}
- editionStatus={status}
- key={edition.key}
- onAction={this.handleOpenLicenseForm}
- />
- ))}
-
- {this.renderForms(sortedEditions, installedIdx)}
+ {EDITIONS.map(edition => (
+ <EditionBox
+ currentEdition={currentEdition || 'community'}
+ edition={edition}
+ key={edition.key}
+ ncloc={ncloc}
+ serverId={serverId}
+ />
+ ))}
</div>
);
}
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 6e153cd084d..dd49bb10079 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
@@ -20,110 +20,22 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import EditionBoxes from '../EditionBoxes';
-import { EditionStatus } from '../../../api/marketplace';
-const DEFAULT_STATUS: EditionStatus = {
- currentEditionKey: 'developer',
- nextEditionKey: '',
- installationStatus: 'NONE'
-};
-
-const DEFAULT_EDITIONS = [
- {
- key: 'developer',
- name: 'Developer Edition',
- textDescription: 'foo',
- downloadUrl: 'download_url',
- homeUrl: 'more_url',
- licenseRequestUrl: 'license_url'
- },
- {
- key: 'comunity',
- name: 'Comunity Edition',
- textDescription: 'bar',
- downloadUrl: 'download_url',
- homeUrl: 'more_url',
- licenseRequestUrl: 'license_url'
- }
-];
+jest.mock('../utils', () => ({
+ EDITIONS: [
+ { key: 'comunity', homeUrl: 'more_url' },
+ { key: 'developer', downloadUrl: 'download_url', homeUrl: 'more_url' }
+ ]
+}));
it('should display the edition boxes correctly', () => {
- const wrapper = getWrapper({ editions: DEFAULT_EDITIONS, loading: true });
- expect(wrapper).toMatchSnapshot();
- wrapper.setProps({ loading: false });
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should display an error message', () => {
- const wrapper = getWrapper();
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should display community without the downgrade button', () => {
- const communityBox = getWrapper({
- editions: DEFAULT_EDITIONS,
- editionStatus: {
- currentEditionKey: '',
- installationStatus: 'NONE'
- },
- loading: false
- })
- .find('EditionBox')
- .first();
- expect(communityBox.prop('displayAction')).toBeFalsy();
-});
-
-it('should not display action buttons', () => {
- const wrapper = getWrapper({
- editions: DEFAULT_EDITIONS,
- editionStatus: {
- currentEditionKey: '',
- installationStatus: 'NONE'
- },
- loading: false,
- canInstall: false,
- canUninstall: false
- });
- wrapper.find('EditionBox').forEach(box => expect(box.prop('displayAction')).toBeFalsy());
-});
-
-it('should display disabled action buttons', () => {
- const wrapper = getWrapper({
- editions: DEFAULT_EDITIONS,
- editionStatus: { installationStatus: 'AUTOMATIC_IN_PROGRESS', nextEditionKey: 'developer' },
- loading: false
- });
-
- wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy());
- expect(wrapper.find('EditionBox').map(box => box.prop('displayAction'))).toEqual([true, false]);
-
- wrapper.setProps({
- editionStatus: { currentEditionKey: 'developer', installationStatus: 'UNINSTALL_IN_PROGRESS' }
- });
- wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy());
- expect(wrapper.find('EditionBox').map(box => box.prop('displayAction'))).toEqual([false, true]);
-
- wrapper.setProps({ editionStatus: { installationStatus: 'AUTOMATIC_READY' } });
- wrapper.find('EditionBox').forEach(box => expect(box.prop('disableAction')).toBeTruthy());
+ expect(getWrapper()).toMatchSnapshot();
});
-it('should open the license form', () => {
- const wrapper = getWrapper({ editions: DEFAULT_EDITIONS });
- (wrapper.instance() as EditionBoxes).handleOpenLicenseForm(DEFAULT_EDITIONS[0]);
- wrapper.update();
- expect(wrapper.find('LicenseEditionForm').exists()).toBeTruthy();
+it('should display the developer edition as installed', () => {
+ expect(getWrapper({ currentEdition: 'developer' })).toMatchSnapshot();
});
function getWrapper(props = {}) {
- return shallow(
- <EditionBoxes
- canInstall={true}
- canUninstall={true}
- loading={false}
- editionStatus={DEFAULT_STATUS}
- updateCenterActive={true}
- updateEditionStatus={jest.fn()}
- {...props}
- />
- );
+ return shallow(<EditionBoxes currentEdition={undefined} {...props} />);
}
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 85ba8946957..687a2cde364 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
@@ -1,87 +1,57 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should display an error message 1`] = `
+exports[`should display the developer edition as installed 1`] = `
<div
className="spacer-bottom marketplace-editions"
>
- <span
- className="alert alert-info"
- >
- <FormattedMessage
- defaultMessage="marketplace.editions_unavailable"
- id="marketplace.editions_unavailable"
- values={
- Object {
- "url": <a
- href="https://redirect.sonarsource.com/editions/editions.html"
- target="_blank"
- >
- SonarSource.com
- </a>,
- }
+ <EditionBox
+ currentEdition="developer"
+ edition={
+ Object {
+ "homeUrl": "more_url",
+ "key": "comunity",
}
- />
- </span>
+ }
+ key="comunity"
+ />
+ <EditionBox
+ currentEdition="developer"
+ edition={
+ Object {
+ "downloadUrl": "download_url",
+ "homeUrl": "more_url",
+ "key": "developer",
+ }
+ }
+ key="developer"
+ />
</div>
`;
exports[`should display the edition boxes correctly 1`] = `
-<i
- className="big-spacer-bottom spinner"
-/>
-`;
-
-exports[`should display the edition boxes correctly 2`] = `
<div
className="spacer-bottom marketplace-editions"
>
<EditionBox
- actionLabel="marketplace.downgrade"
- disableAction={false}
- displayAction={true}
+ currentEdition="community"
edition={
Object {
- "downloadUrl": "download_url",
"homeUrl": "more_url",
"key": "comunity",
- "licenseRequestUrl": "license_url",
- "name": "Comunity Edition",
- "textDescription": "bar",
- }
- }
- editionStatus={
- Object {
- "currentEditionKey": "developer",
- "installationStatus": "NONE",
- "nextEditionKey": "",
}
}
key="comunity"
- onAction={[Function]}
/>
<EditionBox
- actionLabel="marketplace.upgrade"
- disableAction={false}
- displayAction={false}
+ currentEdition="community"
edition={
Object {
"downloadUrl": "download_url",
"homeUrl": "more_url",
"key": "developer",
- "licenseRequestUrl": "license_url",
- "name": "Developer Edition",
- "textDescription": "foo",
- }
- }
- editionStatus={
- Object {
- "currentEditionKey": "developer",
- "installationStatus": "NONE",
- "nextEditionKey": "",
}
}
key="developer"
- onAction={[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 0ad689bc93d..968aa566020 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
@@ -18,45 +18,37 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import EditionBoxBadge from './EditionBoxBadge';
-import { Edition, EditionStatus } from '../../../api/marketplace';
-import { Button } from '../../../components/ui/buttons';
+import CheckIcon from '../../../components/icons-components/CheckIcon';
import { translate } from '../../../helpers/l10n';
+import DocInclude from '../../../components/docs/DocInclude';
+import { Edition, getEditionUrl } from '../utils';
interface Props {
- actionLabel: string;
- disableAction: boolean;
- displayAction: boolean;
+ currentEdition: string;
edition: Edition;
- editionStatus?: EditionStatus;
- onAction: (edition: Edition) => void;
+ ncloc?: number;
+ serverId?: string;
}
-export default class EditionBox extends React.PureComponent<Props> {
- handleAction = () => {
- this.props.onAction(this.props.edition);
- };
-
- render() {
- const { disableAction, displayAction, edition, editionStatus } = this.props;
- return (
- <div className="boxed-group boxed-group-inner marketplace-edition">
- {editionStatus && <EditionBoxBadge editionKey={edition.key} status={editionStatus} />}
- <div>
- <h3 className="spacer-bottom">{edition.name}</h3>
- <p>{edition.textDescription}</p>
- </div>
- <div className="marketplace-edition-action spacer-top">
- <a href={edition.homeUrl} target="_blank">
- {translate('marketplace.learn_more')}
- </a>
- {displayAction && (
- <Button disabled={disableAction} onClick={this.handleAction}>
- {this.props.actionLabel}
- </Button>
- )}
- </div>
+export default function EditionBox({ currentEdition, edition, ncloc, serverId }: Props) {
+ const isInstalled = currentEdition === edition.key;
+ const url = getEditionUrl(edition, { ncloc, serverId, sourceEdition: currentEdition });
+ return (
+ <div className="boxed-group boxed-group-inner marketplace-edition">
+ {isInstalled && (
+ <span className="marketplace-edition-badge badge badge-normal-size display-flex-center">
+ <CheckIcon className="little-spacer-right" size={14} />
+ {translate('marketplace.installed')}
+ </span>
+ )}
+ <div>
+ <DocInclude path={'/tooltips/editions/' + edition.key} />
+ </div>
+ <div className="marketplace-edition-action spacer-top">
+ <a href={url} target="_blank">
+ {translate('marketplace.learn_more')}
+ </a>
</div>
- );
- }
+ </div>
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx
deleted file mode 100644
index 341bd2e221b..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/EditionBoxBadge.tsx
+++ /dev/null
@@ -1,62 +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 * as React from 'react';
-import CheckIcon from '../../../components/icons-components/CheckIcon';
-import { EditionStatus } from '../../../api/marketplace';
-import { translate } from '../../../helpers/l10n';
-
-interface Props {
- editionKey: string;
- status: EditionStatus;
-}
-
-export default function EditionBoxBadge({ editionKey, status }: Props) {
- const isInstalled =
- status.currentEditionKey === editionKey ||
- (!status.currentEditionKey && editionKey === 'community');
- const isProgressing =
- status.nextEditionKey === editionKey || (!status.nextEditionKey && editionKey === 'community');
- const inProgressStatus = [
- 'AUTOMATIC_READY',
- 'AUTOMATIC_IN_PROGRESS',
- 'UNINSTALL_IN_PROGRESS'
- ].includes(status.installationStatus);
-
- if (inProgressStatus) {
- if (isProgressing) {
- return (
- <span className="marketplace-edition-badge badge badge-normal-size">
- {status.installationStatus === 'AUTOMATIC_IN_PROGRESS'
- ? translate('marketplace.installing')
- : translate('marketplace.pending')}
- </span>
- );
- }
- } else if (isInstalled) {
- return (
- <span className="marketplace-edition-badge badge badge-normal-size display-flex-center">
- <CheckIcon size={14} className="little-spacer-right" />
- {translate('marketplace.installed')}
- </span>
- );
- }
-
- return null;
-}
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
deleted file mode 100644
index a4c48287869..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionForm.tsx
+++ /dev/null
@@ -1,114 +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 * as React from 'react';
-import LicenseEditionSet from './LicenseEditionSet';
-import { Edition, EditionStatus, applyLicense } from '../../../api/marketplace';
-import Modal from '../../../components/controls/Modal';
-import { Button, ResetButtonLink } from '../../../components/ui/buttons';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-
-export interface Props {
- edition: Edition;
- editions: Edition[];
- isDowngrade: boolean;
- onClose: () => void;
- updateEditionStatus: (editionStatus: EditionStatus) => void;
-}
-
-interface State {
- license: string;
- status?: string;
- submitting: boolean;
-}
-
-export default class LicenseEditionForm extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { license: '', submitting: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleLicenseChange = (license: string, status?: string) => {
- if (this.mounted) {
- this.setState({ license, status });
- }
- };
-
- handleConfirmClick = () => {
- const { license, status } = this.state;
- if (license && status) {
- this.setState({ submitting: true });
- applyLicense({ license }).then(
- editionStatus => {
- this.props.updateEditionStatus(editionStatus);
- this.props.onClose();
- },
- () => {
- if (this.mounted) {
- this.setState({ submitting: false });
- }
- }
- );
- }
- };
-
- render() {
- const { edition, isDowngrade } = this.props;
- const { license, submitting, status } = this.state;
-
- const header = isDowngrade
- ? translateWithParameters('marketplace.downgrade_to_x', edition.name)
- : translateWithParameters('marketplace.upgrade_to_x', edition.name);
- return (
- <Modal contentLabel={header} onRequestClose={this.props.onClose}>
- <header className="modal-head">
- <h2>{header}</h2>
- </header>
-
- <LicenseEditionSet
- className="modal-body"
- edition={edition}
- editions={this.props.editions}
- updateLicense={this.handleLicenseChange}
- />
-
- <footer className="modal-foot">
- {submitting && <i className="spinner spacer-right" />}
- {status && (
- <Button
- className="js-confirm"
- disabled={!license || submitting}
- onClick={this.handleConfirmClick}>
- {status === 'AUTOMATIC_INSTALL'
- ? translate('marketplace.install')
- : translate('save')}
- </Button>
- )}
- <ResetButtonLink onClick={this.props.onClose}>{translate('cancel')}</ResetButtonLink>
- </footer>
- </Modal>
- );
- }
-}
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
deleted file mode 100644
index b8099a65079..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/LicenseEditionSet.tsx
+++ /dev/null
@@ -1,269 +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 { stringify } from 'querystring';
-import * as React from 'react';
-import * as classNames from 'classnames';
-import { FormattedMessage } from 'react-intl';
-import { debounce } from 'lodash';
-import Checkbox from '../../../components/controls/Checkbox';
-import DeferredSpinner from '../../../components/common/DeferredSpinner';
-import { omitNil } from '../../../helpers/request';
-import { Edition, getFormData, getLicensePreview } from '../../../api/marketplace';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
-
-export interface Props {
- className?: string;
- edition?: Edition;
- editions: Edition[];
- updateLicense: (license?: string, status?: string) => void;
-}
-
-interface State {
- acceptTerms: boolean;
- formData?: {
- serverId?: string;
- ncloc?: number;
- };
- license: string;
- licenseEdition?: Edition;
- loading: boolean;
- previewStatus?: string;
- wrongEdition: boolean;
-}
-
-export default class LicenseEditionSet extends React.PureComponent<Props, State> {
- mounted = false;
-
- constructor(props: Props) {
- super(props);
- this.state = { acceptTerms: false, license: '', loading: false, wrongEdition: false };
- this.fetchLicensePreview = debounce(this.fetchLicensePreview, 100);
- }
-
- componentDidMount() {
- this.mounted = true;
- this.fetchFormData();
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- fetchLicensePreview = (license: string) => {
- this.setState({ loading: true });
- getLicensePreview({ license }).then(
- ({ previewStatus, nextEditionKey }) => {
- if (this.mounted) {
- const { edition } = this.props;
- const licenseEdition = this.props.editions.find(
- edition => edition.key === nextEditionKey
- );
- const wrongEdition = Boolean(
- !licenseEdition || (edition && edition.key !== nextEditionKey)
- );
- this.setLicense({ license, loading: false, licenseEdition, previewStatus, wrongEdition });
- }
- },
- () => {
- if (this.mounted) {
- this.resetLicense({ license, loading: false });
- }
- }
- );
- };
-
- fetchFormData = () => {
- getFormData().then(
- formData => {
- if (this.mounted) {
- this.setState({ formData });
- }
- },
- () => {}
- );
- };
-
- getLicenseFormUrl = (edition: Edition) => {
- let url = edition.licenseRequestUrl;
- if (this.state.formData) {
- const query = stringify(omitNil(this.state.formData));
- if (query) {
- url += '?' + query;
- }
- }
- return url;
- };
-
- handleLicenseChange = (event: React.SyntheticEvent<HTMLTextAreaElement>) => {
- const license = event.currentTarget.value;
- if (license) {
- this.fetchLicensePreview(license);
- this.setState({ license });
- } else {
- this.resetLicense({});
- }
- };
-
- handleTermsCheck = (checked: boolean) => {
- this.setLicense({ acceptTerms: checked });
- };
-
- resetLicense<K extends keyof State>(state: Pick<State, K>) {
- this.setLicense(
- Object.assign(
- {
- license: '',
- licenseEdition: undefined,
- previewStatus: undefined,
- wrongEdition: false
- },
- state
- )
- );
- }
-
- setLicense<K extends keyof State>(state: Pick<State, K>) {
- this.setState(state, this.updateParentLicense);
- }
-
- updateParentLicense = () => {
- const { acceptTerms, license, previewStatus, wrongEdition } = this.state;
- this.props.updateLicense(
- previewStatus !== 'NO_INSTALL' && !acceptTerms ? undefined : license,
- wrongEdition ? undefined : previewStatus
- );
- };
-
- renderAlert() {
- const { licenseEdition, previewStatus, wrongEdition } = this.state;
- if (!previewStatus || wrongEdition) {
- const { edition } = this.props;
-
- return (
- <div className="spacer-top">
- {wrongEdition && (
- <p className="alert alert-danger">
- {edition
- ? translateWithParameters('marketplace.wrong_license_type_x', edition.name)
- : translate('marketplace.wrong_license_type')}
- </p>
- )}
- {edition && (
- <a href={this.getLicenseFormUrl(edition)} target="_blank">
- {translate('marketplace.i_need_a_license')}
- </a>
- )}
- </div>
- );
- }
-
- return (
- <div className="spacer-top">
- <p
- className={classNames('alert', {
- 'alert-warning': previewStatus === 'AUTOMATIC_INSTALL',
- 'alert-success': previewStatus === 'NO_INSTALL',
- 'alert-danger': previewStatus === 'MANUAL_INSTALL'
- })}>
- {translateWithParameters(
- 'marketplace.license_preview_status.' + previewStatus,
- licenseEdition ? licenseEdition.name : translate('marketplace.commercial_edition')
- )}
- {licenseEdition &&
- licenseEdition.key === 'datacenter' &&
- previewStatus !== 'NO_INSTALL' && (
- <span className="little-spacer-left">
- <FormattedMessage
- defaultMessage={translate('marketplace.how_to_setup_cluster_url')}
- id="marketplace.how_to_setup_cluster_url"
- values={{
- url: (
- <a
- href="https://redirect.sonarsource.com/doc/data-center-edition.html"
- rel="noopener noreferrer"
- target="_blank">
- {licenseEdition.name}
- </a>
- )
- }}
- />
- </span>
- )}
- </p>
- {previewStatus !== 'NO_INSTALL' && (
- <span className="js-edition-tos">
- <Checkbox
- checked={this.state.acceptTerms}
- id="edition-terms"
- onCheck={this.handleTermsCheck}>
- <label className="little-spacer-left" htmlFor="edition-terms">
- {translate('marketplace.i_accept_the')}
- </label>
- </Checkbox>
- <a
- className="nowrap little-spacer-left"
- href="http://dist.sonarsource.com/SonarSource_Terms_And_Conditions.pdf"
- rel="noopener noreferrer"
- target="_blank">
- {translate('marketplace.terms_and_conditions')}
- </a>
- </span>
- )}
- </div>
- );
- }
-
- render() {
- const { className, edition } = this.props;
- const { license, loading } = this.state;
-
- return (
- <div className={className}>
- {edition && (
- <label className="display-inline-block spacer-bottom" htmlFor="set-license">
- {translateWithParameters('marketplace.enter_license_for_x', edition.name)}
- <em className="mandatory">*</em>
- </label>
- )}
- <textarea
- autoFocus={true}
- className="display-block input-super-large"
- id="set-license"
- onChange={this.handleLicenseChange}
- required={true}
- rows={8}
- style={{ resize: 'none' }}
- value={license}
- />
-
- <DeferredSpinner
- customSpinner={
- <p className="spacer-top">
- <i className="spinner spacer-right text-bottom" />
- {translate('marketplace.checking_license')}
- </p>
- }
- loading={loading}>
- {this.renderAlert()}
- </DeferredSpinner>
- </div>
- );
- }
-}
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
deleted file mode 100644
index 1b9be29c7df..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/UninstallEditionForm.tsx
+++ /dev/null
@@ -1,94 +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 * as React from 'react';
-import { Edition, EditionStatus, uninstallEdition } from '../../../api/marketplace';
-import Modal from '../../../components/controls/Modal';
-import { Button, ResetButtonLink } from '../../../components/ui/buttons';
-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 = false;
- state: State = { loading: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- handleConfirmClick = () => {
- this.setState({ loading: true });
- uninstallEdition()
- .then(() => {
- this.props.updateEditionStatus({
- ...this.props.editionStatus,
- currentEditionKey: undefined,
- 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.downgrade_to_community_edition');
- return (
- <Modal contentLabel={header} 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 disabled={loading} onClick={this.handleConfirmClick}>
- {translate('marketplace.downgrade')}
- </Button>
- <ResetButtonLink className="js-modal-close" onClick={this.props.onClose}>
- {translate('cancel')}
- </ResetButtonLink>
- </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 c217058bb4f..ec0303053a4 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
@@ -19,46 +19,22 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
-import { Edition, EditionStatus } from '../../../../api/marketplace';
import EditionBox from '../EditionBox';
-const DEFAULT_STATUS: EditionStatus = {
- currentEditionKey: '',
- nextEditionKey: '',
- installationStatus: 'NONE'
-};
-
-const DEFAULT_EDITION: Edition = {
+const DEFAULT_EDITION = {
key: 'foo',
- name: 'Foo',
- textDescription: 'Foo desc',
downloadUrl: 'download_url',
- homeUrl: 'more_url',
- licenseRequestUrl: 'license_url'
+ homeUrl: 'more_url'
};
it('should display the edition', () => {
- expect(getWrapper()).toMatchSnapshot();
-});
-
-it('should disable action button', () => {
- expect(getWrapper({ disableAction: true })).toMatchSnapshot();
+ expect(getWrapper({ ncloc: 1000, serverId: 'serverId' })).toMatchSnapshot();
});
-it('should correctly hide action buttons', () => {
- expect(getWrapper({ displayAction: false })).toMatchSnapshot();
+it('should show insalled badge', () => {
+ expect(getWrapper({ currentEdition: 'foo' })).toMatchSnapshot();
});
function getWrapper(props = {}) {
- return shallow(
- <EditionBox
- actionLabel="action"
- disableAction={false}
- displayAction={true}
- edition={DEFAULT_EDITION}
- editionStatus={DEFAULT_STATUS}
- onAction={jest.fn()}
- {...props}
- />
- );
+ return shallow(<EditionBox currentEdition="community" edition={DEFAULT_EDITION} {...props} />);
}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx
deleted file mode 100644
index dfe79a80762..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx
+++ /dev/null
@@ -1,81 +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 * as React from 'react';
-import { shallow } from 'enzyme';
-import { EditionStatus } from '../../../../api/marketplace';
-import EditionBoxBadge from '../EditionBoxBadge';
-
-const DEFAULT_STATUS: EditionStatus = {
- currentEditionKey: '',
- nextEditionKey: '',
- installationStatus: 'NONE'
-};
-
-it('should display installed badge', () => {
- expect(
- getWrapper({
- status: {
- currentEditionKey: 'foo',
- nextEditionKey: '',
- installationStatus: 'NONE'
- }
- })
- ).toMatchSnapshot();
-});
-
-it('should display installing badge', () => {
- expect(
- getWrapper({
- status: {
- currentEditionKey: 'foo',
- nextEditionKey: 'foo',
- installationStatus: 'AUTOMATIC_IN_PROGRESS'
- }
- })
- ).toMatchSnapshot();
-});
-
-it('should display pending badge', () => {
- expect(
- getWrapper({
- status: {
- currentEditionKey: '',
- nextEditionKey: 'foo',
- installationStatus: 'AUTOMATIC_READY'
- }
- })
- ).toMatchSnapshot();
-});
-
-it('should not display a badge', () => {
- expect(
- getWrapper({
- status: {
- currentEditionKey: '',
- nextEditionKey: 'bar',
- installationStatus: 'AUTOMATIC_READY'
- }
- }).type()
- ).toBeNull();
-});
-
-function getWrapper(props = {}) {
- return shallow(<EditionBoxBadge editionKey="foo" status={DEFAULT_STATUS} {...props} />);
-}
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
deleted file mode 100644
index 9814570cbf8..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx
+++ /dev/null
@@ -1,98 +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.
- */
-/* eslint-disable import/order */
-import * as React from 'react';
-import { shallow } from 'enzyme';
-import { click } from '../../../../helpers/testUtils';
-import LicenseEditionForm from '../LicenseEditionForm';
-
-jest.mock('../../../../api/marketplace', () => ({
- applyLicense: jest.fn(() =>
- Promise.resolve({ nextEditionKey: 'foo', installationStatus: 'AUTOMATIC_IN_PROGRESS' })
- )
-}));
-
-const applyLicense = require('../../../../api/marketplace').applyLicense as jest.Mock<any>;
-
-const DEFAULT_EDITION = {
- key: 'foo',
- name: 'Foo',
- textDescription: 'Foo desc',
- downloadUrl: 'download_url',
- homeUrl: 'more_url',
- licenseRequestUrl: 'license_url'
-};
-
-beforeEach(() => {
- applyLicense.mockClear();
-});
-
-it('should display correctly', () => {
- expect(getWrapper()).toMatchSnapshot();
-});
-
-it('should correctly change the button based on the status and license', () => {
- const wrapper = getWrapper();
- let button;
- (wrapper.instance() as LicenseEditionForm).mounted = true;
-
- wrapper.setState({ license: 'mylicense', status: 'NO_INSTALL' });
- button = wrapper.find('Button');
- expect(button).toMatchSnapshot();
-
- wrapper.setState({ license: undefined, status: 'MANUAL_INSTALL' });
- button = wrapper.find('Button');
- expect(button).toMatchSnapshot();
-
- wrapper.setState({ status: 'AUTOMATIC_INSTALL' });
- button = wrapper.find('Button');
- expect(button).toMatchSnapshot();
-
- wrapper.setState({ license: 'mylicense' });
- expect(wrapper.find('Button').prop('disabled')).toBeFalsy();
-});
-
-it('should update the edition status after install', async () => {
- const updateEditionStatus = jest.fn();
- const wrapper = getWrapper({ updateEditionStatus });
- const form = wrapper.instance() as LicenseEditionForm;
- form.handleLicenseChange('mylicense', 'AUTOMATIC_INSTALL');
- wrapper.update();
- click(wrapper.find('Button'));
- expect(applyLicense).toHaveBeenCalledWith({ license: 'mylicense' });
- await new Promise(setImmediate);
- expect(updateEditionStatus).toHaveBeenCalledWith({
- nextEditionKey: 'foo',
- installationStatus: 'AUTOMATIC_IN_PROGRESS'
- });
-});
-
-function getWrapper(props = {}) {
- return shallow(
- <LicenseEditionForm
- edition={DEFAULT_EDITION}
- editions={[DEFAULT_EDITION]}
- isDowngrade={false}
- onClose={jest.fn()}
- updateEditionStatus={jest.fn()}
- {...props}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx
deleted file mode 100644
index 691f9c3613f..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx
+++ /dev/null
@@ -1,126 +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.
- */
-/* eslint-disable import/order */
-import * as React from 'react';
-import { shallow } from 'enzyme';
-import { change, waitAndUpdate } from '../../../../helpers/testUtils';
-import LicenseEditionSet from '../LicenseEditionSet';
-
-jest.mock('../../../../api/marketplace', () => ({
- getLicensePreview: jest.fn(() =>
- Promise.resolve({ nextEditionKey: 'foo', previewStatus: 'NO_INSTALL' })
- ),
- getFormData: jest.fn(() => Promise.resolve({ serverId: 'foo', ncloc: 1000 }))
-}));
-
-jest.mock('lodash', () => {
- const lodash = require.requireActual('lodash');
- lodash.debounce = (fn: Function) => (...args: any[]) => fn(...args);
- return lodash;
-});
-
-const getLicensePreview = require('../../../../api/marketplace').getLicensePreview as jest.Mock<
- any
->;
-
-const DEFAULT_EDITION = {
- key: 'foo',
- name: 'Foo',
- textDescription: 'Foo desc',
- downloadUrl: 'download_url',
- homeUrl: 'more_url',
- licenseRequestUrl: 'license_url'
-};
-
-beforeEach(() => {
- getLicensePreview.mockClear();
-});
-
-it('should display correctly', () => {
- expect(getWrapper()).toMatchSnapshot();
-});
-
-it('should display the get license link with parameters', async () => {
- const wrapper = getWrapper();
- await waitAndUpdate(wrapper);
- expect(wrapper.find('a')).toMatchSnapshot();
-});
-
-it('should correctly display status message after checking license', async () => {
- let wrapper = await testLicenseStatus('NO_INSTALL');
- expect(wrapper.find('p.alert')).toMatchSnapshot();
- wrapper = await testLicenseStatus('AUTOMATIC_INSTALL');
- expect(wrapper.find('p.alert')).toMatchSnapshot();
- wrapper = await testLicenseStatus('MANUAL_INSTALL');
- expect(wrapper.find('p.alert')).toMatchSnapshot();
- wrapper = await testLicenseStatus('AUTOMATIC_INSTALL', jest.fn(), 'bar');
- expect(wrapper.find('p.alert')).toMatchSnapshot();
- wrapper = await testLicenseStatus('AUTOMATIC_INSTALL', jest.fn(), 'bar', { edition: undefined });
- expect(wrapper.find('p.alert')).toMatchSnapshot();
-});
-
-it('should display terms of license checkbox', async () => {
- let updateLicense = jest.fn();
- let wrapper = await testLicenseStatus('NO_INSTALL', updateLicense);
- expect(wrapper.find('.js-edition-tos').exists()).toBeFalsy();
- expect(updateLicense).toHaveBeenCalledWith('mylicense', 'NO_INSTALL');
-
- updateLicense = jest.fn();
- wrapper = await testLicenseStatus('AUTOMATIC_INSTALL', updateLicense);
- const tosCheckbox = wrapper.find('.js-edition-tos');
- expect(tosCheckbox.find('a').exists()).toBeTruthy();
- expect(updateLicense).toHaveBeenLastCalledWith(undefined, 'AUTOMATIC_INSTALL');
- (tosCheckbox.find('Checkbox').prop('onCheck') as Function)(true);
- expect(updateLicense).toHaveBeenLastCalledWith('mylicense', 'AUTOMATIC_INSTALL');
-
- updateLicense = jest.fn();
- wrapper = await testLicenseStatus('MANUAL_INSTALL', updateLicense);
- expect(wrapper.find('.js-edition-tos').exists()).toBeTruthy();
- expect(updateLicense).toHaveBeenLastCalledWith(undefined, 'MANUAL_INSTALL');
-});
-
-function getWrapper(props = {}) {
- return shallow(
- <LicenseEditionSet
- edition={DEFAULT_EDITION}
- editions={[DEFAULT_EDITION]}
- updateLicense={jest.fn()}
- {...props}
- />
- );
-}
-
-async function testLicenseStatus(
- status: string,
- updateLicense = jest.fn(),
- nextEditionKey = 'foo',
- props = {}
-) {
- getLicensePreview.mockImplementation(() =>
- Promise.resolve({ nextEditionKey, previewStatus: status })
- );
- const wrapper = getWrapper({ updateLicense, ...props });
- change(wrapper.find('textarea'), 'mylicense');
- expect(getLicensePreview).toHaveBeenCalled();
- await new Promise(setImmediate);
- expect(updateLicense).toHaveBeenCalled();
- wrapper.update();
- return wrapper;
-}
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
deleted file mode 100644
index f8dbede4cb2..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/UninstallEditionForm-test.tsx
+++ /dev/null
@@ -1,72 +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.
- */
-/* eslint-disable import/order */
-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',
- textDescription: 'Foo desc',
- downloadUrl: 'download_url',
- homeUrl: 'more_url',
- licenseRequestUrl: '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: undefined,
- 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 b9925cec2c1..39c71155bc6 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
@@ -1,34 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should correctly hide action buttons 1`] = `
+exports[`should display the edition 1`] = `
<div
className="boxed-group boxed-group-inner marketplace-edition"
>
- <EditionBoxBadge
- editionKey="foo"
- status={
- Object {
- "currentEditionKey": "",
- "installationStatus": "NONE",
- "nextEditionKey": "",
- }
- }
- />
<div>
- <h3
- className="spacer-bottom"
- >
- Foo
- </h3>
- <p>
- Foo desc
- </p>
+ <DocInclude
+ path="/tooltips/editions/foo"
+ />
</div>
<div
className="marketplace-edition-action spacer-top"
>
<a
- href="more_url"
+ href="more_url?ncloc=1000&serverId=serverId&sourceEdition=community"
target="_blank"
>
marketplace.learn_more
@@ -37,88 +22,33 @@ exports[`should correctly hide action buttons 1`] = `
</div>
`;
-exports[`should disable action button 1`] = `
+exports[`should show insalled badge 1`] = `
<div
className="boxed-group boxed-group-inner marketplace-edition"
>
- <EditionBoxBadge
- editionKey="foo"
- status={
- Object {
- "currentEditionKey": "",
- "installationStatus": "NONE",
- "nextEditionKey": "",
- }
- }
- />
- <div>
- <h3
- className="spacer-bottom"
- >
- Foo
- </h3>
- <p>
- Foo desc
- </p>
- </div>
- <div
- className="marketplace-edition-action spacer-top"
+ <span
+ className="marketplace-edition-badge badge badge-normal-size display-flex-center"
>
- <a
- href="more_url"
- target="_blank"
- >
- marketplace.learn_more
- </a>
- <Button
- disabled={true}
- onClick={[Function]}
- >
- action
- </Button>
- </div>
-</div>
-`;
-
-exports[`should display the edition 1`] = `
-<div
- className="boxed-group boxed-group-inner marketplace-edition"
->
- <EditionBoxBadge
- editionKey="foo"
- status={
- Object {
- "currentEditionKey": "",
- "installationStatus": "NONE",
- "nextEditionKey": "",
- }
- }
- />
+ <CheckIcon
+ className="little-spacer-right"
+ size={14}
+ />
+ marketplace.installed
+ </span>
<div>
- <h3
- className="spacer-bottom"
- >
- Foo
- </h3>
- <p>
- Foo desc
- </p>
+ <DocInclude
+ path="/tooltips/editions/foo"
+ />
</div>
<div
className="marketplace-edition-action spacer-top"
>
<a
- href="more_url"
+ href="more_url?sourceEdition=foo"
target="_blank"
>
marketplace.learn_more
</a>
- <Button
- disabled={false}
- onClick={[Function]}
- >
- action
- </Button>
</div>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap
deleted file mode 100644
index 733b0062157..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap
+++ /dev/null
@@ -1,29 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display installed badge 1`] = `
-<span
- className="marketplace-edition-badge badge badge-normal-size display-flex-center"
->
- <CheckIcon
- className="little-spacer-right"
- size={14}
- />
- marketplace.installed
-</span>
-`;
-
-exports[`should display installing badge 1`] = `
-<span
- className="marketplace-edition-badge badge badge-normal-size"
->
- marketplace.installing
-</span>
-`;
-
-exports[`should display pending badge 1`] = `
-<span
- className="marketplace-edition-badge badge badge-normal-size"
->
- marketplace.pending
-</span>
-`;
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
deleted file mode 100644
index c0dcce3c40a..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap
+++ /dev/null
@@ -1,81 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should correctly change the button based on the status and license 1`] = `
-<Button
- className="js-confirm"
- disabled={false}
- onClick={[Function]}
->
- save
-</Button>
-`;
-
-exports[`should correctly change the button based on the status and license 2`] = `
-<Button
- className="js-confirm"
- disabled={true}
- onClick={[Function]}
->
- save
-</Button>
-`;
-
-exports[`should correctly change the button based on the status and license 3`] = `
-<Button
- className="js-confirm"
- disabled={true}
- onClick={[Function]}
->
- marketplace.install
-</Button>
-`;
-
-exports[`should display correctly 1`] = `
-<Modal
- contentLabel="marketplace.upgrade_to_x.Foo"
- onRequestClose={[MockFunction]}
->
- <header
- className="modal-head"
- >
- <h2>
- marketplace.upgrade_to_x.Foo
- </h2>
- </header>
- <LicenseEditionSet
- className="modal-body"
- edition={
- Object {
- "downloadUrl": "download_url",
- "homeUrl": "more_url",
- "key": "foo",
- "licenseRequestUrl": "license_url",
- "name": "Foo",
- "textDescription": "Foo desc",
- }
- }
- editions={
- Array [
- Object {
- "downloadUrl": "download_url",
- "homeUrl": "more_url",
- "key": "foo",
- "licenseRequestUrl": "license_url",
- "name": "Foo",
- "textDescription": "Foo desc",
- },
- ]
- }
- updateLicense={[Function]}
- />
- <footer
- className="modal-foot"
- >
- <ResetButtonLink
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
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
deleted file mode 100644
index 5ff0d2f7d37..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap
+++ /dev/null
@@ -1,105 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should correctly display status message after checking license 1`] = `
-<p
- className="alert alert-success"
->
- marketplace.license_preview_status.NO_INSTALL.Foo
-</p>
-`;
-
-exports[`should correctly display status message after checking license 2`] = `
-<p
- className="alert alert-warning"
->
- marketplace.license_preview_status.AUTOMATIC_INSTALL.Foo
-</p>
-`;
-
-exports[`should correctly display status message after checking license 3`] = `
-<p
- className="alert alert-danger"
->
- marketplace.license_preview_status.MANUAL_INSTALL.Foo
-</p>
-`;
-
-exports[`should correctly display status message after checking license 4`] = `
-<p
- className="alert alert-danger"
->
- marketplace.wrong_license_type_x.Foo
-</p>
-`;
-
-exports[`should correctly display status message after checking license 5`] = `
-<p
- className="alert alert-danger"
->
- marketplace.wrong_license_type
-</p>
-`;
-
-exports[`should display correctly 1`] = `
-<div>
- <label
- className="display-inline-block spacer-bottom"
- htmlFor="set-license"
- >
- marketplace.enter_license_for_x.Foo
- <em
- className="mandatory"
- >
- *
- </em>
- </label>
- <textarea
- autoFocus={true}
- className="display-block input-super-large"
- id="set-license"
- onChange={[Function]}
- required={true}
- rows={8}
- style={
- Object {
- "resize": "none",
- }
- }
- value=""
- />
- <DeferredSpinner
- customSpinner={
- <p
- className="spacer-top"
- >
- <i
- className="spinner spacer-right text-bottom"
- />
- marketplace.checking_license
- </p>
- }
- loading={false}
- timeout={100}
- >
- <div
- className="spacer-top"
- >
- <a
- href="license_url"
- target="_blank"
- >
- marketplace.i_need_a_license
- </a>
- </div>
- </DeferredSpinner>
-</div>
-`;
-
-exports[`should display the get license link with parameters 1`] = `
-<a
- href="license_url?serverId=foo&ncloc=1000"
- target="_blank"
->
- marketplace.i_need_a_license
-</a>
-`;
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
deleted file mode 100644
index 491d59eb8f5..00000000000
--- a/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/UninstallEditionForm-test.tsx.snap
+++ /dev/null
@@ -1,39 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display correctly 1`] = `
-<Modal
- contentLabel="marketplace.downgrade_to_community_edition."
- onRequestClose={[MockFunction]}
->
- <header
- className="modal-head"
- >
- <h2>
- marketplace.downgrade_to_community_edition.
- </h2>
- </header>
- <div
- className="modal-body"
- >
- <p>
- marketplace.uninstall_x_confirmation.Foo
- </p>
- </div>
- <footer
- className="modal-foot"
- >
- <Button
- disabled={false}
- onClick={[Function]}
- >
- marketplace.downgrade
- </Button>
- <ResetButtonLink
- className="js-modal-close"
- onClick={[MockFunction]}
- >
- cancel
- </ResetButtonLink>
- </footer>
-</Modal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/style.css b/server/sonar-web/src/main/js/apps/marketplace/style.css
index 9e51642bbc5..0e2ef2a2831 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/style.css
+++ b/server/sonar-web/src/main/js/apps/marketplace/style.css
@@ -35,6 +35,11 @@
margin-right: 8px;
}
+.marketplace-edition .markdown h3 {
+ font-size: 14px;
+ margin-top: 0;
+}
+
.marketplace-edition-badge {
position: absolute;
right: -1px;
diff --git a/server/sonar-web/src/main/js/apps/marketplace/utils.ts b/server/sonar-web/src/main/js/apps/marketplace/utils.ts
index 690b6873121..9aebc85c3ad 100644
--- a/server/sonar-web/src/main/js/apps/marketplace/utils.ts
+++ b/server/sonar-web/src/main/js/apps/marketplace/utils.ts
@@ -17,26 +17,58 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { memoize, sortBy } from 'lodash';
+import { stringify } from 'querystring';
+import { memoize } from 'lodash';
import { Plugin, PluginAvailable, PluginInstalled, PluginPending } from '../../api/plugins';
import { cleanQuery, parseAsString, RawQuery, serializeString } from '../../helpers/query';
-import { Edition } from '../../api/marketplace';
+import { omitNil } from '../../helpers/request';
+
+export interface Edition {
+ downloadUrl?: string;
+ homeUrl: string;
+ key: string;
+}
export interface Query {
filter: string;
search?: string;
}
-export function isPluginAvailable(plugin: Plugin): plugin is PluginAvailable {
- return (plugin as any).release !== undefined;
-}
+export const EDITIONS: Edition[] = [
+ {
+ key: 'community',
+ homeUrl: 'https://redirect.sonarsource.com/editions/community.html'
+ },
+ {
+ key: 'developer',
+ homeUrl: 'https://redirect.sonarsource.com/editions/developer.html',
+ downloadUrl:
+ 'https://sonarsource.bintray.com/CommercialDistribution/editions/developer-edition-7.0.0.717.zip'
+ },
+ {
+ key: 'enterprise',
+ homeUrl: 'https://redirect.sonarsource.com/editions/enterprise.html',
+ downloadUrl:
+ 'https://sonarsource.bintray.com/CommercialDistribution/editions/enterprise-edition-7.0.0.717.zip'
+ },
+ {
+ key: 'datacenter',
+ homeUrl: 'https://redirect.sonarsource.com/editions/datacenter.html',
+ downloadUrl:
+ 'https://sonarsource.bintray.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip'
+ }
+];
-export function isPluginInstalled(plugin: Plugin): plugin is PluginInstalled {
- return isPluginPending(plugin) && (plugin as any).updatedAt !== undefined;
-}
-
-export function isPluginPending(plugin: Plugin): plugin is PluginPending {
- return (plugin as any).version !== undefined;
+export function getEditionUrl(
+ edition: Edition,
+ data: { serverId?: string; ncloc?: number; sourceEdition: string }
+) {
+ let url = edition.homeUrl;
+ const query = stringify(omitNil(data));
+ if (query) {
+ url += '?' + query;
+ }
+ return url;
}
export function filterPlugins(plugins: Plugin[], search: string): Plugin[] {
@@ -50,9 +82,16 @@ export function filterPlugins(plugins: Plugin[], search: string): Plugin[] {
});
}
-const EDITIONS_ORDER = ['community', 'developer', 'enterprise', 'datacenter'];
-export function sortEditions(editions: Edition[]): Edition[] {
- return sortBy(editions, edition => EDITIONS_ORDER.indexOf(edition.key));
+export function isPluginAvailable(plugin: Plugin): plugin is PluginAvailable {
+ return (plugin as any).release !== undefined;
+}
+
+export function isPluginInstalled(plugin: Plugin): plugin is PluginInstalled {
+ return isPluginPending(plugin) && (plugin as any).updatedAt !== undefined;
+}
+
+export function isPluginPending(plugin: Plugin): plugin is PluginPending {
+ return (plugin as any).version !== undefined;
}
export const DEFAULT_FILTER = 'all';
diff --git a/server/sonar-web/src/main/js/store/marketplace/actions.ts b/server/sonar-web/src/main/js/store/marketplace/actions.ts
index 894c74f2398..7e9b6156d9c 100644
--- a/server/sonar-web/src/main/js/store/marketplace/actions.ts
+++ b/server/sonar-web/src/main/js/store/marketplace/actions.ts
@@ -18,62 +18,34 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { Dispatch } from 'react-redux';
-import { getEditionsForVersion, getEditionsForLastVersion } from './utils';
-import { Edition, EditionStatus, getEditionStatus, getEditionsList } from '../../api/marketplace';
+import { getEditionStatus } from '../../api/marketplace';
import { getPendingPlugins, PluginPendingResult } from '../../api/plugins';
-interface LoadEditionsAction {
- type: 'LOAD_EDITIONS';
- loading: boolean;
-}
-
interface SetPendingPluginsAction {
type: 'SET_PENDING_PLUGINS';
pending: PluginPendingResult;
}
-interface SetEditionsAction {
- type: 'SET_EDITIONS';
- editions: Edition[];
- readOnly: boolean;
-}
-
-interface SetEditionStatusAction {
- type: 'SET_EDITION_STATUS';
- status: EditionStatus;
+interface SetCurrentEditionAction {
+ type: 'SET_CURRENT_EDITION';
+ currentEdition?: string;
}
-export type Action =
- | LoadEditionsAction
- | SetEditionsAction
- | SetEditionStatusAction
- | SetPendingPluginsAction;
-
-export function loadEditions(loading = true): LoadEditionsAction {
- return { type: 'LOAD_EDITIONS', loading };
-}
+export type Action = SetCurrentEditionAction | SetPendingPluginsAction;
export function setPendingPlugins(pending: PluginPendingResult): SetPendingPluginsAction {
return { type: 'SET_PENDING_PLUGINS', pending };
}
-export function setEditions(editions: Edition[], readOnly?: boolean): SetEditionsAction {
- return { type: 'SET_EDITIONS', editions, readOnly: !!readOnly };
-}
+export const setCurrentEdition = (currentEdition?: string) => (dispatch: Dispatch<Action>) => {
+ dispatch({ type: 'SET_CURRENT_EDITION', currentEdition });
+};
-let editionTimer: number | undefined;
-export const setEditionStatus = (status: EditionStatus) => (dispatch: Dispatch<Action>) => {
- dispatch({ type: 'SET_EDITION_STATUS', status });
- if (editionTimer) {
- window.clearTimeout(editionTimer);
- editionTimer = undefined;
- }
- if (status.installationStatus === 'AUTOMATIC_IN_PROGRESS') {
- editionTimer = window.setTimeout(() => {
- getEditionStatus().then(status => setEditionStatus(status)(dispatch), () => {});
- editionTimer = undefined;
- }, 2000);
- }
+export const fetchCurrentEdition = () => (dispatch: Dispatch<Action>) => {
+ getEditionStatus().then(
+ editionStatus => dispatch(setCurrentEdition(editionStatus.currentEditionKey)),
+ () => {}
+ );
};
export const fetchPendingPlugins = () => (dispatch: Dispatch<Action>) => {
@@ -84,18 +56,3 @@ export const fetchPendingPlugins = () => (dispatch: Dispatch<Action>) => {
() => {}
);
};
-
-export const fetchEditions = (url: string, version: string) => (dispatch: Dispatch<Action>) => {
- dispatch(loadEditions(true));
- getEditionsList(url).then(
- editionsPerVersion => {
- const editions = getEditionsForVersion(editionsPerVersion, version);
- if (editions) {
- dispatch(setEditions(editions));
- } else {
- dispatch(setEditions(getEditionsForLastVersion(editionsPerVersion), true));
- }
- },
- () => dispatch(loadEditions(false))
- );
-};
diff --git a/server/sonar-web/src/main/js/store/marketplace/reducer.ts b/server/sonar-web/src/main/js/store/marketplace/reducer.ts
index 11c5e28d7f5..55ad93d69db 100644
--- a/server/sonar-web/src/main/js/store/marketplace/reducer.ts
+++ b/server/sonar-web/src/main/js/store/marketplace/reducer.ts
@@ -18,48 +18,30 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { Action } from './actions';
-import { Edition, EditionStatus } from '../../api/marketplace';
import { PluginPendingResult } from '../../api/plugins';
interface State {
- editions?: Edition[];
- loading: boolean;
- status?: EditionStatus;
- readOnly: boolean;
+ currentEdition?: string;
pending: PluginPendingResult;
}
-const defaultState: State = {
- loading: true,
- readOnly: false,
- pending: { installing: [], removing: [], updating: [] }
-};
+const defaultState: State = { pending: { installing: [], removing: [], updating: [] } };
export default function(state: State = defaultState, action: Action): State {
- if (action.type === 'SET_EDITIONS') {
- return { ...state, editions: action.editions, readOnly: action.readOnly, loading: false };
- }
- if (action.type === 'LOAD_EDITIONS') {
- return { ...state, loading: action.loading };
- }
if (action.type === 'SET_PENDING_PLUGINS') {
return {
...state,
pending: action.pending
};
}
- if (action.type === 'SET_EDITION_STATUS') {
- const hasChanged = Object.keys(action.status).some(
- (key: keyof EditionStatus) => !state.status || state.status[key] !== action.status[key]
- );
- // Prevent from rerendering the whole admin if the status didn't change
- if (hasChanged) {
- return { ...state, status: action.status };
- }
+ if (action.type === 'SET_CURRENT_EDITION') {
+ return {
+ ...state,
+ currentEdition: action.currentEdition
+ };
}
return state;
}
-export const getEditions = (state: State) => state.editions;
-export const getEditionStatus = (state: State) => state.status;
+export const getCurrentEdition = (state: State) => state.currentEdition;
export const getPendingPlugins = (state: State) => state.pending;
diff --git a/server/sonar-web/src/main/js/store/marketplace/utils.ts b/server/sonar-web/src/main/js/store/marketplace/utils.ts
deleted file mode 100644
index 7e22118a78d..00000000000
--- a/server/sonar-web/src/main/js/store/marketplace/utils.ts
+++ /dev/null
@@ -1,49 +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 { sortBy } from 'lodash';
-import { Edition, EditionsPerVersion } from '../../api/marketplace';
-
-export function getEditionsForLastVersion(editions: EditionsPerVersion): Edition[] {
- const sortedVersion = sortBy(Object.keys(editions), [
- (version: string) => -Number(version.split('.')[0]),
- (version: string) => -Number(version.split('.')[1] || 0),
- (version: string) => -Number(version.split('.')[2] || 0)
- ]);
- return editions[sortedVersion[0]];
-}
-
-export function getEditionsForVersion(
- editions: EditionsPerVersion,
- version: string
-): Edition[] | undefined {
- const minorVersion = version.match(/\d+\.\d+.\d+/);
- if (minorVersion) {
- if (editions[minorVersion[0]]) {
- return editions[minorVersion[0]];
- }
- }
- const majorVersion = version.match(/\d+\.\d+/);
- if (majorVersion) {
- if (editions[majorVersion[0]]) {
- return editions[majorVersion[0]];
- }
- }
- return undefined;
-}
diff --git a/server/sonar-web/src/main/js/store/rootReducer.js b/server/sonar-web/src/main/js/store/rootReducer.js
index 0ca7aab07c4..7581381ce28 100644
--- a/server/sonar-web/src/main/js/store/rootReducer.js
+++ b/server/sonar-web/src/main/js/store/rootReducer.js
@@ -73,10 +73,8 @@ export const isFavorite = (state, componentKey) =>
export const getMarketplaceState = state => state.marketplace;
-export const getMarketplaceEditions = state => fromMarketplace.getEditions(state.marketplace);
-
-export const getMarketplaceEditionStatus = state =>
- fromMarketplace.getEditionStatus(state.marketplace);
+export const getMarketplaceCurrentEdition = state =>
+ fromMarketplace.getCurrentEdition(state.marketplace);
export const getMarketplacePendingPlugins = state =>
fromMarketplace.getPendingPlugins(state.marketplace);