mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Catalog force enable via config and remove enterprise plugin options (#34358)
* force enable catalog based on backend config * update comments * chore(plugin-catalog): remove config page in favour of backend flag * docs(plugin-catalog): update readme Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
This commit is contained in:
parent
354aa54a33
commit
4e31169a43
@ -84,6 +84,11 @@ func (app *AppPlugin) InitApp(panels map[string]*PanelPlugin, dataSources map[st
|
|||||||
cfg *setting.Cfg) []*PluginStaticRoute {
|
cfg *setting.Cfg) []*PluginStaticRoute {
|
||||||
staticRoutes := app.InitFrontendPlugin(cfg)
|
staticRoutes := app.InitFrontendPlugin(cfg)
|
||||||
|
|
||||||
|
// force enable bundled catalog app
|
||||||
|
if app.Id == "grafana-plugin-catalog-app" && cfg.CatalogAppEnabled {
|
||||||
|
app.AutoEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
// check if we have child panels
|
// check if we have child panels
|
||||||
for _, panel := range panels {
|
for _, panel := range panels {
|
||||||
if strings.HasPrefix(panel.PluginDir, app.PluginDir) {
|
if strings.HasPrefix(panel.PluginDir, app.PluginDir) {
|
||||||
|
@ -1,21 +1,5 @@
|
|||||||
# Grafana plugin catalog
|
# Grafana plugin catalog
|
||||||
|
|
||||||
Browse and manage plugins from within Grafana.
|
Allow Admin users to browse and manage plugins from within Grafana.
|
||||||
|
|
||||||
- Admin users can browse plugins, list installed plugins, and install / uninstall plugins
|
This plugin is **included** with Grafana however it is only accessible if [enabled in Grafana settings](https://grafana.com/docs/grafana/next/administration/configuration/#catalog_app_enabled).
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
1. Navigate to **Configuration** -> **Plugins** and click on the Catalog plugin in the list
|
|
||||||
1. Click the **Enable app** to enable the plugin
|
|
||||||
1. Click the **Pin app** to add it to the side menu
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
| ------------------------- | ------------------------------------------------ |
|
|
||||||
| _Enable app_ | Must be done before being able to use the plugin |
|
|
||||||
| _Pin app_ | Add the app to the side menu |
|
|
||||||
| _Show Enterprise plugins_ | Show Enterprise plugins in the catalog |
|
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { PluginConfigPageProps, AppPluginMeta, PluginMeta } from '@grafana/data';
|
|
||||||
import { CatalogAppSettings } from 'types';
|
|
||||||
import { Button, Field, Legend, Switch } from '@grafana/ui';
|
|
||||||
import { api } from '../api';
|
|
||||||
import { PLUGIN_ID } from '../constants';
|
|
||||||
|
|
||||||
interface Props extends PluginConfigPageProps<AppPluginMeta<CatalogAppSettings>> {}
|
|
||||||
|
|
||||||
export const Settings = ({ plugin }: Props) => {
|
|
||||||
const [meta, setMeta] = useState(plugin.meta);
|
|
||||||
const [state, setState] = useState<CatalogAppSettings>(meta.jsonData ?? {});
|
|
||||||
|
|
||||||
const { pinned, enabled } = meta;
|
|
||||||
const { includeEnterprise } = state;
|
|
||||||
|
|
||||||
const onSave = () => {
|
|
||||||
updateAndReload(PLUGIN_ID, {
|
|
||||||
pinned,
|
|
||||||
enabled,
|
|
||||||
jsonData: state,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Legend>General</Legend>
|
|
||||||
<Field label="Enable app">
|
|
||||||
<Switch
|
|
||||||
value={enabled}
|
|
||||||
onChange={(e) => {
|
|
||||||
setMeta({ ...meta, enabled: e.currentTarget.checked });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Field label="Pin app" description="Add the app to the side menu.">
|
|
||||||
<Switch
|
|
||||||
value={pinned}
|
|
||||||
onChange={(e) => {
|
|
||||||
setMeta({ ...meta, pinned: e.currentTarget.checked });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Legend>Plugins</Legend>
|
|
||||||
<Field
|
|
||||||
label="Show Enterprise plugins"
|
|
||||||
description="Enterprise plugins require a Grafana Enterprise subscription."
|
|
||||||
>
|
|
||||||
<Switch
|
|
||||||
value={includeEnterprise}
|
|
||||||
onChange={(e) => {
|
|
||||||
setState({ ...state, includeEnterprise: e.currentTarget.checked });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Field>
|
|
||||||
<Button onClick={onSave}>Save</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateAndReload = async (pluginId: string, data: Partial<PluginMeta>) => {
|
|
||||||
try {
|
|
||||||
await api.updatePlugin(pluginId, data);
|
|
||||||
|
|
||||||
// Reloading the page as the changes made here wouldn't be propagated to the actual plugin otherwise.
|
|
||||||
// This is not ideal, however unfortunately currently there is no supported way for updating the plugin state.
|
|
||||||
window.location.reload();
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error while updating the plugin', e);
|
|
||||||
}
|
|
||||||
};
|
|
@ -9,7 +9,7 @@ type PluginsState = {
|
|||||||
installedPlugins: any[];
|
installedPlugins: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePlugins = (includeEnterprise = false) => {
|
export const usePlugins = () => {
|
||||||
const [state, setState] = useState<PluginsState>({ isLoading: true, items: [], installedPlugins: [] });
|
const [state, setState] = useState<PluginsState>({ isLoading: true, items: [], installedPlugins: [] });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -17,7 +17,7 @@ export const usePlugins = (includeEnterprise = false) => {
|
|||||||
const items = await api.getRemotePlugins();
|
const items = await api.getRemotePlugins();
|
||||||
const filteredItems = items
|
const filteredItems = items
|
||||||
.filter((plugin) => Boolean(plugin.versionSignatureType))
|
.filter((plugin) => Boolean(plugin.versionSignatureType))
|
||||||
.filter((plugin) => includeEnterprise || plugin.status !== 'enterprise')
|
.filter((plugin) => plugin.status !== 'enterprise')
|
||||||
.filter((plugin) => !status || plugin.status === status);
|
.filter((plugin) => !status || plugin.status === status);
|
||||||
const installedPlugins = await api.getInstalledPlugins();
|
const installedPlugins = await api.getInstalledPlugins();
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ export const usePlugins = (includeEnterprise = false) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchPluginData();
|
fetchPluginData();
|
||||||
}, [includeEnterprise]);
|
}, []);
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,6 @@
|
|||||||
import { ComponentClass } from 'react';
|
import { ComponentClass } from 'react';
|
||||||
|
|
||||||
import { AppPlugin, AppPluginMeta, AppRootProps, PluginConfigPageProps } from '@grafana/data';
|
import { AppPlugin, AppRootProps } from '@grafana/data';
|
||||||
import { Settings } from './config/Settings';
|
|
||||||
import { MarketplaceRootPage } from './RootPage';
|
import { MarketplaceRootPage } from './RootPage';
|
||||||
import { CatalogAppSettings } from './types';
|
|
||||||
|
|
||||||
export const plugin = new AppPlugin<CatalogAppSettings>()
|
export const plugin = new AppPlugin().setRootPage((MarketplaceRootPage as unknown) as ComponentClass<AppRootProps>);
|
||||||
.setRootPage((MarketplaceRootPage as unknown) as ComponentClass<AppRootProps>)
|
|
||||||
.addConfigPage({
|
|
||||||
title: 'Settings',
|
|
||||||
icon: 'info-circle',
|
|
||||||
body: (Settings as unknown) as ComponentClass<PluginConfigPageProps<AppPluginMeta<CatalogAppSettings>>>,
|
|
||||||
id: 'settings',
|
|
||||||
});
|
|
||||||
|
@ -8,14 +8,13 @@ import { SearchField } from '../components/SearchField';
|
|||||||
import { HorizontalGroup } from '../components/HorizontalGroup';
|
import { HorizontalGroup } from '../components/HorizontalGroup';
|
||||||
import { usePlugins } from '../hooks/usePlugins';
|
import { usePlugins } from '../hooks/usePlugins';
|
||||||
import { useHistory } from '../hooks/useHistory';
|
import { useHistory } from '../hooks/useHistory';
|
||||||
import { CatalogAppSettings, Plugin } from '../types';
|
import { Plugin } from '../types';
|
||||||
import { Page } from 'components/Page';
|
import { Page } from 'components/Page';
|
||||||
|
|
||||||
export const Browse = ({ query, meta }: AppRootProps) => {
|
export const Browse = ({ query }: AppRootProps) => {
|
||||||
const { q, filterBy, sortBy } = query;
|
const { q, filterBy, sortBy } = query;
|
||||||
const { includeEnterprise } = meta.jsonData as CatalogAppSettings;
|
|
||||||
|
|
||||||
const plugins = usePlugins(includeEnterprise);
|
const plugins = usePlugins();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const onSortByChange = (value: SelectableValue<string>) => {
|
const onSortByChange = (value: SelectableValue<string>) => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { cx, css } from '@emotion/css';
|
import { cx, css } from '@emotion/css';
|
||||||
|
|
||||||
import { dateTimeParse, AppRootProps, GrafanaTheme2 } from '@grafana/data';
|
import { dateTimeParse, GrafanaTheme2 } from '@grafana/data';
|
||||||
import { useStyles2, Legend, LinkButton } from '@grafana/ui';
|
import { useStyles2, Legend, LinkButton } from '@grafana/ui';
|
||||||
|
|
||||||
import { PLUGIN_ROOT } from '../constants';
|
import { PLUGIN_ROOT } from '../constants';
|
||||||
@ -12,14 +12,12 @@ import { SearchField } from '../components/SearchField';
|
|||||||
import { PluginTypeIcon } from '../components/PluginTypeIcon';
|
import { PluginTypeIcon } from '../components/PluginTypeIcon';
|
||||||
import { usePlugins } from '../hooks/usePlugins';
|
import { usePlugins } from '../hooks/usePlugins';
|
||||||
import { useHistory } from '../hooks/useHistory';
|
import { useHistory } from '../hooks/useHistory';
|
||||||
import { CatalogAppSettings, Plugin } from '../types';
|
import { Plugin } from '../types';
|
||||||
import { Page } from 'components/Page';
|
import { Page } from 'components/Page';
|
||||||
import { Loader } from 'components/Loader';
|
import { Loader } from 'components/Loader';
|
||||||
|
|
||||||
export const Discover = ({ meta }: AppRootProps) => {
|
export const Discover = () => {
|
||||||
const { includeEnterprise } = meta.jsonData as CatalogAppSettings;
|
const { items, isLoading } = usePlugins();
|
||||||
|
|
||||||
const { items, isLoading } = usePlugins(includeEnterprise);
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import { Page } from 'components/Page';
|
|||||||
import { Loader } from 'components/Loader';
|
import { Loader } from 'components/Loader';
|
||||||
|
|
||||||
export const Library = () => {
|
export const Library = () => {
|
||||||
const { isLoading, items, installedPlugins } = usePlugins(true);
|
const { isLoading, items, installedPlugins } = usePlugins();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
const filteredPlugins = items.filter((plugin) => !!installedPlugins.find((_) => _.id === plugin.slug));
|
const filteredPlugins = items.filter((plugin) => !!installedPlugins.find((_) => _.id === plugin.slug));
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
export interface CatalogAppSettings {
|
|
||||||
includeEnterprise?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PluginTypeCode = 'app' | 'panel' | 'datasource';
|
export type PluginTypeCode = 'app' | 'panel' | 'datasource';
|
||||||
|
|
||||||
export interface Plugin {
|
export interface Plugin {
|
||||||
|
Loading…
Reference in New Issue
Block a user