From: Grégoire Aubert Date: Fri, 1 Jun 2018 09:04:05 +0000 (+0200) Subject: SONAR-10696 Display wrong edition error on license page X-Git-Tag: 7.5~1022 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=7ab10a96ddaaff5b21e139fbac1fb54afd5bdf85;p=sonarqube.git SONAR-10696 Display wrong edition error on license page * Handle optional "edition" field in api/navigation/global correctly * Introduce enum of edition keys --- diff --git a/server/sonar-web/src/main/js/api/marketplace.ts b/server/sonar-web/src/main/js/api/marketplace.ts index 0681931e509..a90f4fcc24f 100644 --- a/server/sonar-web/src/main/js/api/marketplace.ts +++ b/server/sonar-web/src/main/js/api/marketplace.ts @@ -28,6 +28,7 @@ export interface License { isExpired: boolean; isOfficialDistribution: boolean; isSupported: boolean; + isValidEdition: boolean; isValidServerId: boolean; loc: number; maxLoc: number; diff --git a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx index 363102ff653..d50fd613b48 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx +++ b/server/sonar-web/src/main/js/app/components/GlobalFooter.tsx @@ -22,20 +22,21 @@ import { Link } from 'react-router'; import GlobalFooterSonarCloud from './GlobalFooterSonarCloud'; import GlobalFooterBranding from './GlobalFooterBranding'; import InstanceMessage from '../../components/common/InstanceMessage'; -import { EDITIONS } from '../../apps/marketplace/utils'; +import { EDITIONS, EditionKey } from '../../apps/marketplace/utils'; import { translate, translateWithParameters } from '../../helpers/l10n'; import { isSonarCloud } from '../../helpers/system'; interface Props { hideLoggedInInfo?: boolean; productionDatabase: boolean; - sonarqubeEdition: string; + sonarqubeEdition?: EditionKey; sonarqubeVersion?: string; } export default function GlobalFooter({ hideLoggedInInfo, productionDatabase, + sonarqubeEdition, sonarqubeVersion }: Props) { if (isSonarCloud()) { diff --git a/server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx b/server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx index 915fb155f2f..4f079504d72 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx @@ -20,10 +20,11 @@ import { connect } from 'react-redux'; import GlobalFooter from './GlobalFooter'; import { getAppState } from '../../store/rootReducer'; +import { EditionKey } from '../../apps/marketplace/utils'; interface StateProps { productionDatabase: boolean; - sonarqubeEdition: string; + sonarqubeEdition?: EditionKey; sonarqubeVersion?: string; } diff --git a/server/sonar-web/src/main/js/app/components/StartupModal.tsx b/server/sonar-web/src/main/js/app/components/StartupModal.tsx index f71ddd87bad..b2429cccd19 100644 --- a/server/sonar-web/src/main/js/app/components/StartupModal.tsx +++ b/server/sonar-web/src/main/js/app/components/StartupModal.tsx @@ -22,17 +22,18 @@ import * as PropTypes from 'prop-types'; import { connect } from 'react-redux'; import OnboardingModal from '../../apps/tutorials/onboarding/OnboardingModal'; import LicensePromptModal from '../../apps/marketplace/components/LicensePromptModal'; -import { showLicense } from '../../api/marketplace'; +import { CurrentUser, isLoggedIn } from '../types'; import { differenceInDays, parseDate, toShortNotSoISOString } from '../../helpers/dates'; -import { hasMessage } from '../../helpers/l10n'; -import { save, get } from '../../helpers/storage'; +import { EditionKey } from '../../apps/marketplace/utils'; import { getCurrentUser, getAppState } from '../../store/rootReducer'; import { skipOnboarding } from '../../store/users/actions'; -import { CurrentUser, isLoggedIn } from '../types'; +import { showLicense } from '../../api/marketplace'; +import { hasMessage } from '../../helpers/l10n'; +import { save, get } from '../../helpers/storage'; interface StateProps { canAdmin: boolean; - currentEdition: string; + currentEdition?: EditionKey; currentUser: CurrentUser; } @@ -77,16 +78,22 @@ export class StartupModal extends React.PureComponent { } closeOnboarding = () => { - this.setState(state => ({ - modal: state.modal === ModalKey.onboarding ? undefined : state.modal - })); - this.props.skipOnboarding(); + this.setState(state => { + if (state.modal === ModalKey.onboarding) { + this.props.skipOnboarding(); + return { modal: undefined }; + } + return undefined; + }); }; closeLicense = () => { - this.setState(state => ({ - modal: state.modal === ModalKey.license ? undefined : state.modal - })); + this.setState(state => { + if (state.modal === ModalKey.license) { + return { modal: undefined }; + } + return undefined; + }); }; openOnboarding = () => { @@ -96,16 +103,14 @@ export class StartupModal extends React.PureComponent { tryAutoOpenLicense = () => { const { canAdmin, currentEdition, currentUser } = this.props; const hasLicenseManager = hasMessage('license.prompt.title'); - if ( - currentEdition !== 'community' && - isLoggedIn(currentUser) && - canAdmin && - hasLicenseManager - ) { + const hasLicensedEdition = currentEdition && currentEdition !== EditionKey.community; + + if (canAdmin && hasLicensedEdition && isLoggedIn(currentUser) && hasLicenseManager) { const lastPrompt = get(LICENSE_PROMPT, currentUser.login); + if (!lastPrompt || differenceInDays(new Date(), parseDate(lastPrompt)) >= 1) { return showLicense().then(license => { - if (!license || license.edition !== currentEdition) { + if (!license || !license.isValidEdition) { save(LICENSE_PROMPT, toShortNotSoISOString(new Date()), currentUser.login); this.setState({ modal: ModalKey.license }); return Promise.resolve(); diff --git a/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx index 47d63dd2ffc..dad1ed27cab 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx @@ -20,6 +20,7 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import GlobalFooter from '../GlobalFooter'; +import { EditionKey } from '../../../apps/marketplace/utils'; import { isSonarCloud } from '../../../helpers/system'; jest.mock('../../../helpers/system', () => ({ isSonarCloud: jest.fn() })); @@ -40,7 +41,7 @@ it('should show the db warning message', () => { it('should display the sq version', () => { expect( - getWrapper({ sonarqubeEdition: 'enterprise', sonarqubeVersion: '6.4-SNAPSHOT' }) + getWrapper({ sonarqubeEdition: EditionKey.enterprise, sonarqubeVersion: '6.4-SNAPSHOT' }) ).toMatchSnapshot(); }); @@ -50,5 +51,5 @@ it('should render SonarCloud footer', () => { function getWrapper(props = {}, onSonarCloud = false) { (isSonarCloud as jest.Mock).mockImplementation(() => onSonarCloud); - return shallow(); + return shallow(); } diff --git a/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx index dee4a3bcd29..0f8cbe18131 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx @@ -26,6 +26,7 @@ import { hasMessage } from '../../../helpers/l10n'; import { waitAndUpdate } from '../../../helpers/testUtils'; import { differenceInDays, toShortNotSoISOString } from '../../../helpers/dates'; import { LoggedInUser } from '../../types'; +import { EditionKey } from '../../../apps/marketplace/utils'; jest.mock('../../../api/marketplace', () => ({ showLicense: jest.fn().mockResolvedValue(undefined) @@ -63,7 +64,7 @@ beforeEach(() => { }); it('should render only the children', async () => { - const wrapper = getWrapper({ currentEdition: 'community' }); + const wrapper = getWrapper({ currentEdition: EditionKey.community }); await shouldNotHaveModals(wrapper); expect(showLicense).toHaveBeenCalledTimes(0); expect(wrapper.find('div').exists()).toBeTruthy(); @@ -73,7 +74,7 @@ it('should render only the children', async () => { (hasMessage as jest.Mock).mockReturnValueOnce(false); await shouldNotHaveModals(getWrapper()); - (showLicense as jest.Mock).mockResolvedValueOnce({ edition: 'enterprise' }); + (showLicense as jest.Mock).mockResolvedValueOnce({ isValidEdition: true }); await shouldNotHaveModals(getWrapper()); (get as jest.Mock).mockReturnValueOnce('date'); @@ -89,7 +90,7 @@ it('should render license prompt', async () => { (differenceInDays as jest.Mock).mockReturnValueOnce(1); await shouldDisplayLicense(getWrapper()); - (showLicense as jest.Mock).mockResolvedValueOnce({ edition: 'developer' }); + (showLicense as jest.Mock).mockResolvedValueOnce({ isValidEdition: false }); await shouldDisplayLicense(getWrapper()); }); @@ -101,7 +102,7 @@ it('should render onboarding modal', async () => { }) ); - (showLicense as jest.Mock).mockResolvedValueOnce({ edition: 'enterprise' }); + (showLicense as jest.Mock).mockResolvedValueOnce({ isValidEdition: true }); await shouldDisplayOnboarding( getWrapper({ currentUser: { ...LOGGED_IN_USER, showOnboardingTutorial: true } }) ); @@ -127,7 +128,7 @@ function getWrapper(props = {}) { return shallow( 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 486f4e2bd17..2e80802d8a9 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -26,7 +26,7 @@ import EditionBoxes from './EditionBoxes'; import Footer from './Footer'; import PluginsList from './PluginsList'; import Search from './Search'; -import { filterPlugins, parseQuery, Query, serializeQuery } from './utils'; +import { filterPlugins, parseQuery, Query, serializeQuery, EditionKey } from './utils'; import Suggestions from '../../app/components/embed-docs-modal/Suggestions'; import { getAvailablePlugins, @@ -41,7 +41,7 @@ import { translate } from '../../helpers/l10n'; import './style.css'; export interface Props { - currentEdition?: string; + currentEdition?: EditionKey; fetchPendingPlugins: () => void; location: { pathname: string; query: RawQuery }; pendingPlugins: PluginPendingResult; @@ -119,7 +119,7 @@ export default class App extends React.PureComponent { }; render() { - const { currentEdition = 'community', 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; 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 78f4496d67a..a5e2fbc24eb 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx @@ -19,6 +19,7 @@ */ import { connect } from 'react-redux'; import App from './App'; +import { EditionKey } from './utils'; import { getAppState, getGlobalSettingValue, @@ -33,7 +34,7 @@ interface OwnProps { } interface StateToProps { - currentEdition?: string; + currentEdition?: EditionKey; pendingPlugins: PluginPendingResult; standaloneMode: boolean; updateCenterActive: boolean; 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 f79b349ec69..a9ee399fffc 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx @@ -20,11 +20,11 @@ import * as React from 'react'; import EditionBox from './components/EditionBox'; -import { EDITIONS } from './utils'; +import { EDITIONS, EditionKey } from './utils'; import { getFormData } from '../../api/marketplace'; export interface Props { - currentEdition: string; + currentEdition?: EditionKey; } interface State { diff --git a/server/sonar-web/src/main/js/apps/marketplace/Header.tsx b/server/sonar-web/src/main/js/apps/marketplace/Header.tsx index 2c17f03f8e7..cfff7fe32ad 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/Header.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/Header.tsx @@ -18,21 +18,24 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { EditionKey } from './utils'; import { translate } from '../../helpers/l10n'; interface Props { - currentEdition: string; + currentEdition?: EditionKey; } export default function Header({ currentEdition }: Props) { return (

{translate('marketplace.page')}

-

- {translate('marketplace.page.you_are_running', currentEdition)} -

+ {currentEdition && ( +

+ {translate('marketplace.page.you_are_running', currentEdition)} +

+ )}

- {currentEdition === 'datacenter' + {currentEdition === EditionKey.datacenter ? translate('marketplace.page.description_best_edition') : translate('marketplace.page.description')}

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 9c35ee382d6..ac0fac8a9f7 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,44 +20,24 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import EditionBoxes from '../EditionBoxes'; - -jest.mock('../utils', () => ({ - EDITIONS: [ - { key: 'community', name: 'Community Edition', homeUrl: 'more_url' }, - { - key: 'developer', - name: 'Developer Edition', - homeUrl: 'more_url' - }, - { - key: 'enterprise', - name: 'Enterprise Edition', - homeUrl: 'more_url' - }, - { - key: 'datacenter', - name: 'Data Center Edition', - homeUrl: 'more_url' - } - ] -})); +import { EditionKey } from '../utils'; it('should display the available edition boxes correctly', () => { expect(getWrapper()).toMatchSnapshot(); }); it('should display the enterprise and datacenter edition boxes', () => { - expect(getWrapper({ currentEdition: 'developer' })).toMatchSnapshot(); + expect(getWrapper({ currentEdition: EditionKey.developer })).toMatchSnapshot(); }); it('should display the datacenter edition box only', () => { - expect(getWrapper({ currentEdition: 'enterprise' })).toMatchSnapshot(); + expect(getWrapper({ currentEdition: EditionKey.enterprise })).toMatchSnapshot(); }); it('should not display any edition box', () => { - expect(getWrapper({ currentEdition: 'datacenter' }).type()).toBeNull(); + expect(getWrapper({ currentEdition: EditionKey.datacenter }).type()).toBeNull(); }); function getWrapper(props = {}) { - return shallow(); + return shallow(); } diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/Header-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/Header-test.tsx index 3e0a609c781..d09ca690c26 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/Header-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/Header-test.tsx @@ -20,8 +20,9 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import Header from '../Header'; +import { EditionKey } from '../utils'; it('should render with installed editions', () => { - expect(shallow(
)).toMatchSnapshot(); - expect(shallow(
)).toMatchSnapshot(); + expect(shallow(
)).toMatchSnapshot(); + expect(shallow(
)).toMatchSnapshot(); }); 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 d8abf481899..b27aa61041a 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 @@ -8,7 +8,8 @@ exports[`should display the available edition boxes correctly 1`] = ` currentEdition="community" edition={ Object { - "homeUrl": "more_url", + "downloadUrl": "https://sonarsource.bintray.com/CommercialDistribution/editions/developer-edition-7.0.0.717.zip", + "homeUrl": "https://redirect.sonarsource.com/editions/developer.html", "key": "developer", "name": "Developer Edition", } @@ -19,7 +20,8 @@ exports[`should display the available edition boxes correctly 1`] = ` currentEdition="community" edition={ Object { - "homeUrl": "more_url", + "downloadUrl": "https://sonarsource.bintray.com/CommercialDistribution/editions/enterprise-edition-7.0.0.717.zip", + "homeUrl": "https://redirect.sonarsource.com/editions/enterprise.html", "key": "enterprise", "name": "Enterprise Edition", } @@ -30,7 +32,8 @@ exports[`should display the available edition boxes correctly 1`] = ` currentEdition="community" edition={ Object { - "homeUrl": "more_url", + "downloadUrl": "https://sonarsource.bintray.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip", + "homeUrl": "https://redirect.sonarsource.com/editions/datacenter.html", "key": "datacenter", "name": "Data Center Edition", } @@ -48,7 +51,8 @@ exports[`should display the datacenter edition box only 1`] = ` currentEdition="enterprise" edition={ Object { - "homeUrl": "more_url", + "downloadUrl": "https://sonarsource.bintray.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip", + "homeUrl": "https://redirect.sonarsource.com/editions/datacenter.html", "key": "datacenter", "name": "Data Center Edition", } @@ -66,7 +70,8 @@ exports[`should display the enterprise and datacenter edition boxes 1`] = ` currentEdition="developer" edition={ Object { - "homeUrl": "more_url", + "downloadUrl": "https://sonarsource.bintray.com/CommercialDistribution/editions/enterprise-edition-7.0.0.717.zip", + "homeUrl": "https://redirect.sonarsource.com/editions/enterprise.html", "key": "enterprise", "name": "Enterprise Edition", } @@ -77,7 +82,8 @@ exports[`should display the enterprise and datacenter edition boxes 1`] = ` currentEdition="developer" edition={ Object { - "homeUrl": "more_url", + "downloadUrl": "https://sonarsource.bintray.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip", + "homeUrl": "https://redirect.sonarsource.com/editions/datacenter.html", "key": "datacenter", "name": "Data Center Edition", } 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 55309126c7e..699d99a114c 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,12 +18,12 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { Edition, getEditionUrl } from '../utils'; +import { Edition, getEditionUrl, EditionKey } from '../utils'; import DocInclude from '../../../components/docs/DocInclude'; import { translate } from '../../../helpers/l10n'; interface Props { - currentEdition: string; + currentEdition?: EditionKey; edition: Edition; ncloc?: number; serverId?: string; 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 f4398076daf..7deb14298dd 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 @@ -20,10 +20,11 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import EditionBox from '../EditionBox'; +import { EditionKey } from '../../utils'; const DEFAULT_EDITION = { - key: 'foo', - name: 'Foo', + key: EditionKey.developer, + name: 'Developer', downloadUrl: 'download_url', homeUrl: 'more_url' }; @@ -32,7 +33,7 @@ it('should display the edition', () => { expect( shallow(