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<PluginPendingResult> {
+export function getPendingPlugins(): Promise<PendingPluginResult> {
return getJSON('/api/plugins/pending').catch(throwGlobalError);
}
return { ...update, previousUpdates };
}
-export function getInstalledPlugins(): Promise<PluginInstalled[]> {
+export function getInstalledPlugins(): Promise<InstalledPlugin[]> {
return getJSON('/api/plugins/installed', { f: 'category' }).then(
({ plugins }) => plugins,
throwGlobalError
);
}
-export function getInstalledPluginsWithUpdates(): Promise<PluginInstalled[]> {
+export function getInstalledPluginsWithUpdates(): Promise<InstalledPlugin[]> {
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 {
.catch(throwGlobalError);
}
-export function getPluginUpdates(): Promise<PluginInstalled[]> {
+export function getPluginUpdates(): Promise<InstalledPlugin[]> {
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,
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';
}
interface State {
- pendingPlugins: PluginPendingResult;
+ pendingPlugins: PendingPluginResult;
systemStatus: T.SysStatus;
}
* 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;
}
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;
}
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';
fetchSystemStatus: () => void;
location: {};
organizationsEnabled?: boolean;
- pendingPlugins: PluginPendingResult;
+ pendingPlugins: PendingPluginResult;
systemStatus: T.SysStatus;
}
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';
interface Props {
currentEdition?: EditionKey;
fetchPendingPlugins: () => void;
- pendingPlugins: PluginPendingResult;
+ pendingPlugins: PendingPluginResult;
location: Location;
router: Pick<Router, 'push'>;
standaloneMode?: boolean;
* 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;
return undefined;
};
- renderPlugin = (plugin: Plugin) => {
+ renderPlugin = (plugin: Plugin, installedPlugins: InstalledPlugin[]) => {
const status = this.getPluginStatus(plugin);
- if (isPluginInstalled(plugin)) {
+ if (isInstalledPlugin(plugin)) {
return (
<PluginInstalled
plugin={plugin}
/>
);
}
- if (isPluginAvailable(plugin)) {
+ if (isAvailablePlugin(plugin)) {
return (
<PluginAvailable
+ installedPlugins={installedPlugins}
plugin={plugin}
readOnly={this.props.readOnly}
refreshPending={this.props.refreshPending}
};
render() {
+ const installedPlugins = this.props.plugins.filter(isInstalledPlugin);
return (
<div className="boxed-group boxed-group-inner" id="marketplace-plugins">
<ul>
{this.props.plugins.map(plugin => (
<li className="panel panel-vertical" key={plugin.key}>
<table className="marketplace-plugin-table">
- <tbody>{this.renderPlugin(plugin)}</tbody>
+ <tbody>{this.renderPlugin(plugin, installedPlugins)}</tbody>
</table>
</li>
))}
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 {
return (
<div className="js-actions">
- {isPluginAvailable(plugin) && (
+ {isAvailablePlugin(plugin) && (
<div>
<p className="little-spacer-bottom">
{translate('marketplace.available_under_commercial_license')}
</a>
</div>
)}
- {isPluginInstalled(plugin) && (
+ {isInstalledPlugin(plugin) && (
<p>
<CheckIcon className="little-spacer-right" />
{translate('marketplace.installed')}
</p>
)}
- {isPluginInstalled(plugin) && plugin.updates && plugin.updates.length > 0 && (
+ {isInstalledPlugin(plugin) && plugin.updates && plugin.updates.length > 0 && (
<div className="spacer-top">
{plugin.updates.map((update, idx) => (
<PluginUpdateButton
const { loading } = this.state;
return (
<div className="js-actions">
- {isPluginAvailable(plugin) && plugin.termsAndConditionsUrl && (
+ {isAvailablePlugin(plugin) && plugin.termsAndConditionsUrl && (
<p className="little-spacer-bottom">
<Checkbox
checked={this.state.acceptTerms}
</p>
)}
{loading && <i className="spinner spacer-right little-spacer-top little-spacer-bottom" />}
- {isPluginInstalled(plugin) && (
+ {isInstalledPlugin(plugin) && (
<div className="display-inlin-block">
{plugin.updates &&
plugin.updates.map((update, idx) => (
</Button>
</div>
)}
- {isPluginAvailable(plugin) && (
+ {isAvailablePlugin(plugin) && (
<Button
className="js-install"
disabled={loading || (plugin.termsAndConditionsUrl != null && !this.state.acceptTerms)}
*/
import * as React from 'react';
import { translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { PluginAvailable as IPluginAvailable } from '../../../api/plugins';
+import { AvailablePlugin, InstalledPlugin } from '../../../types/plugins';
import PluginChangeLogButton from './PluginChangeLogButton';
import PluginDescription from './PluginDescription';
import PluginLicense from './PluginLicense';
import PluginStatus from './PluginStatus';
import PluginUrls from './PluginUrls';
-interface Props {
- plugin: IPluginAvailable;
+export interface PluginAvailableProps {
+ installedPlugins: InstalledPlugin[];
+ plugin: AvailablePlugin;
readOnly: boolean;
refreshPending: () => void;
status?: string;
}
-export default function PluginAvailable({ plugin, readOnly, refreshPending, status }: Props) {
+export default function PluginAvailable(props: PluginAvailableProps) {
+ const { installedPlugins, plugin, readOnly, status } = props;
+ const installedPluginKeys = installedPlugins.map(({ key }) => key);
return (
<tr>
<PluginDescription plugin={plugin} />
<strong>
{translateWithParameters(
'marketplace.installing_this_plugin_will_also_install_x',
- plugin.update.requires.map(requiredPlugin => requiredPlugin.name).join(', ')
+ plugin.update.requires
+ .filter(({ key }) => !installedPluginKeys.includes(key))
+ .map(requiredPlugin => requiredPlugin.name)
+ .join(', ')
)}
</strong>
</p>
</td>
{!readOnly && (
- <PluginStatus plugin={plugin} refreshPending={refreshPending} status={status} />
+ <PluginStatus plugin={plugin} refreshPending={props.refreshPending} status={status} />
)}
</tr>
);
*/
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { Release, Update } from '../../../api/plugins';
+import { Release, Update } from '../../../types/plugins';
import PluginChangeLogItem from './PluginChangeLogItem';
export interface Props {
import { ButtonLink } from 'sonar-ui-common/components/controls/buttons';
import Dropdown from 'sonar-ui-common/components/controls/Dropdown';
import EllipsisIcon from 'sonar-ui-common/components/icons/EllipsisIcon';
-import { Release, Update } from '../../../api/plugins';
+import { Release, Update } from '../../../types/plugins';
import PluginChangeLog from './PluginChangeLog';
interface Props {
import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import DateFormatter from 'sonar-ui-common/components/intl/DateFormatter';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { Release, Update } from '../../../api/plugins';
+import { Release, Update } from '../../../types/plugins';
interface Props {
release: Release;
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { Plugin } from '../../../api/plugins';
+import { Plugin } from '../../../types/plugins';
interface Props {
plugin: Plugin;
*/
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { PluginInstalled as IPluginInstalled } from '../../../api/plugins';
+import { InstalledPlugin } from '../../../types/plugins';
import PluginDescription from './PluginDescription';
import PluginLicense from './PluginLicense';
import PluginOrganization from './PluginOrganization';
import PluginUrls from './PluginUrls';
interface Props {
- plugin: IPluginInstalled;
+ plugin: InstalledPlugin;
readOnly: boolean;
refreshPending: () => void;
status?: string;
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { Plugin } from '../../../api/plugins';
+import { Plugin } from '../../../types/plugins';
export interface PluginOrganizationProps {
plugin: Plugin;
*/
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { Plugin } from '../../../api/plugins';
+import { Plugin } from '../../../types/plugins';
import PluginActions from './PluginActions';
interface Props {
import * as React from 'react';
import { Button } from 'sonar-ui-common/components/controls/buttons';
import { translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { Update } from '../../../api/plugins';
+import { Update } from '../../../types/plugins';
interface Props {
disabled: boolean;
import * as React from 'react';
import Tooltip from 'sonar-ui-common/components/controls/Tooltip';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { Release, Update } from '../../../api/plugins';
+import { Release, Update } from '../../../types/plugins';
import PluginChangeLogButton from './PluginChangeLogButton';
interface Props {
*/
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { Update } from '../../../api/plugins';
+import { Update } from '../../../types/plugins';
import PluginUpdateItem from './PluginUpdateItem';
interface Props {
*/
import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
-import { Plugin } from '../../../api/plugins';
+import { Plugin } from '../../../types/plugins';
interface Props {
plugin: Plugin;
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { PluginAvailable, PluginInstalled } from '../../../../api/plugins';
+import { AvailablePlugin, InstalledPlugin } from '../../../../types/plugins';
import PluginActions from '../PluginActions';
-const installedPlugin: PluginInstalled = {
+const installedPlugin: InstalledPlugin = {
key: 'foo',
name: 'Foo',
filename: 'foo.zip',
version: '7.7'
};
-const availablePlugin: PluginAvailable = {
+const availablePlugin: AvailablePlugin = {
key: 'foo',
name: 'Foo',
release: { version: '7.7', date: 'date' },
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { shallow } from 'enzyme';
+import * as React from 'react';
+import {
+ mockAvailablePlugin,
+ mockInstalledPlugin,
+ mockPlugin,
+ mockUpdate
+} from '../../../../helpers/mocks/plugins';
+import PluginAvailable, { PluginAvailableProps } from '../PluginAvailable';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot('default');
+ expect(shallowRender({ readOnly: true })).toMatchSnapshot('read only');
+ expect(
+ shallowRender({
+ plugin: mockAvailablePlugin({
+ update: mockUpdate({ requires: [mockPlugin()] })
+ })
+ })
+ ).toMatchSnapshot('has requirements');
+ const installed = mockInstalledPlugin({ key: 'sonar-bar', name: 'Sonar Bar' });
+ expect(
+ shallowRender({
+ installedPlugins: [installed],
+ plugin: mockAvailablePlugin({
+ update: mockUpdate({
+ requires: [mockPlugin(), installed]
+ })
+ })
+ })
+ ).toMatchSnapshot('has requirements, some of them already met');
+});
+
+function shallowRender(props: Partial<PluginAvailableProps> = {}) {
+ return shallow<PluginAvailableProps>(
+ <PluginAvailable
+ installedPlugins={[]}
+ plugin={mockAvailablePlugin()}
+ readOnly={false}
+ refreshPending={jest.fn()}
+ {...props}
+ />
+ );
+}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly: default 1`] = `
+<tr>
+ <PluginDescription
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [],
+ "status": "available",
+ },
+ }
+ }
+ />
+ <td
+ className="text-top big-spacer-right"
+ >
+ <ul>
+ <li
+ className="display-flex-row little-spacer-bottom"
+ >
+ <div
+ className="pull-left spacer-right"
+ >
+ <span
+ className="badge badge-success"
+ >
+ 8.2
+ </span>
+ </div>
+ <div>
+ <PluginChangeLogButton
+ release={
+ Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ }
+ }
+ update={
+ Object {
+ "requires": Array [],
+ "status": "available",
+ }
+ }
+ />
+ </div>
+ </li>
+ </ul>
+ </td>
+ <td
+ className="text-top width-20"
+ >
+ <ul>
+ <PluginUrls
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [],
+ "status": "available",
+ },
+ }
+ }
+ />
+ <PluginLicense />
+ <PluginOrganization
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [],
+ "status": "available",
+ },
+ }
+ }
+ />
+ </ul>
+ </td>
+ <PluginStatus
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [],
+ "status": "available",
+ },
+ }
+ }
+ refreshPending={[MockFunction]}
+ />
+</tr>
+`;
+
+exports[`should render correctly: has requirements 1`] = `
+<tr>
+ <PluginDescription
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ ],
+ "status": "available",
+ },
+ }
+ }
+ />
+ <td
+ className="text-top big-spacer-right"
+ >
+ <ul>
+ <li
+ className="display-flex-row little-spacer-bottom"
+ >
+ <div
+ className="pull-left spacer-right"
+ >
+ <span
+ className="badge badge-success"
+ >
+ 8.2
+ </span>
+ </div>
+ <div>
+ <PluginChangeLogButton
+ release={
+ Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ }
+ }
+ update={
+ Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ ],
+ "status": "available",
+ }
+ }
+ />
+ <p
+ className="little-spacer-top"
+ >
+ <strong>
+ marketplace.installing_this_plugin_will_also_install_x.Sonar Foo
+ </strong>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </td>
+ <td
+ className="text-top width-20"
+ >
+ <ul>
+ <PluginUrls
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ ],
+ "status": "available",
+ },
+ }
+ }
+ />
+ <PluginLicense />
+ <PluginOrganization
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ ],
+ "status": "available",
+ },
+ }
+ }
+ />
+ </ul>
+ </td>
+ <PluginStatus
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ ],
+ "status": "available",
+ },
+ }
+ }
+ refreshPending={[MockFunction]}
+ />
+</tr>
+`;
+
+exports[`should render correctly: has requirements, some of them already met 1`] = `
+<tr>
+ <PluginDescription
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ Object {
+ "filename": "sonar-bar-1.0.jar",
+ "hash": "hash",
+ "implementationBuild": "1.0.0.1234",
+ "key": "sonar-bar",
+ "name": "Sonar Bar",
+ "sonarLintSupported": false,
+ "updatedAt": 100,
+ "version": "1.0",
+ },
+ ],
+ "status": "available",
+ },
+ }
+ }
+ />
+ <td
+ className="text-top big-spacer-right"
+ >
+ <ul>
+ <li
+ className="display-flex-row little-spacer-bottom"
+ >
+ <div
+ className="pull-left spacer-right"
+ >
+ <span
+ className="badge badge-success"
+ >
+ 8.2
+ </span>
+ </div>
+ <div>
+ <PluginChangeLogButton
+ release={
+ Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ }
+ }
+ update={
+ Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ Object {
+ "filename": "sonar-bar-1.0.jar",
+ "hash": "hash",
+ "implementationBuild": "1.0.0.1234",
+ "key": "sonar-bar",
+ "name": "Sonar Bar",
+ "sonarLintSupported": false,
+ "updatedAt": 100,
+ "version": "1.0",
+ },
+ ],
+ "status": "available",
+ }
+ }
+ />
+ <p
+ className="little-spacer-top"
+ >
+ <strong>
+ marketplace.installing_this_plugin_will_also_install_x.Sonar Foo
+ </strong>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </td>
+ <td
+ className="text-top width-20"
+ >
+ <ul>
+ <PluginUrls
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ Object {
+ "filename": "sonar-bar-1.0.jar",
+ "hash": "hash",
+ "implementationBuild": "1.0.0.1234",
+ "key": "sonar-bar",
+ "name": "Sonar Bar",
+ "sonarLintSupported": false,
+ "updatedAt": 100,
+ "version": "1.0",
+ },
+ ],
+ "status": "available",
+ },
+ }
+ }
+ />
+ <PluginLicense />
+ <PluginOrganization
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ Object {
+ "filename": "sonar-bar-1.0.jar",
+ "hash": "hash",
+ "implementationBuild": "1.0.0.1234",
+ "key": "sonar-bar",
+ "name": "Sonar Bar",
+ "sonarLintSupported": false,
+ "updatedAt": 100,
+ "version": "1.0",
+ },
+ ],
+ "status": "available",
+ },
+ }
+ }
+ />
+ </ul>
+ </td>
+ <PluginStatus
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ },
+ Object {
+ "filename": "sonar-bar-1.0.jar",
+ "hash": "hash",
+ "implementationBuild": "1.0.0.1234",
+ "key": "sonar-bar",
+ "name": "Sonar Bar",
+ "sonarLintSupported": false,
+ "updatedAt": 100,
+ "version": "1.0",
+ },
+ ],
+ "status": "available",
+ },
+ }
+ }
+ refreshPending={[MockFunction]}
+ />
+</tr>
+`;
+
+exports[`should render correctly: read only 1`] = `
+<tr>
+ <PluginDescription
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [],
+ "status": "available",
+ },
+ }
+ }
+ />
+ <td
+ className="text-top big-spacer-right"
+ >
+ <ul>
+ <li
+ className="display-flex-row little-spacer-bottom"
+ >
+ <div
+ className="pull-left spacer-right"
+ >
+ <span
+ className="badge badge-success"
+ >
+ 8.2
+ </span>
+ </div>
+ <div>
+ <PluginChangeLogButton
+ release={
+ Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ }
+ }
+ update={
+ Object {
+ "requires": Array [],
+ "status": "available",
+ }
+ }
+ />
+ </div>
+ </li>
+ </ul>
+ </td>
+ <td
+ className="text-top width-20"
+ >
+ <ul>
+ <PluginUrls
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [],
+ "status": "available",
+ },
+ }
+ }
+ />
+ <PluginLicense />
+ <PluginOrganization
+ plugin={
+ Object {
+ "key": "sonar-foo",
+ "name": "Sonar Foo",
+ "release": Object {
+ "date": "2020-01-01",
+ "version": "8.2",
+ },
+ "update": Object {
+ "requires": Array [],
+ "status": "available",
+ },
+ }
+ }
+ />
+ </ul>
+ </td>
+</tr>
+`;
*/
import { memoize } from 'lodash';
import { cleanQuery, parseAsString, serializeString } from 'sonar-ui-common/helpers/query';
-import { Plugin, PluginAvailable, PluginInstalled, PluginPending } from '../../api/plugins';
+import { Plugin } from '../../types/plugins';
export interface Query {
filter: string;
});
}
-export function isPluginAvailable(plugin: Plugin): plugin is PluginAvailable {
- return (plugin as any).release !== undefined;
-}
-
-export function isPluginInstalled(plugin: Plugin): plugin is PluginInstalled {
- return isPluginPending(plugin) && (plugin as any).updatedAt !== undefined;
-}
-
-export function isPluginPending(plugin: Plugin): plugin is PluginPending {
- return (plugin as any).version !== undefined;
-}
-
export const DEFAULT_FILTER = 'all';
export const parseQuery = memoize(
(urlQuery: T.RawQuery): Query => ({
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 { AvailablePlugin, InstalledPlugin, Plugin, Release, Update } from '../../types/plugins';
+
+export function mockPlugin(overrides: Partial<Plugin> = {}): Plugin {
+ return {
+ key: 'sonar-foo',
+ name: 'Sonar Foo',
+ ...overrides
+ };
+}
+
+export function mockInstalledPlugin(overrides: Partial<InstalledPlugin> = {}): InstalledPlugin {
+ return {
+ key: 'sonar-bar',
+ name: 'Sonar Bar',
+ version: '1.0',
+ implementationBuild: '1.0.0.1234',
+ filename: 'sonar-bar-1.0.jar',
+ hash: 'hash',
+ sonarLintSupported: false,
+ updatedAt: 100,
+ ...overrides
+ };
+}
+
+export function mockAvailablePlugin(overrides: Partial<AvailablePlugin> = {}): AvailablePlugin {
+ return {
+ release: mockRelease(),
+ update: mockUpdate(),
+ ...mockPlugin(),
+ ...overrides
+ };
+}
+
+export function mockRelease(overrides: Partial<Release> = {}): Release {
+ return {
+ date: '2020-01-01',
+ version: '8.2',
+ ...overrides
+ };
+}
+
+export function mockUpdate(overrides: Partial<Update> = {}): Update {
+ return {
+ status: 'available',
+ requires: [],
+ ...overrides
+ };
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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 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 PendingPluginResult {
+ installing: PendingPlugin[];
+ updating: PendingPlugin[];
+ removing: PendingPlugin[];
+}
+
+export interface AvailablePlugin extends Plugin {
+ release: Release;
+ update: Update;
+}
+
+export interface PendingPlugin extends Plugin {
+ version: string;
+ implementationBuild: string;
+}
+
+export interface InstalledPlugin extends PendingPlugin {
+ documentationPath?: string;
+ filename: string;
+ hash: string;
+ sonarLintSupported: boolean;
+ updatedAt: number;
+ updates?: Update[];
+}
+
+export interface Release {
+ version: string;
+ date: string;
+ description?: string;
+ changeLogUrl?: string;
+}
+
+export interface Update {
+ status: string;
+ release?: Release;
+ requires: Plugin[];
+ previousUpdates?: Update[];
+}
+
+export function isAvailablePlugin(plugin: Plugin): plugin is AvailablePlugin {
+ return (plugin as any).release !== undefined;
+}
+
+export function isInstalledPlugin(plugin: Plugin): plugin is InstalledPlugin {
+ return isPendingPlugin(plugin) && (plugin as any).updatedAt !== undefined;
+}
+
+export function isPendingPlugin(plugin: Plugin): plugin is PendingPlugin {
+ return (plugin as any).version !== undefined;
+}