]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9944 Update editions json format and use url from global settings
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Tue, 17 Oct 2017 12:41:51 +0000 (14:41 +0200)
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>
Mon, 23 Oct 2017 15:01:13 +0000 (08:01 -0700)
12 files changed:
server/sonar-web/src/main/js/api/marketplace.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/__tests__/EditionBoxes-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__/LicenseEditionForm-test.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionForm-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/utils.ts

index 17ef7d367a3445035bd5c5ab58b782bab0b4332b..4cff1587d94c2bef05287d4fc4052f5171840e66 100644 (file)
@@ -21,6 +21,7 @@ import { checkStatus, corsRequest, getJSON, parseJSON, postJSON } from '../helpe
 import throwGlobalError from '../app/utils/throwGlobalError';
 
 export interface Edition {
+  key: string;
   name: string;
   desc: string;
   more_link: string;
@@ -28,8 +29,8 @@ export interface Edition {
   download_link: string;
 }
 
-export interface Editions {
-  [key: string]: Edition;
+export interface EditionsPerVersion {
+  [version: string]: Edition[];
 }
 
 export interface EditionStatus {
@@ -47,10 +48,7 @@ export function getEditionStatus(): Promise<EditionStatus> {
   return getJSON('/api/editions/status').catch(throwGlobalError);
 }
 
-export function getEditionsList(): Promise<Editions> {
-  // TODO Replace with real url
-  const url =
-    'https://gist.githubusercontent.com/gregaubert/e34535494f8a94bec7cbc4d750ae7d06/raw/ba8670a28d4bc6fbac18f92e450ec42029cc5dcb/editions.json';
+export function getEditionsList(url: string): Promise<EditionsPerVersion> {
   return corsRequest(url)
     .submit()
     .then(checkStatus)
index 6ee3d1bce8f2b35b1d57b3c38fd150238ef81ba9..18fc8adaaeeddf709271419ebb8cd928c9f9c47c 100644 (file)
@@ -42,7 +42,9 @@ import { filterPlugins, parseQuery, Query, serializeQuery } from './utils';
 
 export interface Props {
   editionStatus?: EditionStatus;
+  editionsUrl: string;
   location: { pathname: string; query: RawQuery };
+  sonarqubeVersion: string;
   updateCenterActive: boolean;
 }
 
@@ -167,6 +169,8 @@ export default class App extends React.PureComponent<Props, State> {
         <Header />
         <EditionBoxes
           editionStatus={this.props.editionStatus}
+          editionsUrl={this.props.editionsUrl}
+          sonarqubeVersion={this.props.sonarqubeVersion}
           updateCenterActive={this.props.updateCenterActive}
         />
         <PendingActions refreshPending={this.fetchPendingPlugins} pending={pending} />
index 418219fd22a1d0e274ae3183f56d0927fda2d64f..64794ebbda247ae0f4ae64e6bee24b88ce8afbe1 100644 (file)
@@ -24,6 +24,8 @@ import './style.css';
 
 const mapStateToProps = (state: any) => ({
   editionStatus: getAppState(state).editionStatus,
+  editionsUrl: (getGlobalSettingValue(state, 'sonar.editions.jsonUrl') || {}).value,
+  sonarqubeVersion: getAppState(state).version,
   updateCenterActive: (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value
 });
 
index 64290f13e9e754919cd4bbd90e7fe1b7532eeddc..58ffe846c632dc2c0d7a3fb794085ca2cb70f143 100644 (file)
@@ -21,16 +21,19 @@ import * as React from 'react';
 import { FormattedMessage } from 'react-intl';
 import EditionBox from './components/EditionBox';
 import LicenseEditionForm from './components/LicenseEditionForm';
-import { Edition, Editions, EditionStatus, getEditionsList } from '../../api/marketplace';
+import { Edition, EditionStatus, getEditionsList } from '../../api/marketplace';
+import { getEditionsForVersion } from './utils';
 import { translate } from '../../helpers/l10n';
 
 export interface Props {
   editionStatus?: EditionStatus;
+  editionsUrl: string;
+  sonarqubeVersion: string;
   updateCenterActive: boolean;
 }
 
 interface State {
-  editions: Editions;
+  editions?: Edition[];
   editionsError: boolean;
   loading: boolean;
   installEdition?: Edition;
@@ -38,7 +41,7 @@ interface State {
 
 export default class EditionBoxes extends React.PureComponent<Props, State> {
   mounted: boolean;
-  state: State = { editions: {}, editionsError: false, loading: true };
+  state: State = { editionsError: false, loading: true };
 
   componentDidMount() {
     this.mounted = true;
@@ -51,12 +54,12 @@ export default class EditionBoxes extends React.PureComponent<Props, State> {
 
   fetchEditions = () => {
     this.setState({ loading: true });
-    getEditionsList().then(
-      editions => {
+    getEditionsList(this.props.editionsUrl).then(
+      editionsPerVersion => {
         if (this.mounted) {
           this.setState({
             loading: false,
-            editions,
+            editions: getEditionsForVersion(editionsPerVersion, this.props.sonarqubeVersion),
             editionsError: false
           });
         }
@@ -73,13 +76,13 @@ export default class EditionBoxes extends React.PureComponent<Props, State> {
   handleCloseLicenseForm = () => this.setState({ installEdition: undefined });
 
   render() {
-    const { editions, loading, installEdition } = this.state;
+    const { editions, editionsError, loading, installEdition } = this.state;
     if (loading) {
-      return null;
+      return <i className="big-spacer-bottom spinner" />;
     }
     return (
       <div className="spacer-bottom marketplace-editions">
-        {this.state.editionsError ? (
+        {!editions || editionsError ? (
           <span className="alert alert-info">
             <FormattedMessage
               defaultMessage={translate('marketplace.editions_unavailable')}
@@ -94,12 +97,11 @@ export default class EditionBoxes extends React.PureComponent<Props, State> {
             />
           </span>
         ) : (
-          Object.keys(editions).map(key => (
+          editions.map(edition => (
             <EditionBox
-              edition={editions[key]}
-              editionKey={key}
+              edition={edition}
               editionStatus={this.props.editionStatus}
-              key={key}
+              key={edition.key}
               onInstall={this.handleOpenLicenseForm}
             />
           ))
index ca32326f225ce8ba2fbae065233baa43e3f3637c..c3525b0e3044059d75220fbf7c5fc58c53d17836 100644 (file)
@@ -28,22 +28,24 @@ const DEFAULT_STATUS: EditionStatus = {
   installationStatus: 'NONE'
 };
 
-const DEFAULT_EDITIONS = {
-  foo: {
+const DEFAULT_EDITIONS = [
+  {
+    key: 'foo',
     name: 'Foo',
     desc: 'Foo desc',
     download_link: 'download_url',
     more_link: 'more_url',
     request_license_link: 'license_url'
   },
-  bar: {
+  {
+    key: 'bar',
     name: 'Bar',
     desc: 'Bar desc',
     download_link: 'download_url',
     more_link: 'more_url',
     request_license_link: 'license_url'
   }
-};
+];
 
 it('should display the edition boxes', () => {
   const wrapper = getWrapper();
@@ -67,12 +69,18 @@ it('should open the license form', () => {
     editions: DEFAULT_EDITIONS,
     loading: false
   });
-  (wrapper.instance() as EditionBoxes).handleOpenLicenseForm(DEFAULT_EDITIONS.foo);
+  (wrapper.instance() as EditionBoxes).handleOpenLicenseForm(DEFAULT_EDITIONS[0]);
   expect(wrapper.find('LicenseEditionForm').exists()).toBeTruthy();
 });
 
 function getWrapper(props = {}) {
   return shallow(
-    <EditionBoxes editionStatus={DEFAULT_STATUS} updateCenterActive={true} {...props} />
+    <EditionBoxes
+      editionStatus={DEFAULT_STATUS}
+      editionsUrl=""
+      sonarqubeVersion="6.7.5"
+      updateCenterActive={true}
+      {...props}
+    />
   );
 }
index cfc6695810a6c25eb759824ea112d71edb6bf1f6..f6b1475c0e6690aeac62cd916fa1d440eaab85ab 100644 (file)
@@ -25,7 +25,11 @@ exports[`should display an error message 1`] = `
 </div>
 `;
 
-exports[`should display the edition boxes 1`] = `null`;
+exports[`should display the edition boxes 1`] = `
+<i
+  className="big-spacer-bottom spinner"
+/>
+`;
 
 exports[`should display the edition boxes 2`] = `
 <div
@@ -36,12 +40,12 @@ exports[`should display the edition boxes 2`] = `
       Object {
         "desc": "Foo desc",
         "download_link": "download_url",
+        "key": "foo",
         "more_link": "more_url",
         "name": "Foo",
         "request_license_link": "license_url",
       }
     }
-    editionKey="foo"
     editionStatus={
       Object {
         "currentEditionKey": "foo",
@@ -56,12 +60,12 @@ exports[`should display the edition boxes 2`] = `
       Object {
         "desc": "Bar desc",
         "download_link": "download_url",
+        "key": "bar",
         "more_link": "more_url",
         "name": "Bar",
         "request_license_link": "license_url",
       }
     }
-    editionKey="bar"
     editionStatus={
       Object {
         "currentEditionKey": "foo",
index 43dcd7ea577777fa6a394901391544a2fcfbc078..147cb081c5ffc3dbf6c15482348e265aad53e812 100644 (file)
@@ -24,7 +24,6 @@ import { translate } from '../../../helpers/l10n';
 
 interface Props {
   edition: Edition;
-  editionKey: string;
   editionStatus?: EditionStatus;
   onInstall: (edition: Edition) => void;
 }
@@ -33,9 +32,9 @@ export default class EditionBox extends React.PureComponent<Props> {
   handleInstall = () => this.props.onInstall(this.props.edition);
 
   render() {
-    const { edition, editionKey, editionStatus } = this.props;
-    const isInstalled = editionStatus && editionStatus.currentEditionKey === editionKey;
-    const isInstalling = editionStatus && editionStatus.nextEditionKey === editionKey;
+    const { edition, editionStatus } = this.props;
+    const isInstalled = editionStatus && editionStatus.currentEditionKey === edition.key;
+    const isInstalling = editionStatus && editionStatus.nextEditionKey === edition.key;
     const installInProgress =
       editionStatus && editionStatus.installationStatus === 'AUTOMATIC_IN_PROGRESS';
     return (
index e0faa1bc3a3a53c2d7c0618a1332d2e50e15faf3..2b89b1bc1e2c23e84177d74e638edb305b25cf58 100644 (file)
@@ -29,6 +29,7 @@ const DEFAULT_STATUS: EditionStatus = {
 };
 
 const DEFAULT_EDITION: Edition = {
+  key: 'foo',
   name: 'Foo',
   desc: 'Foo desc',
   download_link: 'download_url',
@@ -92,7 +93,6 @@ function getWrapper(props = {}) {
   return shallow(
     <EditionBox
       edition={DEFAULT_EDITION}
-      editionKey="foo"
       editionStatus={DEFAULT_STATUS}
       onInstall={jest.fn()}
       {...props}
index 850d9afb4113113ef1cae57591ca03e9597d959a..7dff71932d5f044fcb88239f82557efc251c0c79 100644 (file)
@@ -36,6 +36,7 @@ const applyLicense = require('../../../../api/marketplace').applyLicense as jest
 const getStore = require('../../../../app/utils/getStore').default as jest.Mock<any>;
 
 const DEFAULT_EDITION = {
+  key: 'foo',
   name: 'Foo',
   desc: 'Foo desc',
   download_link: 'download_url',
index 49c97b43027772b665398e49cfe713bb1682d1e8..3e1d45ac3e01ce411b19f482cfc9c97a417353a8 100644 (file)
@@ -39,6 +39,7 @@ const getLicensePreview = require('../../../../api/marketplace').getLicensePrevi
 >;
 
 const DEFAULT_EDITION = {
+  key: 'foo',
   name: 'Foo',
   desc: 'Foo desc',
   download_link: 'download_url',
index 492f821f6300fd7c92a5744a923836bcc5bfb587..e6dde0fb6ede056b2d02a33188458cabf79093b4 100644 (file)
@@ -45,6 +45,7 @@ exports[`should display correctly 1`] = `
       Object {
         "desc": "Foo desc",
         "download_link": "download_url",
+        "key": "foo",
         "more_link": "more_url",
         "name": "Foo",
         "request_license_link": "license_url",
index 780fafb3af81126ba8404d7c03731bd63cfc009c..f4c8eea8d9264df30147225dadd24d753cdd65e8 100644 (file)
@@ -19,6 +19,7 @@
  */
 import { memoize } from 'lodash';
 import { Plugin, PluginAvailable, PluginInstalled, PluginPending } from '../../api/plugins';
+import { Edition, EditionsPerVersion } from '../../api/marketplace';
 import { cleanQuery, parseAsString, RawQuery, serializeString } from '../../helpers/query';
 
 export interface Query {
@@ -51,6 +52,19 @@ export function filterPlugins(plugins: Plugin[], search: string): Plugin[] {
   });
 }
 
+export function getEditionsForVersion(
+  editions: EditionsPerVersion,
+  version: string
+): Edition[] | undefined {
+  const matchResult = version.match(/\d+\.\d+/);
+  if (matchResult) {
+    if (editions[matchResult[0]]) {
+      return editions[matchResult[0]];
+    }
+  }
+  return undefined;
+}
+
 export const parseQuery = memoize((urlQuery: RawQuery): Query => ({
   filter: parseAsString(urlQuery['filter']) || DEFAULT_FILTER,
   search: parseAsString(urlQuery['search'])