]> source.dussan.org Git - sonarqube.git/commitdiff
Improve Edition handling
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Fri, 9 Aug 2019 08:23:50 +0000 (10:23 +0200)
committerSonarTech <sonartech@sonarsource.com>
Mon, 28 Oct 2019 19:21:09 +0000 (20:21 +0100)
23 files changed:
server/sonar-web/src/main/js/api/system.ts
server/sonar-web/src/main/js/app/components/GlobalFooter.tsx
server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx
server/sonar-web/src/main/js/app/components/StartupModal.tsx
server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
server/sonar-web/src/main/js/app/types.d.ts
server/sonar-web/src/main/js/apps/marketplace/App.tsx
server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx
server/sonar-web/src/main/js/apps/marketplace/Header.tsx
server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx
server/sonar-web/src/main/js/apps/marketplace/__tests__/Header-test.tsx
server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/utils.ts
server/sonar-web/src/main/js/apps/system/__tests__/utils-test.ts
server/sonar-web/src/main/js/apps/system/utils.ts
server/sonar-web/src/main/js/helpers/editions.ts [new file with mode: 0644]
server/sonar-web/src/main/js/types/editions.ts [new file with mode: 0644]
server/sonar-web/src/main/js/types/system.ts [new file with mode: 0644]

index 57e3210f1656e6d08090afcb2ae1a907c370621c..3936416d8053c379dfa64687c413d5726efb46db 100644 (file)
@@ -19,6 +19,7 @@
  */
 import { getJSON, post, postJSON, requestTryAndRepeatUntil } from 'sonar-ui-common/helpers/request';
 import throwGlobalError from '../app/utils/throwGlobalError';
+import { SystemUpgrade } from '../types/system';
 
 export function setLogLevel(level: string): Promise<void | Response> {
   return post('/api/system/change_log_level', { level }).catch(throwGlobalError);
@@ -33,7 +34,7 @@ export function getSystemStatus(): Promise<{ id: string; version: string; status
 }
 
 export function getSystemUpgrades(): Promise<{
-  upgrades: T.SystemUpgrade[];
+  upgrades: SystemUpgrade[];
   updateCenterRefresh: string;
 }> {
   return getJSON('/api/system/upgrades');
index 49b5609512a8a356773e8eaa3bef26020b32dfa0..5ffecb7d0ce443c73884047daf8a8ca5bbdc4c16 100644 (file)
@@ -21,16 +21,17 @@ import * as React from 'react';
 import { Link } from 'react-router';
 import { Alert } from 'sonar-ui-common/components/ui/Alert';
 import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { EDITIONS } from '../../apps/marketplace/utils';
 import InstanceMessage from '../../components/common/InstanceMessage';
+import { getEdition } from '../../helpers/editions';
 import { isSonarCloud } from '../../helpers/system';
+import { EditionKey } from '../../types/editions';
 import GlobalFooterBranding from './GlobalFooterBranding';
 import GlobalFooterSonarCloud from './GlobalFooterSonarCloud';
 
 interface Props {
   hideLoggedInInfo?: boolean;
   productionDatabase: boolean;
-  sonarqubeEdition?: T.EditionKey;
+  sonarqubeEdition?: EditionKey;
   sonarqubeVersion?: string;
 }
 
@@ -44,7 +45,7 @@ export default function GlobalFooter({
     return <GlobalFooterSonarCloud />;
   }
 
-  const currentEdition = EDITIONS.find(edition => edition.key === sonarqubeEdition);
+  const currentEdition = sonarqubeEdition && getEdition(sonarqubeEdition);
 
   return (
     <div className="page-footer page-container" id="footer">
index 5655cf12c9653b65c1d57c61dd9418e20631ca02..0f9e21eca3d148c359f2dd06bf4375b24a8c6583 100644 (file)
  */
 import { connect } from 'react-redux';
 import { getAppState, Store } from '../../store/rootReducer';
+import { EditionKey } from '../../types/editions';
 import GlobalFooter from './GlobalFooter';
 
 interface StateProps {
   productionDatabase: boolean;
-  sonarqubeEdition?: T.EditionKey;
+  sonarqubeEdition?: EditionKey;
   sonarqubeVersion?: string;
 }
 
 const mapStateToProps = (state: Store): StateProps => ({
   productionDatabase: getAppState(state).productionDatabase,
-  sonarqubeEdition: getAppState(state).edition,
+  sonarqubeEdition: getAppState(state).edition as EditionKey, // TODO: Fix once AppState is no longer ambiant.
   sonarqubeVersion: getAppState(state).version
 });
 
index d9e7969450717c17fccb54af929a57191c9fc8f8..a8386549ec74242421ff48cce6234392341c5753 100644 (file)
@@ -30,6 +30,7 @@ import { isSonarCloud } from '../../helpers/system';
 import { isLoggedIn } from '../../helpers/users';
 import { getAppState, getCurrentUser, Store } from '../../store/rootReducer';
 import { skipOnboarding } from '../../store/users';
+import { EditionKey } from '../../types/editions';
 import { OnboardingContext } from './OnboardingContext';
 
 const OnboardingModal = lazyLoad(() => import('../../apps/tutorials/onboarding/OnboardingModal'));
@@ -40,7 +41,7 @@ const LicensePromptModal = lazyLoad(
 
 interface StateProps {
   canAdmin?: boolean;
-  currentEdition?: T.EditionKey;
+  currentEdition?: EditionKey;
   currentUser: T.CurrentUser;
 }
 
@@ -163,7 +164,7 @@ export class StartupModal extends React.PureComponent<Props, State> {
 
 const mapStateToProps = (state: Store): StateProps => ({
   canAdmin: getAppState(state).canAdmin,
-  currentEdition: getAppState(state).edition,
+  currentEdition: getAppState(state).edition as EditionKey, // TODO: Fix once AppState is no longer ambiant.
   currentUser: getCurrentUser(state)
 });
 
index e3b0a95237a1ec573df8fb479d0bb411ce2482c9..fb80ac5a4632df9a4f04e74a573099f22bc1152e 100644 (file)
@@ -19,8 +19,8 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { EditionKey } from '../../../apps/marketplace/utils';
 import { isSonarCloud } from '../../../helpers/system';
+import { EditionKey } from '../../../types/editions';
 import GlobalFooter from '../GlobalFooter';
 
 jest.mock('../../../helpers/system', () => ({ isSonarCloud: jest.fn() }));
index f47630a194b105b8c2f7054246e946248b1b6469..7ac78c8872e709b64e76e2457f25470bc3933d75 100644 (file)
@@ -25,8 +25,8 @@ import { hasMessage } from 'sonar-ui-common/helpers/l10n';
 import { get, save } from 'sonar-ui-common/helpers/storage';
 import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
 import { showLicense } from '../../../api/marketplace';
-import { EditionKey } from '../../../apps/marketplace/utils';
 import { mockOrganization, mockRouter } from '../../../helpers/testMocks';
+import { EditionKey } from '../../../types/editions';
 import { ModalKey, StartupModal } from '../StartupModal';
 
 jest.mock('../../../api/marketplace', () => ({
index d9150485955ffee1302deb56f3128f272b2c6f61..a0b919a894e30c71f4e9cbf9f883bf6cc1cd2f5c 100644 (file)
@@ -97,7 +97,7 @@ declare namespace T {
     branchesEnabled?: boolean;
     canAdmin?: boolean;
     defaultOrganization: string;
-    edition: EditionKey | undefined;
+    edition: 'community' | 'developer' | 'enterprise' | 'datacenter' | undefined;
     globalPages?: Extension[];
     organizationsEnabled?: boolean;
     productionDatabase: boolean;
@@ -275,8 +275,6 @@ declare namespace T {
     subProjectName?: string;
   }
 
-  export type EditionKey = 'community' | 'developer' | 'enterprise' | 'datacenter';
-
   export type ExpandDirection = 'up' | 'down';
 
   export interface Extension {
@@ -987,14 +985,6 @@ declare namespace T {
     | 'DB_MIGRATION_NEEDED'
     | 'DB_MIGRATION_RUNNING';
 
-  export interface SystemUpgrade {
-    version: string;
-    description: string;
-    releaseDate: string;
-    changeLogUrl: string;
-    downloadUrl: string;
-  }
-
   export interface Task {
     analysisId?: string;
     branch?: string;
index a21f283ed6988c718c0093de3a7f3151eafcc313..b1cda108aba5dabfe2725eb443505ab0eef42c83 100644 (file)
@@ -31,6 +31,7 @@ import {
 } from '../../api/plugins';
 import Suggestions from '../../app/components/embed-docs-modal/Suggestions';
 import { Location, Router, withRouter } from '../../components/hoc/withRouter';
+import { EditionKey } from '../../types/editions';
 import EditionBoxes from './EditionBoxes';
 import Footer from './Footer';
 import Header from './Header';
@@ -40,7 +41,7 @@ import './style.css';
 import { filterPlugins, parseQuery, Query, serializeQuery } from './utils';
 
 export interface Props {
-  currentEdition?: T.EditionKey;
+  currentEdition?: EditionKey;
   fetchPendingPlugins: () => void;
   pendingPlugins: PluginPendingResult;
   location: Location;
index bf9d761eed933c9ba7836bb7dbc2094c8d76b5d1..607190d272ca3b2ea6d3dc17e0fb6ed279991af9 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { connect } from 'react-redux';
 import AdminContext from '../../app/components/AdminContext';
 import { getAppState, getGlobalSettingValue, Store } from '../../store/rootReducer';
+import { EditionKey } from '../../types/editions';
 import App from './App';
 
 interface OwnProps {
@@ -28,7 +29,7 @@ interface OwnProps {
 }
 
 interface StateToProps {
-  currentEdition?: T.EditionKey;
+  currentEdition?: EditionKey;
   standaloneMode?: boolean;
   updateCenterActive: boolean;
 }
@@ -36,7 +37,7 @@ interface StateToProps {
 const mapStateToProps = (state: Store) => {
   const updateCenterActive = getGlobalSettingValue(state, 'sonar.updatecenter.activate');
   return {
-    currentEdition: getAppState(state).edition,
+    currentEdition: getAppState(state).edition as EditionKey, // TODO: Fix once AppState is no longer ambiant.
     standaloneMode: getAppState(state).standalone,
     updateCenterActive: Boolean(updateCenterActive && updateCenterActive.value === 'true')
   };
index 0f0cf9c21a7b22e65fc63f2a45d1bb85fb5d3fb2..246ca05764853a3e0556b0b229900102e3ecd819 100644 (file)
  */
 import * as React from 'react';
 import { getMarketplaceNavigation } from '../../api/nav';
+import { getAllEditionsAbove } from '../../helpers/editions';
+import { EditionKey } from '../../types/editions';
 import EditionBox from './components/EditionBox';
-import { EDITIONS } from './utils';
 
 export interface Props {
-  currentEdition?: T.EditionKey;
+  currentEdition?: EditionKey;
 }
 
 interface State {
@@ -58,8 +59,7 @@ export default class EditionBoxes extends React.PureComponent<Props, State> {
   render() {
     const { currentEdition } = this.props;
     const { serverId, ncloc } = this.state;
-    const currentEditionIdx = EDITIONS.findIndex(edition => edition.key === currentEdition);
-    const visibleEditions = EDITIONS.slice(currentEditionIdx + 1);
+    const visibleEditions = getAllEditionsAbove(currentEdition);
 
     if (visibleEditions.length <= 0) {
       return null;
index 5eb40c31d9d789b625c5fd8b0ba1bea6220305b0..a457c29f0ffaae7747366cc47378c55c34025074 100644 (file)
  */
 import * as React from 'react';
 import { translate } from 'sonar-ui-common/helpers/l10n';
+import { EditionKey } from '../../types/editions';
 
 interface Props {
-  currentEdition?: T.EditionKey;
+  currentEdition?: EditionKey;
 }
 
 export default function Header({ currentEdition }: Props) {
index 5a3091c86701bc8549394177efa041b0ed0a1b81..f85440002e05b05313bc7ffbc3d2b4e7338c7af6 100644 (file)
@@ -19,8 +19,8 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { EditionKey } from '../../../types/editions';
 import EditionBoxes from '../EditionBoxes';
-import { EditionKey } from '../utils';
 
 it('should display the available edition boxes correctly', () => {
   expect(getWrapper()).toMatchSnapshot();
index 9c2a970045442fdfb369bebe4fd80b53ca7f0378..b1c39d099130503784d054d635e777019b345d1b 100644 (file)
@@ -19,8 +19,8 @@
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { EditionKey } from '../../../types/editions';
 import Header from '../Header';
-import { EditionKey } from '../utils';
 
 it('should render with installed editions', () => {
   expect(shallow(<Header currentEdition={EditionKey.community} />)).toMatchSnapshot();
index 251e57d80e27f80a1c331e02e5e4a72cfe8ee677..7ed8b99d9f6e6bf937b5cdf782ccdcc0f29a98cd 100644 (file)
@@ -8,7 +8,7 @@ exports[`should display the available edition boxes correctly 1`] = `
     currentEdition="community"
     edition={
       Object {
-        "downloadUrl": "https://binaries.sonarsource.com/CommercialDistribution/editions/developer-edition-7.0.0.717.zip",
+        "downloadProperty": "downloadDeveloperUrl",
         "homeUrl": "https://redirect.sonarsource.com/editions/developer.html",
         "key": "developer",
         "name": "Developer Edition",
@@ -20,7 +20,7 @@ exports[`should display the available edition boxes correctly 1`] = `
     currentEdition="community"
     edition={
       Object {
-        "downloadUrl": "https://binaries.sonarsource.com/CommercialDistribution/editions/enterprise-edition-7.0.0.717.zip",
+        "downloadProperty": "downloadEnterpriseUrl",
         "homeUrl": "https://redirect.sonarsource.com/editions/enterprise.html",
         "key": "enterprise",
         "name": "Enterprise Edition",
@@ -32,7 +32,7 @@ exports[`should display the available edition boxes correctly 1`] = `
     currentEdition="community"
     edition={
       Object {
-        "downloadUrl": "https://binaries.sonarsource.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip",
+        "downloadProperty": "downloadDatacenterUrl",
         "homeUrl": "https://redirect.sonarsource.com/editions/datacenter.html",
         "key": "datacenter",
         "name": "Data Center Edition",
@@ -51,7 +51,7 @@ exports[`should display the datacenter edition box only 1`] = `
     currentEdition="enterprise"
     edition={
       Object {
-        "downloadUrl": "https://binaries.sonarsource.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip",
+        "downloadProperty": "downloadDatacenterUrl",
         "homeUrl": "https://redirect.sonarsource.com/editions/datacenter.html",
         "key": "datacenter",
         "name": "Data Center Edition",
@@ -70,7 +70,7 @@ exports[`should display the enterprise and datacenter edition boxes 1`] = `
     currentEdition="developer"
     edition={
       Object {
-        "downloadUrl": "https://binaries.sonarsource.com/CommercialDistribution/editions/enterprise-edition-7.0.0.717.zip",
+        "downloadProperty": "downloadEnterpriseUrl",
         "homeUrl": "https://redirect.sonarsource.com/editions/enterprise.html",
         "key": "enterprise",
         "name": "Enterprise Edition",
@@ -82,7 +82,7 @@ exports[`should display the enterprise and datacenter edition boxes 1`] = `
     currentEdition="developer"
     edition={
       Object {
-        "downloadUrl": "https://binaries.sonarsource.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip",
+        "downloadProperty": "downloadDatacenterUrl",
         "homeUrl": "https://redirect.sonarsource.com/editions/datacenter.html",
         "key": "datacenter",
         "name": "Data Center Edition",
index a4cd935eca9425c271bcce271a95670ce2a37fc2..9efb90bb83c67aa93ca278e0e76f78b893f57a21 100644 (file)
@@ -23,12 +23,13 @@ import tooltipEE from 'Docs/tooltips/editions/enterprise.md';
 import * as React from 'react';
 import { lazyLoad } from 'sonar-ui-common/components/lazyLoad';
 import { translate } from 'sonar-ui-common/helpers/l10n';
-import { Edition, getEditionUrl } from '../utils';
+import { getEditionUrl } from '../../../helpers/editions';
+import { Edition, EditionKey } from '../../../types/editions';
 
 const DocMarkdownBlock = lazyLoad(() => import('../../../components/docs/DocMarkdownBlock'));
 
 interface Props {
-  currentEdition?: T.EditionKey;
+  currentEdition?: EditionKey;
   edition: Edition;
   ncloc?: number;
   serverId?: string;
index a774f10d0bee781a7955c8e2ae199ef832b4c455..c562e5d0cd61bc07315221657471e93a795072a9 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { EditionKey } from '../../utils';
+import { getEdition } from '../../../../helpers/editions';
+import { EditionKey } from '../../../../types/editions';
 import EditionBox from '../EditionBox';
 
-const DEFAULT_EDITION = {
-  key: EditionKey.developer,
-  name: 'Developer',
-  downloadUrl: 'download_url',
-  homeUrl: 'more_url'
-};
-
 it('should display the edition', () => {
   expect(
     shallow(
       <EditionBox
         currentEdition={EditionKey.community}
-        edition={DEFAULT_EDITION}
+        edition={getEdition(EditionKey.developer)}
         ncloc={1000}
         serverId="serverId"
       />
index a047f84b3b4a47878475769542dec7b63596564e..5d63a4a3ec2deff3d69dc384046504fce3e0d6d1 100644 (file)
@@ -9,7 +9,7 @@ exports[`should display the edition 1`] = `
     className="marketplace-edition-action spacer-top"
   >
     <a
-      href="more_url?ncloc=1000&serverId=serverId&sourceEdition=community"
+      href="https://redirect.sonarsource.com/editions/developer.html?ncloc=1000&serverId=serverId&sourceEdition=community"
       rel="noopener noreferrer"
       target="_blank"
     >
index f1f2f939fa22eb3a13ffe52f4a35fe5420e0c91e..325b0889787afbdc9a1f49a32310983d83a6dad8 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { memoize } from 'lodash';
-import { stringify } from 'querystring';
 import { cleanQuery, parseAsString, serializeString } from 'sonar-ui-common/helpers/query';
-import { omitNil } from 'sonar-ui-common/helpers/request';
 import { Plugin, PluginAvailable, PluginInstalled, PluginPending } from '../../api/plugins';
 
-export enum EditionKey {
-  community = 'community',
-  developer = 'developer',
-  enterprise = 'enterprise',
-  datacenter = 'datacenter'
-}
-
-export interface Edition {
-  downloadUrl?: string;
-  homeUrl: string;
-  key: EditionKey;
-  name: string;
-}
-
 export interface Query {
   filter: string;
   search?: string;
 }
 
-export const EDITIONS: Edition[] = [
-  {
-    key: EditionKey.community,
-    name: 'Community Edition',
-    homeUrl: 'https://redirect.sonarsource.com/editions/community.html'
-  },
-  {
-    key: EditionKey.developer,
-    name: 'Developer Edition',
-    homeUrl: 'https://redirect.sonarsource.com/editions/developer.html',
-    downloadUrl:
-      'https://binaries.sonarsource.com/CommercialDistribution/editions/developer-edition-7.0.0.717.zip'
-  },
-  {
-    key: EditionKey.enterprise,
-    name: 'Enterprise Edition',
-    homeUrl: 'https://redirect.sonarsource.com/editions/enterprise.html',
-    downloadUrl:
-      'https://binaries.sonarsource.com/CommercialDistribution/editions/enterprise-edition-7.0.0.717.zip'
-  },
-  {
-    key: EditionKey.datacenter,
-    name: 'Data Center Edition',
-    homeUrl: 'https://redirect.sonarsource.com/editions/datacenter.html',
-    downloadUrl:
-      'https://binaries.sonarsource.com/CommercialDistribution/editions/datacenter-edition-7.0.0.717.zip'
-  }
-];
-
-export function getEditionUrl(
-  edition: Edition,
-  data: { serverId?: string; ncloc?: number; sourceEdition?: T.EditionKey }
-) {
-  let url = edition.homeUrl;
-  const query = stringify(omitNil(data));
-  if (query) {
-    url += '?' + query;
-  }
-  return url;
-}
-
 const EXCLUDED_PLUGINS = ['license'];
 export function filterPlugins(plugins: Plugin[], search?: string): Plugin[] {
   if (!search) {
index b90fab875f1505b39330c49960f3681b48fc7aa4..f760288d2157f8f0c33213f41b62151e17b90df1 100644 (file)
@@ -19,6 +19,7 @@
  */
 /* eslint-disable sonarjs/no-duplicate-string */
 import { mockClusterSysInfo, mockStandaloneSysInfo } from '../../../helpers/testMocks';
+import { SystemUpgrade } from '../../../types/system';
 import * as u from '../utils';
 
 describe('parseQuery', () => {
@@ -80,7 +81,7 @@ describe('sortUpgrades', () => {
         { version: '5.10' },
         { version: '5.1' },
         { version: '5.4' }
-      ] as T.SystemUpgrade[])
+      ] as SystemUpgrade[])
     ).toEqual([{ version: '5.10' }, { version: '5.4.2' }, { version: '5.4' }, { version: '5.1' }]);
     expect(
       u.sortUpgrades([
@@ -88,7 +89,7 @@ describe('sortUpgrades', () => {
         { version: '5.1.2' },
         { version: '6.0' },
         { version: '6.9' }
-      ] as T.SystemUpgrade[])
+      ] as SystemUpgrade[])
     ).toEqual([{ version: '6.9' }, { version: '6.0' }, { version: '5.10' }, { version: '5.1.2' }]);
   });
 });
@@ -101,7 +102,7 @@ describe('groupUpgrades', () => {
         { version: '5.4.2' },
         { version: '5.4' },
         { version: '5.1' }
-      ] as T.SystemUpgrade[])
+      ] as SystemUpgrade[])
     ).toEqual([
       [{ version: '5.10' }, { version: '5.4.2' }, { version: '5.4' }, { version: '5.1' }]
     ]);
@@ -112,7 +113,7 @@ describe('groupUpgrades', () => {
         { version: '6.0' },
         { version: '5.10' },
         { version: '5.4.2' }
-      ] as T.SystemUpgrade[])
+      ] as SystemUpgrade[])
     ).toEqual([
       [{ version: '6.9' }, { version: '6.7' }, { version: '6.0' }],
       [{ version: '5.10' }, { version: '5.4.2' }]
index 9b75c19d451ad9784eb8dd6da3bd3895eaf6b400..2df36614aaed00a653caa53d665f0be6f9e32439 100644 (file)
@@ -25,6 +25,7 @@ import {
   parseAsString,
   serializeStringArray
 } from 'sonar-ui-common/helpers/query';
+import { SystemUpgrade } from '../../types/system';
 
 export interface Query {
   expandedCards: string[];
@@ -230,15 +231,15 @@ export const serializeQuery = memoize(
     })
 );
 
-export function sortUpgrades(upgrades: T.SystemUpgrade[]): T.SystemUpgrade[] {
+export function sortUpgrades(upgrades: SystemUpgrade[]): SystemUpgrade[] {
   return sortBy(upgrades, [
-    (upgrade: T.SystemUpgrade) => -Number(upgrade.version.split('.')[0]),
-    (upgrade: T.SystemUpgrade) => -Number(upgrade.version.split('.')[1] || 0),
-    (upgrade: T.SystemUpgrade) => -Number(upgrade.version.split('.')[2] || 0)
+    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[0]),
+    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[1] || 0),
+    (upgrade: SystemUpgrade) => -Number(upgrade.version.split('.')[2] || 0)
   ]);
 }
 
-export function groupUpgrades(upgrades: T.SystemUpgrade[]): T.SystemUpgrade[][] {
+export function groupUpgrades(upgrades: SystemUpgrade[]): SystemUpgrade[][] {
   const groupedVersions = groupBy(upgrades, upgrade => upgrade.version.split('.')[0]);
   const sortedMajor = sortBy(Object.keys(groupedVersions), key => -Number(key));
   return sortedMajor.map(key => groupedVersions[key]);
diff --git a/server/sonar-web/src/main/js/helpers/editions.ts b/server/sonar-web/src/main/js/helpers/editions.ts
new file mode 100644 (file)
index 0000000..8e1de90
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { omitNil } from 'sonar-ui-common/helpers/request';
+import { Edition, EditionKey } from '../types/editions';
+import { SystemUpgrade } from '../types/system';
+
+const EDITIONS: { [x in EditionKey]: Edition } = {
+  community: {
+    key: EditionKey.community,
+    name: 'Community Edition',
+    homeUrl: 'https://redirect.sonarsource.com/editions/community.html',
+    downloadProperty: 'downloadUrl'
+  },
+  developer: {
+    key: EditionKey.developer,
+    name: 'Developer Edition',
+    homeUrl: 'https://redirect.sonarsource.com/editions/developer.html',
+    downloadProperty: 'downloadDeveloperUrl'
+  },
+  enterprise: {
+    key: EditionKey.enterprise,
+    name: 'Enterprise Edition',
+    homeUrl: 'https://redirect.sonarsource.com/editions/enterprise.html',
+    downloadProperty: 'downloadEnterpriseUrl'
+  },
+  datacenter: {
+    key: EditionKey.datacenter,
+    name: 'Data Center Edition',
+    homeUrl: 'https://redirect.sonarsource.com/editions/datacenter.html',
+    downloadProperty: 'downloadDatacenterUrl'
+  }
+};
+
+export function getEdition(editionKey: EditionKey) {
+  return EDITIONS[editionKey];
+}
+
+export function getAllEditionsAbove(currentEdition?: EditionKey) {
+  const editions = Object.values(EDITIONS);
+  const currentEditionIdx = editions.findIndex(edition => edition.key === currentEdition);
+  return editions.slice(currentEditionIdx + 1);
+}
+
+export function getEditionUrl(
+  edition: Edition,
+  data: { serverId?: string; ncloc?: number; sourceEdition?: EditionKey }
+) {
+  let url = edition.homeUrl;
+  const query = stringify(omitNil(data));
+  if (query) {
+    url += '?' + query;
+  }
+  return url;
+}
+
+export function getEditionDownloadUrl(edition: Edition, lastUpgrade: SystemUpgrade) {
+  return lastUpgrade[edition.downloadProperty] || lastUpgrade.downloadUrl;
+}
+
+export function getEditionDownloadFilename(url: string) {
+  return url.replace(/^.+\/(sonarqube-[\w\-.]+\.zip)$/, '$1');
+}
diff --git a/server/sonar-web/src/main/js/types/editions.ts b/server/sonar-web/src/main/js/types/editions.ts
new file mode 100644 (file)
index 0000000..fe0e94b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 { SystemUpgradeDownloadUrls } from './system';
+
+export enum EditionKey {
+  community = 'community',
+  developer = 'developer',
+  enterprise = 'enterprise',
+  datacenter = 'datacenter'
+}
+
+export interface Edition {
+  downloadProperty: keyof SystemUpgradeDownloadUrls;
+  homeUrl: string;
+  key: EditionKey;
+  name: string;
+}
diff --git a/server/sonar-web/src/main/js/types/system.ts b/server/sonar-web/src/main/js/types/system.ts
new file mode 100644 (file)
index 0000000..79a97eb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.
+ */
+export interface SystemUpgradeDownloadUrls {
+  downloadDatacenterUrl?: string;
+  downloadDeveloperUrl?: string;
+  downloadEnterpriseUrl?: string;
+  downloadUrl: string;
+}
+
+export interface SystemUpgrade extends SystemUpgradeDownloadUrls {
+  changeLogUrl?: string;
+  description?: string;
+  releaseDate?: string;
+  version: string;
+}