diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 4dd24346d98..80a81a7a4b6 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -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[] = []; diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index b5cc24de00c..297986c9a30 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -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 { diff --git a/public/app/features/plugins/admin/api.ts b/public/app/features/plugins/admin/api.ts index d69f0626382..3232527eed9 100644 --- a/public/app/features/plugins/admin/api.ts +++ b/public/app/features/plugins/admin/api.ts @@ -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 { - 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 { @@ -108,8 +109,9 @@ async function getLocalPluginReadme(id: string): Promise { } export async function getLocalPlugins(): Promise { - 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 { diff --git a/public/app/features/plugins/admin/helpers.test.ts b/public/app/features/plugins/admin/helpers.test.ts index 44e8f550133..b3034f2d154 100644 --- a/public/app/features/plugins/admin/helpers.test.ts +++ b/public/app/features/plugins/admin/helpers.test.ts @@ -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); + }); + }); }); diff --git a/public/app/features/plugins/admin/helpers.ts b/public/app/features/plugins/admin/helpers.ts index b36bb95d1f4..4f337eb2545 100644 --- a/public/app/features/plugins/admin/helpers.ts +++ b/public/app/features/plugins/admin/helpers.ts @@ -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); +}