Plugins Catalog: hide plugins based on the Grafana config (#41679)

* refactor(pkg/api/frontendsettings): group plugin catalog settings

* feat(plugins/admin): add a default config value for `pluginCatalogHiddenPlugins`

* chore(grafana/config): add type for `pluginCatalogHiddenPlugins`

* feat(plugins/admin): hide plugins from the catalog based on the grafana config

* fix(plugins/admin): fix a rebase typo

* fix(plugins/admin): remove unnecessary filtering
This commit is contained in:
Levente Balogh 2021-11-15 15:51:16 +01:00 committed by GitHub
parent 2c8ac1f58f
commit 4219e4f107
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 7 deletions

View File

@ -84,6 +84,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
pluginCatalogURL = 'https://grafana.com/grafana/plugins/';
pluginAdminEnabled = true;
pluginAdminExternalManageEnabled = false;
pluginCatalogHiddenPlugins: string[] = [];
expressionsEnabled = false;
customTheme?: any;
awsAllowedAuthProviders: string[] = [];

View File

@ -288,6 +288,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"pluginCatalogURL": hs.Cfg.PluginCatalogURL,
"pluginAdminEnabled": hs.Cfg.PluginAdminEnabled,
"pluginAdminExternalManageEnabled": hs.Cfg.PluginAdminEnabled && hs.Cfg.PluginAdminExternalManageEnabled,
"pluginCatalogHiddenPlugins": hs.Cfg.PluginCatalogHiddenPlugins,
"expressionsEnabled": hs.Cfg.ExpressionsEnabled,
"awsAllowedAuthProviders": hs.Cfg.AWSAllowedAuthProviders,
"awsAssumeRoleEnabled": hs.Cfg.AWSAssumeRoleEnabled,
@ -298,8 +299,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"caching": map[string]bool{
"enabled": hs.Cfg.SectionWithEnvOverrides("caching").Key("enabled").MustBool(true),
},
"unifiedAlertingEnabled": hs.Cfg.UnifiedAlerting.Enabled,
"pluginCatalogHiddenPlugins": hs.Cfg.PluginCatalogHiddenPlugins,
"unifiedAlertingEnabled": hs.Cfg.UnifiedAlerting.Enabled,
}
if hs.Cfg.GeomapDefaultBaseLayerConfig != nil {

View File

@ -1,7 +1,7 @@
import { getBackendSrv } from '@grafana/runtime';
import { PluginError, renderMarkdown } from '@grafana/data';
import { API_ROOT, GCOM_API_ROOT } from './constants';
import { mergeLocalAndRemote } from './helpers';
import { mergeLocalAndRemote, isLocalPluginVisible, isRemotePluginVisible } from './helpers';
import {
PluginDetails,
Org,
@ -40,8 +40,9 @@ export async function getPluginDetails(id: string): Promise<CatalogPluginDetails
}
export async function getRemotePlugins(): Promise<RemotePlugin[]> {
const res = await getBackendSrv().get(`${GCOM_API_ROOT}/plugins`);
return res.items;
const { items: remotePlugins }: { items: RemotePlugin[] } = await getBackendSrv().get(`${GCOM_API_ROOT}/plugins`);
return remotePlugins.filter(isRemotePluginVisible);
}
async function getPlugin(slug: string): Promise<PluginDetails> {
@ -108,8 +109,9 @@ async function getLocalPluginReadme(id: string): Promise<string> {
}
export async function getLocalPlugins(): Promise<LocalPlugin[]> {
const installed = await getBackendSrv().get(`${API_ROOT}`, { embedded: 0 });
return installed;
const localPlugins: LocalPlugin[] = await getBackendSrv().get(`${API_ROOT}`, { embedded: 0 });
return localPlugins.filter(isLocalPluginVisible);
}
async function getOrg(slug: string): Promise<Org> {

View File

@ -1,6 +1,7 @@
import { RemotePlugin, LocalPlugin } from './types';
import { getLocalPluginMock, getRemotePluginMock, getCatalogPluginMock } from './__mocks__';
import { PluginSignatureStatus, PluginSignatureType, PluginType } from '@grafana/data';
import { config } from '@grafana/runtime';
import {
mapToCatalogPlugin,
mapRemoteToCatalog,
@ -9,6 +10,8 @@ import {
mergeLocalsAndRemotes,
sortPlugins,
Sorters,
isLocalPluginVisible,
isRemotePluginVisible,
} from './helpers';
describe('Plugins/Helpers', () => {
@ -641,4 +644,44 @@ describe('Plugins/Helpers', () => {
expect(sorted.map(({ id }) => id)).toEqual(['pie-chart', 'cloud-watch', 'jira', 'zabbix', 'snowflake']);
});
});
describe('isLocalPluginVisible()', () => {
test('should return TRUE if the plugin is not listed as hidden in the main Grafana configuration', () => {
config.pluginCatalogHiddenPlugins = ['akumuli-datasource'];
const plugin = getLocalPluginMock({
id: 'barchart',
});
expect(isLocalPluginVisible(plugin)).toBe(true);
});
test('should return FALSE if the plugin is listed as hidden in the main Grafana configuration', () => {
config.pluginCatalogHiddenPlugins = ['akumuli-datasource'];
const plugin = getLocalPluginMock({
id: 'akumuli-datasource',
});
expect(isLocalPluginVisible(plugin)).toBe(false);
});
});
describe('isRemotePluginVisible()', () => {
test('should return TRUE if the plugin is not listed as hidden in the main Grafana configuration', () => {
config.pluginCatalogHiddenPlugins = ['akumuli-datasource'];
const plugin = getRemotePluginMock({
slug: 'barchart',
});
expect(isRemotePluginVisible(plugin)).toBe(true);
});
test('should return FALSE if the plugin is listed as hidden in the main Grafana configuration', () => {
config.pluginCatalogHiddenPlugins = ['akumuli-datasource'];
const plugin = getRemotePluginMock({
slug: 'akumuli-datasource',
});
expect(isRemotePluginVisible(plugin)).toBe(false);
});
});
});

View File

@ -257,3 +257,13 @@ export function getLatestCompatibleVersion(versions: Version[] | undefined): Ver
return latest;
}
export const isLocalPluginVisible = (p: LocalPlugin) => isPluginVisible(p.id);
export const isRemotePluginVisible = (p: RemotePlugin) => isPluginVisible(p.slug);
function isPluginVisible(id: string) {
const { pluginCatalogHiddenPlugins }: { pluginCatalogHiddenPlugins: string[] } = config;
return !pluginCatalogHiddenPlugins.includes(id);
}