From b66618cf03868a56c3fee751a82c1de13c674e79 Mon Sep 17 00:00:00 2001 From: Wouter Admiraal Date: Mon, 15 Jun 2020 13:06:06 +0200 Subject: [PATCH] SONAR-13386 Don't show 'plugin will get installed' if it's already installed --- server/sonar-web/src/main/js/api/plugins.ts | 74 +-- .../main/js/app/components/AdminContainer.tsx | 5 +- .../main/js/app/components/AdminContext.tsx | 4 +- .../settings/PendingPluginsActionNotif.tsx | 5 +- .../components/nav/settings/SettingsNav.tsx | 4 +- .../src/main/js/apps/marketplace/App.tsx | 7 +- .../main/js/apps/marketplace/PluginsList.tsx | 25 +- .../marketplace/components/PluginActions.tsx | 16 +- .../components/PluginAvailable.tsx | 18 +- .../components/PluginChangeLog.tsx | 2 +- .../components/PluginChangeLogButton.tsx | 2 +- .../components/PluginChangeLogItem.tsx | 2 +- .../components/PluginDescription.tsx | 2 +- .../components/PluginInstalled.tsx | 4 +- .../components/PluginOrganization.tsx | 2 +- .../marketplace/components/PluginStatus.tsx | 2 +- .../components/PluginUpdateButton.tsx | 2 +- .../components/PluginUpdateItem.tsx | 2 +- .../marketplace/components/PluginUpdates.tsx | 2 +- .../marketplace/components/PluginUrls.tsx | 2 +- .../__tests__/PluginActions-test.tsx | 6 +- .../__tests__/PluginAvailable-test.tsx | 64 ++ .../PluginAvailable-test.tsx.snap | 546 ++++++++++++++++++ .../src/main/js/apps/marketplace/utils.ts | 14 +- .../src/main/js/helpers/mocks/plugins.ts | 68 +++ server/sonar-web/src/main/js/types/plugins.ts | 84 +++ 26 files changed, 838 insertions(+), 126 deletions(-) create mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginAvailable-test.tsx create mode 100644 server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginAvailable-test.tsx.snap create mode 100644 server/sonar-web/src/main/js/helpers/mocks/plugins.ts create mode 100644 server/sonar-web/src/main/js/types/plugins.ts diff --git a/server/sonar-web/src/main/js/api/plugins.ts b/server/sonar-web/src/main/js/api/plugins.ts index 7a848e582ad..4e898155f52 100644 --- a/server/sonar-web/src/main/js/api/plugins.ts +++ b/server/sonar-web/src/main/js/api/plugins.ts @@ -21,68 +21,16 @@ import { findLastIndex } from 'lodash'; import { getJSON, post } from 'sonar-ui-common/helpers/request'; import { isDefined } from 'sonar-ui-common/helpers/types'; import throwGlobalError from '../app/utils/throwGlobalError'; - -export interface Plugin { - key: string; - name: string; - category?: string; - description?: string; - editionBundled?: boolean; - license?: string; - organizationName?: string; - homepageUrl?: string; - organizationUrl?: string; - issueTrackerUrl?: string; - termsAndConditionsUrl?: string; -} - -export interface Release { - version: string; - date: string; - description?: string; - changeLogUrl?: string; -} - -export interface Update { - status: string; - release?: Release; - requires: Plugin[]; - previousUpdates?: Update[]; -} - -export interface PluginPendingResult { - installing: PluginPending[]; - updating: PluginPending[]; - removing: PluginPending[]; -} - -export interface PluginAvailable extends Plugin { - release: Release; - update: Update; -} - -export interface PluginPending extends Plugin { - version: string; - implementationBuild: string; -} - -export interface PluginInstalled extends PluginPending { - documentationPath?: string; - filename: string; - hash: string; - sonarLintSupported: boolean; - updatedAt: number; - updates?: Update[]; -} +import { AvailablePlugin, InstalledPlugin, PendingPluginResult, Update } from '../types/plugins'; export function getAvailablePlugins(): Promise<{ - plugins: PluginAvailable[]; + plugins: AvailablePlugin[]; updateCenterRefresh: string; }> { return getJSON('/api/plugins/available').catch(throwGlobalError); } -export function getPendingPlugins(): Promise { +export function getPendingPlugins(): Promise { return getJSON('/api/plugins/pending').catch(throwGlobalError); } @@ -108,22 +56,22 @@ function addChangelog(update: Update, updates?: Update[]) { return { ...update, previousUpdates }; } -export function getInstalledPlugins(): Promise { +export function getInstalledPlugins(): Promise { return getJSON('/api/plugins/installed', { f: 'category' }).then( ({ plugins }) => plugins, throwGlobalError ); } -export function getInstalledPluginsWithUpdates(): Promise { +export function getInstalledPluginsWithUpdates(): Promise { return Promise.all([ getJSON('/api/plugins/installed', { f: 'category' }), getJSON('/api/plugins/updates') ]) .then(([installed, updates]) => - installed.plugins.map((plugin: PluginInstalled) => { - const updatePlugin: PluginInstalled = updates.plugins.find( - (p: PluginInstalled) => p.key === plugin.key + installed.plugins.map((plugin: InstalledPlugin) => { + const updatePlugin: InstalledPlugin = updates.plugins.find( + (p: InstalledPlugin) => p.key === plugin.key ); if (updatePlugin) { return { @@ -140,14 +88,14 @@ export function getInstalledPluginsWithUpdates(): Promise { .catch(throwGlobalError); } -export function getPluginUpdates(): Promise { +export function getPluginUpdates(): Promise { return Promise.all([getJSON('/api/plugins/updates'), getJSON('/api/plugins/installed')]) .then(([updates, installed]) => - updates.plugins.map((updatePlugin: PluginInstalled) => { + updates.plugins.map((updatePlugin: InstalledPlugin) => { const updates = getLastUpdates(updatePlugin.updates).map(update => addChangelog(update, updatePlugin.updates) ); - const plugin = installed.plugins.find((p: PluginInstalled) => p.key === updatePlugin.key); + const plugin = installed.plugins.find((p: InstalledPlugin) => p.key === updatePlugin.key); if (plugin) { return { ...plugin, diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx index 43c2465e9e2..232a9fa9722 100644 --- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx @@ -22,11 +22,12 @@ import { Helmet } from 'react-helmet-async'; import { connect } from 'react-redux'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { getSettingsNavigation } from '../../api/nav'; -import { getPendingPlugins, PluginPendingResult } from '../../api/plugins'; +import { getPendingPlugins } from '../../api/plugins'; import { getSystemStatus, waitSystemUPStatus } from '../../api/system'; import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization'; import { setAdminPages } from '../../store/appState'; import { getAppState, Store } from '../../store/rootReducer'; +import { PendingPluginResult } from '../../types/plugins'; import AdminContext, { defaultPendingPlugins, defaultSystemStatus } from './AdminContext'; import SettingsNav from './nav/settings/SettingsNav'; @@ -37,7 +38,7 @@ interface Props { } interface State { - pendingPlugins: PluginPendingResult; + pendingPlugins: PendingPluginResult; systemStatus: T.SysStatus; } diff --git a/server/sonar-web/src/main/js/app/components/AdminContext.tsx b/server/sonar-web/src/main/js/app/components/AdminContext.tsx index 5f41575bbc6..9b251813e27 100644 --- a/server/sonar-web/src/main/js/app/components/AdminContext.tsx +++ b/server/sonar-web/src/main/js/app/components/AdminContext.tsx @@ -18,12 +18,12 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { PluginPendingResult } from '../../api/plugins'; +import { PendingPluginResult } from '../../types/plugins'; export interface AdminContextInterface { fetchSystemStatus: () => void; fetchPendingPlugins: () => void; - pendingPlugins: PluginPendingResult; + pendingPlugins: PendingPluginResult; systemStatus: T.SysStatus; } diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/PendingPluginsActionNotif.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/PendingPluginsActionNotif.tsx index 21db1cfde35..8b57882fab4 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/PendingPluginsActionNotif.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/PendingPluginsActionNotif.tsx @@ -22,13 +22,14 @@ import { FormattedMessage } from 'react-intl'; import { Button } from 'sonar-ui-common/components/controls/buttons'; import { Alert } from 'sonar-ui-common/components/ui/Alert'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { cancelPendingPlugins, PluginPendingResult } from '../../../../api/plugins'; +import { cancelPendingPlugins } from '../../../../api/plugins'; import InstanceMessage from '../../../../components/common/InstanceMessage'; import RestartButton from '../../../../components/common/RestartButton'; +import { PendingPluginResult } from '../../../../types/plugins'; interface Props { fetchSystemStatus: () => void; - pending: PluginPendingResult; + pending: PendingPluginResult; refreshPending: () => void; systemStatus: T.SysStatus; } diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx index 854e32fda3d..5bbda188e0e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.tsx @@ -26,7 +26,7 @@ import ContextNavBar from 'sonar-ui-common/components/ui/ContextNavBar'; import NavBarTabs from 'sonar-ui-common/components/ui/NavBarTabs'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; -import { PluginPendingResult } from '../../../../api/plugins'; +import { PendingPluginResult } from '../../../../types/plugins'; import { rawSizes } from '../../../theme'; import PendingPluginsActionNotif from './PendingPluginsActionNotif'; import SystemRestartNotif from './SystemRestartNotif'; @@ -37,7 +37,7 @@ interface Props { fetchSystemStatus: () => void; location: {}; organizationsEnabled?: boolean; - pendingPlugins: PluginPendingResult; + pendingPlugins: PendingPluginResult; systemStatus: T.SysStatus; } 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 6f54f90f4c8..be0278ceb20 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/App.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/App.tsx @@ -25,13 +25,12 @@ import { getAvailablePlugins, getInstalledPlugins, getInstalledPluginsWithUpdates, - getPluginUpdates, - Plugin, - PluginPendingResult + getPluginUpdates } 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 { PendingPluginResult, Plugin } from '../../types/plugins'; import EditionBoxes from './EditionBoxes'; import Footer from './Footer'; import Header from './Header'; @@ -43,7 +42,7 @@ import { filterPlugins, parseQuery, Query, serializeQuery } from './utils'; interface Props { currentEdition?: EditionKey; fetchPendingPlugins: () => void; - pendingPlugins: PluginPendingResult; + pendingPlugins: PendingPluginResult; location: Location; router: Pick; standaloneMode?: boolean; diff --git a/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx b/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx index 8d864fcdf59..4c1933ac587 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/PluginsList.tsx @@ -18,17 +18,22 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { Plugin, PluginPending } from '../../api/plugins'; +import { + InstalledPlugin, + isAvailablePlugin, + isInstalledPlugin, + PendingPlugin, + Plugin +} from '../../types/plugins'; import PluginAvailable from './components/PluginAvailable'; import PluginInstalled from './components/PluginInstalled'; -import { isPluginAvailable, isPluginInstalled } from './utils'; interface Props { plugins: Plugin[]; pending: { - installing: PluginPending[]; - updating: PluginPending[]; - removing: PluginPending[]; + installing: PendingPlugin[]; + updating: PendingPlugin[]; + removing: PendingPlugin[]; }; readOnly: boolean; refreshPending: () => void; @@ -49,9 +54,9 @@ export default class PluginsList extends React.PureComponent { return undefined; }; - renderPlugin = (plugin: Plugin) => { + renderPlugin = (plugin: Plugin, installedPlugins: InstalledPlugin[]) => { const status = this.getPluginStatus(plugin); - if (isPluginInstalled(plugin)) { + if (isInstalledPlugin(plugin)) { return ( { /> ); } - if (isPluginAvailable(plugin)) { + if (isAvailablePlugin(plugin)) { return ( { }; render() { + const installedPlugins = this.props.plugins.filter(isInstalledPlugin); return (
    {this.props.plugins.map(plugin => (
  • - {this.renderPlugin(plugin)} + {this.renderPlugin(plugin, installedPlugins)}
  • ))} diff --git a/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx b/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx index 57b237d965f..06319354ad5 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/components/PluginActions.tsx @@ -22,8 +22,8 @@ import { Button } from 'sonar-ui-common/components/controls/buttons'; import Checkbox from 'sonar-ui-common/components/controls/Checkbox'; import CheckIcon from 'sonar-ui-common/components/icons/CheckIcon'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { installPlugin, Plugin, uninstallPlugin, updatePlugin } from '../../../api/plugins'; -import { isPluginAvailable, isPluginInstalled } from '../utils'; +import { installPlugin, uninstallPlugin, updatePlugin } from '../../../api/plugins'; +import { isAvailablePlugin, isInstalledPlugin, Plugin } from '../../../types/plugins'; import PluginUpdateButton from './PluginUpdateButton'; interface Props { @@ -75,7 +75,7 @@ export default class PluginActions extends React.PureComponent { return (
    - {isPluginAvailable(plugin) && ( + {isAvailablePlugin(plugin) && (

    {translate('marketplace.available_under_commercial_license')} @@ -85,13 +85,13 @@ export default class PluginActions extends React.PureComponent {

    )} - {isPluginInstalled(plugin) && ( + {isInstalledPlugin(plugin) && (

    {translate('marketplace.installed')}

    )} - {isPluginInstalled(plugin) && plugin.updates && plugin.updates.length > 0 && ( + {isInstalledPlugin(plugin) && plugin.updates && plugin.updates.length > 0 && (
    {plugin.updates.map((update, idx) => ( { const { loading } = this.state; return (
    - {isPluginAvailable(plugin) && plugin.termsAndConditionsUrl && ( + {isAvailablePlugin(plugin) && plugin.termsAndConditionsUrl && (

    {

    )} {loading && } - {isPluginInstalled(plugin) && ( + {isInstalledPlugin(plugin) && (
    {plugin.updates && plugin.updates.map((update, idx) => ( @@ -157,7 +157,7 @@ export default class PluginActions extends React.PureComponent {
    )} - {isPluginAvailable(plugin) && ( + {isAvailablePlugin(plugin) && (