From: Grégoire Aubert Date: Mon, 16 Oct 2017 08:01:37 +0000 (+0200) Subject: SONAR-9936 Add Editions pack in the marketplace X-Git-Tag: 6.7-RC1~105 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=48fbc92a514fe94ad1ac435ddc033bd2379a5a69;p=sonarqube.git SONAR-9936 Add Editions pack in the marketplace --- diff --git a/server/sonar-web/src/main/js/api/marketplace.ts b/server/sonar-web/src/main/js/api/marketplace.ts new file mode 100644 index 00000000000..282be5bc7b3 --- /dev/null +++ b/server/sonar-web/src/main/js/api/marketplace.ts @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { checkStatus, corsRequest, getJSON, parseJSON } from '../helpers/request'; +import throwGlobalError from '../app/utils/throwGlobalError'; + +export interface Edition { + name: string; + desc: string; + more_link: string; + request_license_link: string; + download_link: string; +} + +export interface Editions { + [key: string]: Edition; +} + +export interface EditionStatus { + currentEditionKey?: string; + nextEditionKey?: string; + installationStatus: + | 'NONE' + | 'AUTOMATIC_IN_PROGRESS' + | 'MANUAL_IN_PROGRESS' + | 'AUTOMATIC_READY' + | 'AUTOMATIC_FAILURE'; +} + +export function getEditionStatus(): Promise { + return getJSON('/api/editions/status').catch(throwGlobalError); +} + +export function getEditionsList(): Promise { + // TODO Replace with real url + const url = + 'https://gist.githubusercontent.com/gregaubert/e34535494f8a94bec7cbc4d750ae7d06/raw/ba8670a28d4bc6fbac18f92e450ec42029cc5dcb/editions.json'; + return corsRequest(url) + .submit() + .then(checkStatus) + .then(parseJSON); +} diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.js b/server/sonar-web/src/main/js/app/components/AdminContainer.js deleted file mode 100644 index 0794e8f79cd..00000000000 --- a/server/sonar-web/src/main/js/app/components/AdminContainer.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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 React from 'react'; -import Helmet from 'react-helmet'; -import { connect } from 'react-redux'; -import SettingsNav from './nav/settings/SettingsNav'; -import { getAppState } from '../../store/rootReducer'; -import { onFail } from '../../store/rootActions'; -import { getSettingsNavigation } from '../../api/nav'; -import { setAdminPages } from '../../store/appState/duck'; -import { translate } from '../../helpers/l10n'; - -class AdminContainer extends React.PureComponent { - componentDidMount() { - if (!this.props.appState.canAdmin) { - // workaround cyclic dependencies - const handleRequiredAuthorization = require('../utils/handleRequiredAuthorization').default; - handleRequiredAuthorization(); - } - this.loadData(); - } - - loadData() { - getSettingsNavigation().then( - r => this.props.setAdminPages(r.extensions), - onFail(this.props.dispatch) - ); - } - - render() { - const { adminPages } = this.props.appState; - - // Check that the adminPages are loaded - if (!adminPages) { - return null; - } - - const defaultTitle = translate('layout.settings'); - - return ( -
- - - {this.props.children} -
- ); - } -} - -const mapStateToProps = state => ({ - appState: getAppState(state) -}); - -const mapDispatchToProps = { setAdminPages }; - -export default connect(mapStateToProps, mapDispatchToProps)(AdminContainer); diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx new file mode 100644 index 00000000000..fec085b91f2 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx @@ -0,0 +1,99 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 * as PropTypes from 'prop-types'; +import Helmet from 'react-helmet'; +import { connect } from 'react-redux'; +import SettingsNav from './nav/settings/SettingsNav'; +import { getAppState } from '../../store/rootReducer'; +import { getSettingsNavigation } from '../../api/nav'; +import { EditionStatus, getEditionStatus } from '../../api/marketplace'; +import { setAdminPages, setEditionStatus } from '../../store/appState/duck'; +import { translate } from '../../helpers/l10n'; +import { Extension } from '../types'; + +interface Props { + appState: { + adminPages: Extension[]; + editionStatus?: EditionStatus; + organizationsEnabled: boolean; + }; + location: {}; + setAdminPages: (adminPages: Extension[]) => void; + setEditionStatus: (editionStatus: EditionStatus) => void; +} + +class AdminContainer extends React.PureComponent { + static contextTypes = { + canAdmin: PropTypes.bool.isRequired + }; + + componentDidMount() { + if (!this.context.canAdmin) { + // workaround cyclic dependencies + const handleRequiredAuthorization = require('../utils/handleRequiredAuthorization').default; + handleRequiredAuthorization(); + } else { + this.loadData(); + } + } + + loadData() { + Promise.all([getSettingsNavigation(), getEditionStatus()]).then( + ([r, editionStatus]) => { + this.props.setAdminPages(r.extensions); + this.props.setEditionStatus(editionStatus); + }, + () => {} + ); + } + + render() { + const { adminPages, editionStatus, organizationsEnabled } = this.props.appState; + + // Check that the adminPages are loaded + if (!adminPages) { + return null; + } + + const defaultTitle = translate('layout.settings'); + + return ( +
+ + + {this.props.children} +
+ ); + } +} + +const mapStateToProps = (state: any) => ({ + appState: getAppState(state) +}); + +const mapDispatchToProps = { setAdminPages, setEditionStatus }; + +export default connect(mapStateToProps, mapDispatchToProps)(AdminContainer as any); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx index ac54d018716..5f770db9ed4 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import { Link } from 'react-router'; import * as classNames from 'classnames'; import * as PropTypes from 'prop-types'; -import { Branch, Component, ComponentExtension } from '../../../types'; +import { Branch, Component, Extension } from '../../../types'; import NavBarTabs from '../../../../components/nav/NavBarTabs'; import { isShortLivingBranch, @@ -419,7 +419,7 @@ export default class ComponentNavMenu extends React.PureComponent { ); } - renderExtension = ({ key, name }: ComponentExtension, isAdmin: boolean) => { + renderExtension = ({ key, name }: Extension, isAdmin: boolean) => { const pathname = isAdmin ? `/project/admin/extension/${key}` : `/project/extension/${key}`; return (
  • 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 new file mode 100644 index 00000000000..5df886e38b2 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 NavBarNotif from '../../../../components/nav/NavBarNotif'; +import { EditionStatus } from '../../../../api/marketplace'; +import { translate } from '../../../../helpers/l10n'; + +interface Props { + editionStatus: EditionStatus; +} + +export default class SettingsEditionsNotif extends React.PureComponent { + render() { + const { editionStatus } = this.props; + + if (editionStatus.installationStatus === 'AUTOMATIC_IN_PROGRESS') { + return ( + + + {translate('marketplace.status.AUTOMATIC_IN_PROGRESS')} + + ); + } else if (editionStatus.installationStatus === 'AUTOMATIC_READY') { + return ( + + {translate('marketplace.status.AUTOMATIC_READY')} + + ); + } else if ( + ['MANUAL_IN_PROGRESS', 'AUTOMATIC_FAILURE'].includes(editionStatus.installationStatus) + ) { + return ( + + {translate('marketplace.status', editionStatus.installationStatus)} + + {translate('marketplace.how_to_install')} + + + ); + } + return null; + } +} diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js deleted file mode 100644 index e307778eec4..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js +++ /dev/null @@ -1,220 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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 React from 'react'; -import classNames from 'classnames'; -import { IndexLink, Link } from 'react-router'; -import { connect } from 'react-redux'; -import ContextNavBar from '../../../../components/nav/ContextNavBar'; -import NavBarTabs from '../../../../components/nav/NavBarTabs'; -import { translate } from '../../../../helpers/l10n'; -import { areThereCustomOrganizations } from '../../../../store/rootReducer'; - -class SettingsNav extends React.PureComponent { - static defaultProps = { - extensions: [] - }; - - isSomethingActive(urls) { - const path = window.location.pathname; - return urls.some(url => path.indexOf(window.baseUrl + url) === 0); - } - - isSecurityActive() { - const urls = [ - '/admin/users', - '/admin/groups', - '/admin/permissions', - '/admin/permission_templates' - ]; - return this.isSomethingActive(urls); - } - - isProjectsActive() { - const urls = ['/admin/projects_management', '/admin/background_tasks']; - return this.isSomethingActive(urls); - } - - isSystemActive() { - const urls = ['/admin/update_center', '/admin/system']; - return this.isSomethingActive(urls); - } - - renderExtension = ({ key, name }) => { - return ( -
  • - - {name} - -
  • - ); - }; - - render() { - const isSecurity = this.isSecurityActive(); - const isProjects = this.isProjectsActive(); - const isSystem = this.isSystemActive(); - const isSupport = this.isSomethingActive(['/admin/extension/license/support']); - - const securityClassName = classNames('dropdown-toggle', { active: isSecurity }); - const projectsClassName = classNames('dropdown-toggle', { active: isProjects }); - const systemClassName = classNames('dropdown-toggle', { active: isSystem }); - const configurationClassNames = classNames('dropdown-toggle', { - active: !isSecurity && !isProjects && !isSystem && !isSupport - }); - - const extensionsWithoutSupport = this.props.extensions.filter( - extension => extension.key !== 'license/support' - ); - - const hasSupportExtension = extensionsWithoutSupport.length < this.props.extensions.length; - - return ( - -

    - {translate('layout.settings')} -

    - - -
  • - - {translate('sidebar.project_settings')} - -
      -
    • - - {translate('settings.page')} - -
    • -
    • - - {translate('property.category.security.encryption')} - -
    • -
    • - - {translate('custom_metrics.page')} - -
    • - {extensionsWithoutSupport.map(this.renderExtension)} -
    -
  • - -
  • - - {translate('sidebar.security')} - -
      -
    • - - {translate('users.page')} - -
    • - {!this.props.customOrganizations && ( -
    • - - {translate('user_groups.page')} - -
    • - )} - {!this.props.customOrganizations && ( -
    • - - {translate('global_permissions.page')} - -
    • - )} - {!this.props.customOrganizations && ( -
    • - - {translate('permission_templates')} - -
    • - )} -
    -
  • - -
  • - - {translate('sidebar.projects')} - -
      - {!this.props.customOrganizations && ( -
    • - - {translate('management')} - -
    • - )} -
    • - - {translate('background_tasks.page')} - -
    • -
    -
  • - -
  • - - {translate('sidebar.system')} - -
      -
    • - - {translate('update_center.page')} - -
    • -
    • - - {translate('system_info.page')} - -
    • -
    -
  • - -
  • - - {translate('marketplace.page')} - -
  • - - {hasSupportExtension && ( -
  • - - {translate('support')} - -
  • - )} -
    -
    - ); - } -} - -const mapStateToProps = state => ({ - customOrganizations: areThereCustomOrganizations(state) -}); - -export default connect(mapStateToProps)(SettingsNav); - -export const UnconnectedSettingsNav = SettingsNav; 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 new file mode 100644 index 00000000000..b677146a392 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx @@ -0,0 +1,228 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 * as classNames from 'classnames'; +import { IndexLink, Link } from 'react-router'; +import ContextNavBar from '../../../../components/nav/ContextNavBar'; +import SettingsEditionsNotif from './SettingsEditionsNotif'; +import NavBarTabs from '../../../../components/nav/NavBarTabs'; +import { EditionStatus } from '../../../../api/marketplace'; +import { Extension } from '../../../types'; +import { translate } from '../../../../helpers/l10n'; + +interface Props { + editionStatus?: EditionStatus; + extensions: Extension[]; + customOrganizations: boolean; + location: {}; +} + +export default class SettingsNav extends React.PureComponent { + static defaultProps = { + extensions: [] + }; + + isSomethingActive(urls: string[]): boolean { + const path = window.location.pathname; + return urls.some((url: string) => path.indexOf((window as any).baseUrl + url) === 0); + } + + isSecurityActive() { + const urls = [ + '/admin/users', + '/admin/groups', + '/admin/permissions', + '/admin/permission_templates' + ]; + return this.isSomethingActive(urls); + } + + isProjectsActive() { + const urls = ['/admin/projects_management', '/admin/background_tasks']; + return this.isSomethingActive(urls); + } + + isSystemActive() { + const urls = ['/admin/update_center', '/admin/system']; + return this.isSomethingActive(urls); + } + + renderExtension = ({ key, name }: Extension) => { + return ( +
  • + + {name} + +
  • + ); + }; + + render() { + const { customOrganizations, editionStatus, extensions } = this.props; + const isSecurity = this.isSecurityActive(); + const isProjects = this.isProjectsActive(); + const isSystem = this.isSystemActive(); + const isSupport = this.isSomethingActive(['/admin/extension/license/support']); + + const securityClassName = classNames('dropdown-toggle', { active: isSecurity }); + const projectsClassName = classNames('dropdown-toggle', { active: isProjects }); + const systemClassName = classNames('dropdown-toggle', { active: isSystem }); + const configurationClassNames = classNames('dropdown-toggle', { + active: !isSecurity && !isProjects && !isSystem && !isSupport + }); + + const extensionsWithoutSupport = extensions.filter( + extension => extension.key !== 'license/support' + ); + + const hasSupportExtension = extensionsWithoutSupport.length < extensions.length; + + let notifComponent; + if (editionStatus && editionStatus.installationStatus !== 'NONE') { + notifComponent = ; + } + return ( + +

    + {translate('layout.settings')} +

    + + +
  • + + {translate('sidebar.project_settings')} + +
      +
    • + + {translate('settings.page')} + +
    • +
    • + + {translate('property.category.security.encryption')} + +
    • +
    • + + {translate('custom_metrics.page')} + +
    • + {extensionsWithoutSupport.map(this.renderExtension)} +
    +
  • + +
  • + + {translate('sidebar.security')} + +
      +
    • + + {translate('users.page')} + +
    • + {!customOrganizations && ( +
    • + + {translate('user_groups.page')} + +
    • + )} + {!customOrganizations && ( +
    • + + {translate('global_permissions.page')} + +
    • + )} + {!customOrganizations && ( +
    • + + {translate('permission_templates')} + +
    • + )} +
    +
  • + +
  • + + {translate('sidebar.projects')} + +
      + {!customOrganizations && ( +
    • + + {translate('management')} + +
    • + )} +
    • + + {translate('background_tasks.page')} + +
    • +
    +
  • + +
  • + + {translate('sidebar.system')} + +
      +
    • + + {translate('update_center.page')} + +
    • +
    • + + {translate('system_info.page')} + +
    • +
    +
  • + +
  • + + {translate('marketplace.page')} + +
  • + + {hasSupportExtension && ( +
  • + + {translate('support')} + +
  • + )} +
    +
    + ); + } +} 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 new file mode 100644 index 00000000000..55612c06b34 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsEditionsNotif-test.tsx @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 SettingsEditionsNotif from '../SettingsEditionsNotif'; + +it('should display an in progress notif', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); +}); + +it('should display an error notification', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); +}); + +it('should display a ready notification', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.js b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.js deleted file mode 100644 index 48f8539f415..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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 React from 'react'; -import { shallow } from 'enzyme'; -import { UnconnectedSettingsNav } from '../SettingsNav'; - -it('should work with extensions', () => { - const extensions = [{ key: 'foo', name: 'Foo' }]; - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); -}); 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 new file mode 100644 index 00000000000..f819af90c49 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/SettingsNav-test.tsx @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 SettingsNav from '../SettingsNav'; + +it('should work with extensions', () => { + const extensions = [{ key: 'foo', name: 'Foo' }]; + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); +}); + +it('should display an edition notification', () => { + const wrapper = shallow( + + ); + expect({ ...wrapper.find('ContextNavBar').props(), children: [] }).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 new file mode 100644 index 00000000000..030ff105e6a --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsEditionsNotif-test.tsx.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should display a ready notification 1`] = ` + + + marketplace.status.AUTOMATIC_READY + + +`; + +exports[`should display an error notification 1`] = ` + + marketplace.status.AUTOMATIC_FAILURE + + marketplace.how_to_install + + +`; + +exports[`should display an in progress notif 1`] = ` + + + + marketplace.status.AUTOMATIC_IN_PROGRESS + + +`; diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap deleted file mode 100644 index 62c113f24bb..00000000000 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap +++ /dev/null @@ -1,201 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should work with extensions 1`] = ` - -

    - - layout.settings - -

    - -
  • - - sidebar.project_settings - - - -
      -
    • - - settings.page - -
    • -
    • - - property.category.security.encryption - -
    • -
    • - - custom_metrics.page - -
    • -
    • - - Foo - -
    • -
    -
  • -
  • - - sidebar.security - - - -
      -
    • - - users.page - -
    • -
    • - - user_groups.page - -
    • -
    • - - global_permissions.page - -
    • -
    • - - permission_templates - -
    • -
    -
  • -
  • - - sidebar.projects - - - -
      -
    • - - management - -
    • -
    • - - background_tasks.page - -
    • -
    -
  • -
  • - - sidebar.system - - - -
      -
    • - - update_center.page - -
    • -
    • - - system_info.page - -
    • -
    -
  • -
  • - - marketplace.page - -
  • -
    -
    -`; 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 new file mode 100644 index 00000000000..9dd9e567752 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.tsx.snap @@ -0,0 +1,216 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should display an edition notification 1`] = ` +Object { + "children": Array [], + "height": 95, + "id": "context-navigation", + "notif": , +} +`; + +exports[`should work with extensions 1`] = ` + +

    + + layout.settings + +

    + +
  • + + sidebar.project_settings + + + +
      +
    • + + settings.page + +
    • +
    • + + property.category.security.encryption + +
    • +
    • + + custom_metrics.page + +
    • +
    • + + Foo + +
    • +
    +
  • +
  • + + sidebar.security + + + +
      +
    • + + users.page + +
    • +
    • + + user_groups.page + +
    • +
    • + + global_permissions.page + +
    • +
    • + + permission_templates + +
    • +
    +
  • +
  • + + sidebar.projects + + + +
      +
    • + + management + +
    • +
    • + + background_tasks.page + +
    • +
    +
  • +
  • + + sidebar.system + + + +
      +
    • + + update_center.page + +
    • +
    • + + system_info.page + +
    • +
    +
  • +
  • + + marketplace.page + +
  • +
    +
    +`; diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index 8bf75ae6e04..73239aeb430 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -57,7 +57,7 @@ export interface ShortLivingBranch { export type Branch = MainBranch | LongLivingBranch | ShortLivingBranch; -export interface ComponentExtension { +export interface Extension { key: string; name: string; } @@ -71,7 +71,7 @@ export interface Component { }>; configuration?: ComponentConfiguration; description?: string; - extensions?: ComponentExtension[]; + extensions?: Extension[]; isFavorite?: boolean; key: string; name: string; @@ -83,7 +83,7 @@ export interface Component { } interface ComponentConfiguration { - extensions?: ComponentExtension[]; + extensions?: Extension[]; showBackgroundTasks?: boolean; showLinks?: boolean; showManualMeasures?: boolean; 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 0cb9096adc3..6ee3d1bce8f 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -22,6 +22,7 @@ import * as PropTypes from 'prop-types'; import { sortBy, uniqBy } from 'lodash'; import Helmet from 'react-helmet'; import Header from './Header'; +import EditionBoxes from './EditionBoxes'; import Footer from './Footer'; import PendingActions from './PendingActions'; import PluginsList from './PluginsList'; @@ -34,11 +35,13 @@ import { Plugin, PluginPending } from '../../api/plugins'; +import { EditionStatus } from '../../api/marketplace'; import { RawQuery } from '../../helpers/query'; import { translate } from '../../helpers/l10n'; import { filterPlugins, parseQuery, Query, serializeQuery } from './utils'; export interface Props { + editionStatus?: EditionStatus; location: { pathname: string; query: RawQuery }; updateCenterActive: boolean; } @@ -109,7 +112,11 @@ export default class App extends React.PureComponent { }); } }, - () => {} + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } ); }; @@ -121,7 +128,11 @@ export default class App extends React.PureComponent { this.setState({ loading: false, plugins }); } }, - () => {} + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } ); }; @@ -154,6 +165,10 @@ export default class App extends React.PureComponent {
    + ({ + editionStatus: getAppState(state).editionStatus, updateCenterActive: (getGlobalSettingValue(state, 'sonar.updatecenter.activate') || {}).value }); diff --git a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx new file mode 100644 index 00000000000..5c74a1e8aa4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx @@ -0,0 +1,104 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 EditionBox from './components/EditionBox'; +import { Editions, EditionStatus, getEditionsList } from '../../api/marketplace'; +import { translate } from '../../helpers/l10n'; + +export interface Props { + editionStatus?: EditionStatus; + updateCenterActive: boolean; +} + +interface State { + editions: Editions; + editionsError: boolean; + loading: boolean; +} + +export default class EditionBoxes extends React.PureComponent { + mounted: boolean; + state: State = { editions: {}, editionsError: false, loading: true }; + + componentDidMount() { + this.mounted = true; + this.fetchEditions(); + } + + componentWillUnmount() { + this.mounted = false; + } + + fetchEditions = () => { + this.setState({ loading: true }); + getEditionsList().then( + editions => { + if (this.mounted) { + this.setState({ + loading: false, + editions, + editionsError: false + }); + } + }, + () => { + if (this.mounted) { + this.setState({ editionsError: true, loading: false }); + } + } + ); + }; + + render() { + const { editions, loading } = this.state; + if (loading) { + return null; + } + return ( +
    + {this.state.editionsError ? ( + + + SonarSource.com + + ) + }} + /> + + ) : ( + Object.keys(editions).map(key => ( + + )) + )} +
    + ); + } +} 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 new file mode 100644 index 00000000000..49ee1abdcde --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 EditionBoxes from '../EditionBoxes'; +import { EditionStatus } from '../../../api/marketplace'; + +const DEFAULT_STATUS: EditionStatus = { + currentEditionKey: 'foo', + nextEditionKey: '', + installationStatus: 'NONE' +}; + +it('should display the edition boxes', () => { + const wrapper = getWrapper(); + expect(wrapper).toMatchSnapshot(); + wrapper.setState({ + editions: { + foo: { + name: 'Foo', + desc: 'Foo desc', + download_link: 'download_url', + more_link: 'more_url', + request_license_link: 'license_url' + }, + bar: { + name: 'Bar', + desc: 'Bar desc', + download_link: 'download_url', + more_link: 'more_url', + request_license_link: 'license_url' + } + }, + loading: false + }); + expect(wrapper).toMatchSnapshot(); +}); + +it('should display an error message', () => { + const wrapper = getWrapper(); + wrapper.setState({ loading: false, editionsError: true }); + expect(wrapper).toMatchSnapshot(); +}); + +function getWrapper(props = {}) { + return shallow( + + ); +} diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx index 9d9d15c3bf8..4ebc2b4709a 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx @@ -36,14 +36,14 @@ it('should display pending actions', () => { expect(getWrapper()).toMatchSnapshot(); }); -it('should not display nothing', () => { +it('should not display anything', () => { expect(getWrapper({ pending: { installing: [], updating: [], removing: [] } })).toMatchSnapshot(); }); it('should open the restart form', () => { const wrapper = getWrapper(); click(wrapper.find('.js-restart')); - expect(wrapper.find('RestartForm')).toHaveLength(1); + expect(wrapper.find('RestartForm').exists()).toBeTruthy(); }); it('should cancel all pending and refresh them', async () => { 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 new file mode 100644 index 00000000000..7ee4e6b73e9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/EditionBoxes-test.tsx.snap @@ -0,0 +1,73 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should display an error message 1`] = ` +
    + + + SonarSource.com + , + } + } + /> + +
    +`; + +exports[`should display the edition boxes 1`] = `null`; + +exports[`should display the edition boxes 2`] = ` +
    + + +
    +`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap index 36604e80f98..3afbde17ba6 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap @@ -64,4 +64,4 @@ exports[`should display pending actions 1`] = `
    `; -exports[`should not display nothing 1`] = `null`; +exports[`should not display anything 1`] = `null`; 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 new file mode 100644 index 00000000000..3299504fdad --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { Edition, EditionStatus } from '../../../api/marketplace'; +import { translate } from '../../../helpers/l10n'; + +export interface Props { + edition: Edition; + editionKey: string; + editionStatus?: EditionStatus; +} + +export default class EditionBox extends React.PureComponent { + render() { + const { edition, editionKey, editionStatus } = this.props; + const isInstalled = editionStatus && editionStatus.currentEditionKey === editionKey; + const isInstalling = editionStatus && editionStatus.nextEditionKey === editionKey; + const installInProgress = + editionStatus && editionStatus.installationStatus === 'AUTOMATIC_IN_PROGRESS'; + return ( +
    + {isInstalled && + !isInstalling && ( + + + {translate('marketplace.installed')} + + )} + {isInstalling && ( + + {translate('marketplace.installing')} + + )} +
    +

    {edition.name}

    +

    {edition.desc}

    +
    +
    + + {translate('marketplace.learn_more')} + + {!isInstalled && ( + + )} + {isInstalled && ( + + )} +
    +
    + ); + } +} 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 new file mode 100644 index 00000000000..ebb42f27489 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBox-test.tsx @@ -0,0 +1,100 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { Edition, EditionStatus } from '../../../../api/marketplace'; +import EditionBox from '../EditionBox'; + +const DEFAULT_STATUS: EditionStatus = { + currentEditionKey: '', + nextEditionKey: '', + installationStatus: 'NONE' +}; + +const DEFAULT_EDITION: Edition = { + name: 'Foo', + desc: 'Foo desc', + download_link: 'download_url', + more_link: 'more_url', + request_license_link: 'license_url' +}; + +it('should display the edition', () => { + expect(getWrapper()).toMatchSnapshot(); +}); + +it('should display installed badge', () => { + expect( + getWrapper({ + editionStatus: { + currentEditionKey: 'foo', + nextEditionKey: '', + installationStatus: 'NONE' + } + }) + ).toMatchSnapshot(); +}); + +it('should display installing badge', () => { + expect( + getWrapper({ + editionStatus: { + currentEditionKey: 'foo', + nextEditionKey: 'foo', + installationStatus: 'NONE' + } + }) + ).toMatchSnapshot(); +}); + +it('should disable install button', () => { + expect( + getWrapper({ + editionStatus: { + currentEditionKey: 'foo', + nextEditionKey: '', + installationStatus: 'AUTOMATIC_IN_PROGRESS' + } + }) + ).toMatchSnapshot(); +}); + +it('should disable uninstall button', () => { + expect( + getWrapper({ + editionStatus: { + currentEditionKey: '', + nextEditionKey: 'foo', + installationStatus: 'AUTOMATIC_IN_PROGRESS' + } + }) + ).toMatchSnapshot(); +}); + +function getWrapper(props = {}) { + return shallow( + + ); +} 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 new file mode 100644 index 00000000000..6814875f5ba --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBox-test.tsx.snap @@ -0,0 +1,192 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should disable install button 1`] = ` +
    + + + marketplace.installed + +
    +

    + Foo +

    +

    + Foo desc +

    +
    +
    + + marketplace.learn_more + + +
    +
    +`; + +exports[`should disable uninstall button 1`] = ` +
    + + marketplace.installing + +
    +

    + Foo +

    +

    + Foo desc +

    +
    +
    + + marketplace.learn_more + + +
    +
    +`; + +exports[`should display installed badge 1`] = ` +
    + + + marketplace.installed + +
    +

    + Foo +

    +

    + Foo desc +

    +
    +
    + + marketplace.learn_more + + +
    +
    +`; + +exports[`should display installing badge 1`] = ` +
    + + marketplace.installing + +
    +

    + Foo +

    +

    + Foo desc +

    +
    +
    + + marketplace.learn_more + + +
    +
    +`; + +exports[`should display the edition 1`] = ` +
    +
    +

    + Foo +

    +

    + Foo desc +

    +
    +
    + + marketplace.learn_more + + +
    +
    +`; diff --git a/server/sonar-web/src/main/js/apps/marketplace/style.css b/server/sonar-web/src/main/js/apps/marketplace/style.css new file mode 100644 index 00000000000..037a0c133d9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/marketplace/style.css @@ -0,0 +1,32 @@ +.marketplace-editions { + display: flex; + flex-direction: row; + justify-content: space-between; + margin-left: -8px; + margin-right: -8px; +} + +.marketplace-edition { + position: relative; + flex: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + background-color: #f3f3f3; + margin-left: 8px; + margin-right: 8px; +} + +.marketplace-edition-badge { + position: absolute; + right: -1px; + top: 16px; + padding: 4px 8px; + border-radius: 2px 0 0 2px; +} + +.marketplace-edition-action { + display: flex; + align-items: baseline; + justify-content: space-between; +} diff --git a/server/sonar-web/src/main/js/helpers/request.ts b/server/sonar-web/src/main/js/helpers/request.ts index 1abef7a8399..6e955baeeac 100644 --- a/server/sonar-web/src/main/js/helpers/request.ts +++ b/server/sonar-web/src/main/js/helpers/request.ts @@ -78,11 +78,9 @@ class Request { constructor(private url: string, private options: { method?: string } = {}) {} - submit(): Promise { + getSubmitData(customHeaders: any = {}): { url: string; options: RequestInit } { let url = this.url; - const options: RequestInit = { ...DEFAULT_OPTIONS, ...this.options }; - const customHeaders: any = {}; if (this.data) { if (this.data instanceof FormData) { @@ -100,10 +98,13 @@ class Request { options.headers = { ...DEFAULT_HEADERS, - ...customHeaders, - ...getCSRFToken() + ...customHeaders }; + return { url, options }; + } + submit(): Promise { + const { url, options } = this.getSubmitData({ ...getCSRFToken() }); return window.fetch((window as any).baseUrl + url, options); } @@ -127,6 +128,19 @@ export function request(url: string): Request { return new Request(url); } +/** + * Make a cors request + */ +export function corsRequest(url: string, mode: RequestMode = 'cors'): Request { + const options: RequestInit = { mode }; + const request = new Request(url, options); + request.submit = function() { + const { url, options } = this.getSubmitData(); + return window.fetch(url, options); + }; + return request; +} + /** * Check that response status is ok */ diff --git a/server/sonar-web/src/main/js/store/appState/duck.ts b/server/sonar-web/src/main/js/store/appState/duck.ts index ed005f2888f..d783242272f 100644 --- a/server/sonar-web/src/main/js/store/appState/duck.ts +++ b/server/sonar-web/src/main/js/store/appState/duck.ts @@ -17,10 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +import { Extension } from '../../app/types'; +import { EditionStatus } from '../../api/marketplace'; + interface AppState { - adminPages?: any[]; + adminPages?: Extension[]; authenticationError: boolean; authorizationError: boolean; + editionStatus?: EditionStatus; organizationsEnabled: boolean; qualifiers?: string[]; } @@ -32,14 +37,23 @@ interface SetAppStateAction { interface SetAdminPagesAction { type: 'SET_ADMIN_PAGES'; - adminPages: any[]; + adminPages: Extension[]; +} + +interface SetEditionStatusAction { + type: 'SET_EDITION_STATUS'; + editionStatus: EditionStatus; } interface RequireAuthorizationAction { type: 'REQUIRE_AUTHORIZATION'; } -export type Action = SetAppStateAction | SetAdminPagesAction | RequireAuthorizationAction; +export type Action = + | SetAppStateAction + | SetAdminPagesAction + | SetEditionStatusAction + | RequireAuthorizationAction; export function setAppState(appState: AppState): SetAppStateAction { return { @@ -48,7 +62,7 @@ export function setAppState(appState: AppState): SetAppStateAction { }; } -export function setAdminPages(adminPages: any[]): SetAdminPagesAction { +export function setAdminPages(adminPages: Extension[]): SetAdminPagesAction { return { type: 'SET_ADMIN_PAGES', adminPages }; } @@ -56,6 +70,10 @@ export function requireAuthorization(): RequireAuthorizationAction { return { type: 'REQUIRE_AUTHORIZATION' }; } +export function setEditionStatus(editionStatus: EditionStatus): SetEditionStatusAction { + return { type: 'SET_EDITION_STATUS', editionStatus }; +} + const defaultValue: AppState = { authenticationError: false, authorizationError: false, @@ -71,6 +89,10 @@ export default function(state: AppState = defaultValue, action: Action): AppStat return { ...state, adminPages: action.adminPages }; } + if (action.type === 'SET_EDITION_STATUS') { + return { ...state, editionStatus: action.editionStatus }; + } + if (action.type === 'REQUIRE_AUTHORIZATION') { return { ...state, authorizationError: true }; } diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index ead056362e4..a91bb089669 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2069,6 +2069,7 @@ marketplace.revert=Revert marketplace.system_upgrades=System Upgrades marketplace.install=Install marketplace.installed=Installed +marketplace.installing=Installing... marketplace._installed=installed marketplace.available_under_commercial_license=Available under our commercial editions marketplace.learn_more=Learn more @@ -2089,6 +2090,12 @@ marketplace.update_to_x=Update to {0} marketplace.uninstall=Uninstall marketplace.i_accept_the=I accept the marketplace.terms_and_conditions=Terms and Conditions +marketplace.editions_unavailable=Explore our Editions: advanced feature packs brought to you by SonarSource on {url} +marketplace.status.AUTOMATIC_IN_PROGRESS=Updating your installation... Please wait... +marketplace.status.AUTOMATIC_READY=New installation complete. Please restart Server to benefit from it. +marketplace.status.MANUAL_IN_PROGRESS=Can't install Developer Edition because of internet access issue. Please manually install the package in your SonarQube's plugins folder. +marketplace.status.AUTOMATIC_FAILURE=Can't install Developer Edition. Please manually install the package in your SonarQube's plugins folder. +marketplace.how_to_install=How to install it? #------------------------------------------------------------------------------ #