diff --git a/packages/grafana-data/src/types/index.ts b/packages/grafana-data/src/types/index.ts index 84498c6570e..6bf12f650b8 100644 --- a/packages/grafana-data/src/types/index.ts +++ b/packages/grafana-data/src/types/index.ts @@ -54,7 +54,7 @@ export * from './accesscontrol'; export * from './icon'; export { PluginExtensionTypes, - PluginExtensionPlacements, + PluginExtensionPoints, type PluginExtension, type PluginExtensionLink, type PluginExtensionConfig, diff --git a/packages/grafana-data/src/types/pluginExtensions.ts b/packages/grafana-data/src/types/pluginExtensions.ts index 2de8dd82782..796c4c6e24b 100644 --- a/packages/grafana-data/src/types/pluginExtensions.ts +++ b/packages/grafana-data/src/types/pluginExtensions.ts @@ -29,11 +29,11 @@ export type PluginExtensionConfig & ExtraProps & { - // The unique name of the placement - // Core Grafana placements are available in the `PluginExtensionPlacements` enum - placement: string; + // The unique identifier of the Extension Point + // (Core Grafana extension point ids are available in the `PluginExtensionPoints` enum) + extensionPointId: string; - // (Optional) A function that can be used to configure the extension dynamically based on the placement's context + // (Optional) A function that can be used to configure the extension dynamically based on the extension point's context configure?: ( context?: Readonly ) => Partial<{ title: string; description: string } & ExtraProps> | undefined; @@ -58,11 +58,11 @@ export type PluginExtensionEventHelpers = { }) => void; }; -// Placements & Contexts +// Extension Points & Contexts // -------------------------------------------------------- -// Placements available in core Grafana -export enum PluginExtensionPlacements { +// Extension Points available in core Grafana +export enum PluginExtensionPoints { DashboardPanelMenu = 'grafana/dashboard/panel/menu', } diff --git a/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.test.ts b/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.test.ts index f741e24dbb9..cb1c9e35e03 100644 --- a/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.test.ts +++ b/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.test.ts @@ -9,10 +9,10 @@ describe('Plugin Extensions / Get Plugin Extensions', () => { const getter: GetPluginExtensions = jest.fn().mockReturnValue({ extensions: [] }); setPluginExtensionGetter(getter); - getPluginExtensions({ placement: 'panel-menu' }); + getPluginExtensions({ extensionPointId: 'panel-menu' }); expect(getter).toHaveBeenCalledTimes(1); - expect(getter).toHaveBeenCalledWith({ placement: 'panel-menu' }); + expect(getter).toHaveBeenCalledWith({ extensionPointId: 'panel-menu' }); }); test('should throw an error when trying to redefine the app-wide extension-getter function', () => { @@ -33,7 +33,7 @@ describe('Plugin Extensions / Get Plugin Extensions', () => { setPluginExtensionGetter(undefined); expect(() => { - getPluginExtensions({ placement: 'panel-menu' }); + getPluginExtensions({ extensionPointId: 'panel-menu' }); }).toThrowError(); }); }); diff --git a/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.ts b/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.ts index a8c9680138f..965fbf84021 100644 --- a/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.ts +++ b/packages/grafana-runtime/src/services/pluginExtensions/getPluginExtensions.ts @@ -1,10 +1,10 @@ import { PluginExtension } from '@grafana/data'; export type GetPluginExtensions = ({ - placement, + extensionPointId, context, }: { - placement: string; + extensionPointId: string; context?: object | Record; }) => { extensions: PluginExtension[]; diff --git a/public/app/features/dashboard/utils/getPanelMenu.ts b/public/app/features/dashboard/utils/getPanelMenu.ts index 867f348660d..c8b7dea7086 100644 --- a/public/app/features/dashboard/utils/getPanelMenu.ts +++ b/public/app/features/dashboard/utils/getPanelMenu.ts @@ -1,4 +1,4 @@ -import { PanelMenuItem, PluginExtensionPlacements, type PluginExtensionPanelContext } from '@grafana/data'; +import { PanelMenuItem, PluginExtensionPoints, type PluginExtensionPanelContext } from '@grafana/data'; import { isPluginExtensionLink, AngularComponent, @@ -279,7 +279,7 @@ export function getPanelMenu( } const { extensions } = getPluginExtensions({ - placement: PluginExtensionPlacements.DashboardPanelMenu, + extensionPointId: PluginExtensionPoints.DashboardPanelMenu, context: createExtensionContext(panel, dashboard), }); diff --git a/public/app/features/plugins/extensions/constants.ts b/public/app/features/plugins/extensions/constants.ts index c128234677e..9e7dea615f6 100644 --- a/public/app/features/plugins/extensions/constants.ts +++ b/public/app/features/plugins/extensions/constants.ts @@ -1 +1 @@ -export const MAX_EXTENSIONS_PER_PLACEMENT_PER_PLUGIN = 2; +export const MAX_EXTENSIONS_PER_POINT = 2; diff --git a/public/app/features/plugins/extensions/createPluginExtensionRegistry.test.ts b/public/app/features/plugins/extensions/createPluginExtensionRegistry.test.ts index 153a3d67959..a8f2c5d045c 100644 --- a/public/app/features/plugins/extensions/createPluginExtensionRegistry.test.ts +++ b/public/app/features/plugins/extensions/createPluginExtensionRegistry.test.ts @@ -14,7 +14,7 @@ describe('createRegistry()', () => { title: 'Link 1', description: 'Link 1 description', path: `/a/${pluginId}/declare-incident`, - placement: placement1, + extensionPointId: placement1, configure: jest.fn().mockReturnValue({}), }; link2 = { @@ -22,7 +22,7 @@ describe('createRegistry()', () => { title: 'Link 2', description: 'Link 2 description', path: `/a/${pluginId}/declare-incident`, - placement: placement2, + extensionPointId: placement2, configure: jest.fn().mockImplementation((context) => ({ title: context?.title })), }; diff --git a/public/app/features/plugins/extensions/createPluginExtensionRegistry.ts b/public/app/features/plugins/extensions/createPluginExtensionRegistry.ts index 54ab7aaf10a..1004c1957ce 100644 --- a/public/app/features/plugins/extensions/createPluginExtensionRegistry.ts +++ b/public/app/features/plugins/extensions/createPluginExtensionRegistry.ts @@ -1,14 +1,14 @@ import type { PluginPreloadResult } from '../pluginPreloader'; -import { MAX_EXTENSIONS_PER_PLACEMENT_PER_PLUGIN } from './constants'; -import { PlacementsPerPlugin } from './placementsPerPlugin'; +import { MAX_EXTENSIONS_PER_POINT } from './constants'; +import { ExtensionsPerPlugin } from './extensionsPerPlugin'; import type { PluginExtensionRegistryItem, PluginExtensionRegistry } from './types'; import { deepFreeze, logWarning } from './utils'; import { isPluginExtensionConfigValid } from './validators'; export function createPluginExtensionRegistry(pluginPreloadResults: PluginPreloadResult[]): PluginExtensionRegistry { const registry: PluginExtensionRegistry = {}; - const placementsPerPlugin = new PlacementsPerPlugin(); + const extensionsPerPlugin = new ExtensionsPerPlugin(); for (const { pluginId, extensionConfigs, error } of pluginPreloadResults) { if (error) { @@ -17,11 +17,11 @@ export function createPluginExtensionRegistry(pluginPreloadResults: PluginPreloa } for (const extensionConfig of extensionConfigs) { - const { placement } = extensionConfig; + const { extensionPointId } = extensionConfig; - if (!placementsPerPlugin.allowedToAdd(extensionConfig)) { + if (!extensionsPerPlugin.allowedToAdd(extensionConfig)) { logWarning( - `"${pluginId}" plugin has reached the limit of ${MAX_EXTENSIONS_PER_PLACEMENT_PER_PLUGIN} for "${placement}", skip registering extension "${extensionConfig.title}".` + `"${pluginId}" plugin has reached the limit of ${MAX_EXTENSIONS_PER_POINT} for "${extensionPointId}", skip registering extension "${extensionConfig.title}".` ); continue; } @@ -37,10 +37,10 @@ export function createPluginExtensionRegistry(pluginPreloadResults: PluginPreloa pluginId, }; - if (!Array.isArray(registry[placement])) { - registry[placement] = [registryItem]; + if (!Array.isArray(registry[extensionPointId])) { + registry[extensionPointId] = [registryItem]; } else { - registry[placement].push(registryItem); + registry[extensionPointId].push(registryItem); } } } diff --git a/public/app/features/plugins/extensions/extensionsPerPlugin.ts b/public/app/features/plugins/extensions/extensionsPerPlugin.ts new file mode 100644 index 00000000000..1f119f57f5b --- /dev/null +++ b/public/app/features/plugins/extensions/extensionsPerPlugin.ts @@ -0,0 +1,33 @@ +import { PluginExtensionLinkConfig } from '@grafana/data'; + +import { MAX_EXTENSIONS_PER_POINT } from './constants'; + +export class ExtensionsPerPlugin { + private extensionsByExtensionPoint: Record = {}; + + allowedToAdd({ extensionPointId, title }: PluginExtensionLinkConfig): boolean { + if (this.countByExtensionPoint(extensionPointId) >= MAX_EXTENSIONS_PER_POINT) { + return false; + } + + this.addExtensionToExtensionPoint(extensionPointId, title); + + return true; + } + + addExtensionToExtensionPoint(extensionPointId: string, extensionTitle: string) { + if (!this.extensionsByExtensionPoint[extensionPointId]) { + this.extensionsByExtensionPoint[extensionPointId] = []; + } + + this.extensionsByExtensionPoint[extensionPointId].push(extensionTitle); + } + + countByExtensionPoint(extensionPointId: string) { + return this.extensionsByExtensionPoint[extensionPointId]?.length ?? 0; + } + + getExtensionTitlesByExtensionPoint(extensionPointId: string) { + return this.extensionsByExtensionPoint[extensionPointId]; + } +} diff --git a/public/app/features/plugins/extensions/getPluginExtensions.test.ts b/public/app/features/plugins/extensions/getPluginExtensions.test.ts index 51ce03825db..d68b0e1f130 100644 --- a/public/app/features/plugins/extensions/getPluginExtensions.test.ts +++ b/public/app/features/plugins/extensions/getPluginExtensions.test.ts @@ -5,8 +5,8 @@ import { getPluginExtensions } from './getPluginExtensions'; import { assertPluginExtensionLink } from './validators'; describe('getPluginExtensions()', () => { - const placement1 = 'grafana/dashboard/panel/menu'; - const placement2 = 'plugins/myorg-basic-app/start'; + const extensionPoint1 = 'grafana/dashboard/panel/menu'; + const extensionPoint2 = 'plugins/myorg-basic-app/start'; const pluginId = 'grafana-basic-app'; let link1: PluginExtensionLinkConfig, link2: PluginExtensionLinkConfig; @@ -16,7 +16,7 @@ describe('getPluginExtensions()', () => { title: 'Link 1', description: 'Link 1 description', path: `/a/${pluginId}/declare-incident`, - placement: placement1, + extensionPointId: extensionPoint1, configure: jest.fn().mockReturnValue({}), }; link2 = { @@ -24,7 +24,7 @@ describe('getPluginExtensions()', () => { title: 'Link 2', description: 'Link 2 description', path: `/a/${pluginId}/declare-incident`, - placement: placement2, + extensionPointId: extensionPoint2, configure: jest.fn().mockImplementation((context) => ({ title: context?.title })), }; @@ -33,7 +33,7 @@ describe('getPluginExtensions()', () => { test('should return the extensions for the given placement', () => { const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link1, link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement1 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint1 }); expect(extensions).toHaveLength(1); expect(extensions[0]).toEqual( @@ -49,7 +49,7 @@ describe('getPluginExtensions()', () => { test('should return with an empty list if there are no extensions registered for a placement yet', () => { const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link1, link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: 'placement-with-no-extensions' }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: 'placement-with-no-extensions' }); expect(extensions).toEqual([]); }); @@ -58,7 +58,7 @@ describe('getPluginExtensions()', () => { const context = { title: 'New title from the context!' }; const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - getPluginExtensions({ registry, context, placement: placement2 }); + getPluginExtensions({ registry, context, extensionPointId: extensionPoint2 }); expect(link2.configure).toHaveBeenCalledTimes(1); expect(link2.configure).toHaveBeenCalledWith(context); @@ -72,7 +72,7 @@ describe('getPluginExtensions()', () => { })); const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); const [extension] = extensions; assertPluginExtensionLink(extension); @@ -91,7 +91,7 @@ describe('getPluginExtensions()', () => { })); const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); expect(link2.configure).toHaveBeenCalledTimes(1); expect(extensions).toHaveLength(0); @@ -99,7 +99,7 @@ describe('getPluginExtensions()', () => { test('should pass a frozen copy of the context to the configure() function', () => { const context = { title: 'New title from the context!' }; const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, context, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, context, extensionPointId: extensionPoint2 }); const [extension] = extensions; const frozenContext = (link2.configure as jest.Mock).mock.calls[0][0]; @@ -121,7 +121,7 @@ describe('getPluginExtensions()', () => { const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); expect(() => { - getPluginExtensions({ registry, placement: placement2 }); + getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); }).not.toThrow(); expect(link2.configure).toHaveBeenCalledTimes(1); @@ -138,8 +138,8 @@ describe('getPluginExtensions()', () => { })); const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link1, link2] }]); - const { extensions: extensionsAtPlacement1 } = getPluginExtensions({ registry, placement: placement1 }); - const { extensions: extensionsAtPlacement2 } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions: extensionsAtPlacement1 } = getPluginExtensions({ registry, extensionPointId: extensionPoint1 }); + const { extensions: extensionsAtPlacement2 } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); expect(extensionsAtPlacement1).toHaveLength(0); expect(extensionsAtPlacement2).toHaveLength(0); @@ -158,7 +158,7 @@ describe('getPluginExtensions()', () => { link2.configure = jest.fn().mockImplementation(() => overrides); const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); expect(extensions).toHaveLength(0); expect(link2.configure).toHaveBeenCalledTimes(1); @@ -169,7 +169,7 @@ describe('getPluginExtensions()', () => { link2.configure = jest.fn().mockImplementation(() => Promise.resolve({})); const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); expect(extensions).toHaveLength(0); expect(link2.configure).toHaveBeenCalledTimes(1); @@ -180,7 +180,7 @@ describe('getPluginExtensions()', () => { link2.configure = jest.fn().mockImplementation(() => undefined); const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); expect(extensions).toHaveLength(0); expect(global.console.warn).toHaveBeenCalledTimes(0); // As this is intentional, no warning should be logged @@ -194,7 +194,7 @@ describe('getPluginExtensions()', () => { const context = {}; const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); const [extension] = extensions; assertPluginExtensionLink(extension); @@ -217,7 +217,7 @@ describe('getPluginExtensions()', () => { link2.onClick = jest.fn().mockRejectedValue(new Error('testing')); const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); const [extension] = extensions; assertPluginExtensionLink(extension); @@ -236,7 +236,7 @@ describe('getPluginExtensions()', () => { }); const registry = createPluginExtensionRegistry([{ pluginId, extensionConfigs: [link2] }]); - const { extensions } = getPluginExtensions({ registry, placement: placement2 }); + const { extensions } = getPluginExtensions({ registry, extensionPointId: extensionPoint2 }); const [extension] = extensions; assertPluginExtensionLink(extension); diff --git a/public/app/features/plugins/extensions/getPluginExtensions.ts b/public/app/features/plugins/extensions/getPluginExtensions.ts index 84a5d629245..0a46015036c 100644 --- a/public/app/features/plugins/extensions/getPluginExtensions.ts +++ b/public/app/features/plugins/extensions/getPluginExtensions.ts @@ -11,18 +11,18 @@ import { assertIsNotPromise, assertLinkPathIsValid, assertStringProps, isPromise type GetExtensions = ({ context, - placement, + extensionPointId, registry, }: { context?: object | Record; - placement: string; + extensionPointId: string; registry: PluginExtensionRegistry; }) => { extensions: PluginExtension[] }; -// Returns with a list of plugin extensions for the given placement -export const getPluginExtensions: GetExtensions = ({ context, placement, registry }) => { +// Returns with a list of plugin extensions for the given extension point +export const getPluginExtensions: GetExtensions = ({ context, extensionPointId, registry }) => { const frozenContext = context ? deepFreeze(context) : {}; - const registryItems = registry[placement] ?? []; + const registryItems = registry[extensionPointId] ?? []; // We don't return the extensions separated by type, because in that case it would be much harder to define a sort-order for them. const extensions: PluginExtension[] = []; diff --git a/public/app/features/plugins/extensions/placementsPerPlugin.ts b/public/app/features/plugins/extensions/placementsPerPlugin.ts deleted file mode 100644 index 05935e8fa3a..00000000000 --- a/public/app/features/plugins/extensions/placementsPerPlugin.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PluginExtensionLinkConfig } from '@grafana/data'; - -import { MAX_EXTENSIONS_PER_PLACEMENT_PER_PLUGIN } from './constants'; - -export class PlacementsPerPlugin { - private extensionsByPlacement: Record = {}; - - allowedToAdd({ placement, title }: PluginExtensionLinkConfig): boolean { - if (this.countByPlacement(placement) >= MAX_EXTENSIONS_PER_PLACEMENT_PER_PLUGIN) { - return false; - } - - this.addExtensionToPlacement(placement, title); - - return true; - } - - addExtensionToPlacement(placement: string, extensionTitle: string) { - if (!this.extensionsByPlacement[placement]) { - this.extensionsByPlacement[placement] = []; - } - - this.extensionsByPlacement[placement].push(extensionTitle); - } - - countByPlacement(placement: string) { - return this.extensionsByPlacement[placement]?.length ?? 0; - } - - getExtensionTitlesByPlacement(placement: string) { - return this.extensionsByPlacement[placement]; - } -} diff --git a/public/app/features/plugins/extensions/utils.tsx b/public/app/features/plugins/extensions/utils.tsx index ee9622691d8..dd7de44c70a 100644 --- a/public/app/features/plugins/extensions/utils.tsx +++ b/public/app/features/plugins/extensions/utils.tsx @@ -104,7 +104,7 @@ export function deepFreeze(value?: object | Record | u } export function generateExtensionId(pluginId: string, extensionConfig: PluginExtensionConfig): string { - const str = `${pluginId}${extensionConfig.placement}${extensionConfig.title}`; + const str = `${pluginId}${extensionConfig.extensionPointId}${extensionConfig.title}`; return Array.from(str) .reduce((s, c) => (Math.imul(31, s) + c.charCodeAt(0)) | 0, 0) diff --git a/public/app/features/plugins/extensions/validators.test.ts b/public/app/features/plugins/extensions/validators.test.ts index 7038b84cf41..eb1b8e75a92 100644 --- a/public/app/features/plugins/extensions/validators.test.ts +++ b/public/app/features/plugins/extensions/validators.test.ts @@ -3,7 +3,7 @@ import { PluginExtension, PluginExtensionLinkConfig, PluginExtensionTypes } from import { assertConfigureIsValid, assertLinkPathIsValid, - assertPlacementIsValid, + assertExtensionPointIdIsValid, assertPluginExtensionLink, assertStringProps, isPluginExtensionConfigValid, @@ -43,7 +43,7 @@ describe('Plugin Extension Validators', () => { path: `/a/${pluginId}/overview`, title: 'My Plugin', description: 'My Plugin Description', - placement: '...', + extensionPointId: '...', }; assertLinkPathIsValid(pluginId, extension.path); @@ -56,7 +56,7 @@ describe('Plugin Extension Validators', () => { path: `/a/myorg-b-app/overview`, title: 'My Plugin', description: 'My Plugin Description', - placement: '...', + extensionPointId: '...', }; assertLinkPathIsValid('another-plugin-app', extension.path); @@ -69,7 +69,7 @@ describe('Plugin Extension Validators', () => { path: `/some-bad-path`, title: 'My Plugin', description: 'My Plugin Description', - placement: '...', + extensionPointId: '...', }; assertLinkPathIsValid('myorg-b-app', extension.path); @@ -77,35 +77,35 @@ describe('Plugin Extension Validators', () => { }); }); - describe('assertPlacementIsValid()', () => { - it('should throw an error if the placement does not have the right prefix', () => { + describe('assertExtensionPointIdIsValid()', () => { + it('should throw an error if the extensionPointId does not have the right prefix', () => { expect(() => { - assertPlacementIsValid({ + assertExtensionPointIdIsValid({ type: PluginExtensionTypes.link, title: 'Title', description: 'Description', path: '...', - placement: 'some-bad-placement', + extensionPointId: 'wrong-extension-point-id', }); }).toThrowError(); }); - it('should NOT throw an error if the placement is correct', () => { + it('should NOT throw an error if the extensionPointId is correct', () => { expect(() => { - assertPlacementIsValid({ + assertExtensionPointIdIsValid({ type: PluginExtensionTypes.link, title: 'Title', description: 'Description', path: '...', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', }); - assertPlacementIsValid({ + assertExtensionPointIdIsValid({ type: PluginExtensionTypes.link, title: 'Title', description: 'Description', path: '...', - placement: 'plugins/my-super-plugin/some-page/some-placement', + extensionPointId: 'plugins/my-super-plugin/some-page/extension-point-a', }); }).not.toThrowError(); }); @@ -117,7 +117,7 @@ describe('Plugin Extension Validators', () => { assertConfigureIsValid({ title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', } as PluginExtensionLinkConfig); }).not.toThrowError(); }); @@ -127,7 +127,7 @@ describe('Plugin Extension Validators', () => { assertConfigureIsValid({ title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', configure: () => {}, } as PluginExtensionLinkConfig); }).not.toThrowError(); @@ -140,7 +140,7 @@ describe('Plugin Extension Validators', () => { { title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', handler: () => {}, configure: '() => {}', } as PluginExtensionLinkConfig @@ -155,9 +155,9 @@ describe('Plugin Extension Validators', () => { assertStringProps( { description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', }, - ['title', 'description', 'placement'] + ['title', 'description', 'extensionPointId'] ); }).toThrowError(); }); @@ -168,9 +168,9 @@ describe('Plugin Extension Validators', () => { { title: '', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', }, - ['title', 'description', 'placement'] + ['title', 'description', 'extensionPointId'] ); }).toThrowError(); }); @@ -181,9 +181,9 @@ describe('Plugin Extension Validators', () => { { title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', }, - ['title', 'description', 'placement'] + ['title', 'description', 'extensionPointId'] ); }).not.toThrowError(); }); @@ -194,10 +194,10 @@ describe('Plugin Extension Validators', () => { { title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', dontCare: '', }, - ['title', 'description', 'placement'] + ['title', 'description', 'extensionPointId'] ); }).not.toThrowError(); }); @@ -212,8 +212,8 @@ describe('Plugin Extension Validators', () => { type: PluginExtensionTypes.link, title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', onClick: jest.fn(), + extensionPointId: 'grafana/some-page/extension-point-a', } as PluginExtensionLinkConfig) ).toBe(true); @@ -222,7 +222,7 @@ describe('Plugin Extension Validators', () => { type: PluginExtensionTypes.link, title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', path: `/a/${pluginId}/page`, } as PluginExtensionLinkConfig) ).toBe(true); @@ -239,7 +239,7 @@ describe('Plugin Extension Validators', () => { type: PluginExtensionTypes.link, title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', path: '/administration/users', } as PluginExtensionLinkConfig) ).toBe(false); @@ -250,7 +250,7 @@ describe('Plugin Extension Validators', () => { type: PluginExtensionTypes.link, title: 'Title', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', } as PluginExtensionLinkConfig) ).toBe(false); @@ -260,7 +260,7 @@ describe('Plugin Extension Validators', () => { type: PluginExtensionTypes.link, title: '', description: 'Description', - placement: 'grafana/some-page/some-placement', + extensionPointId: 'grafana/some-page/extension-point-a', path: `/a/${pluginId}/page`, } as PluginExtensionLinkConfig) ).toBe(false); diff --git a/public/app/features/plugins/extensions/validators.ts b/public/app/features/plugins/extensions/validators.ts index eb9b12f9bd5..9afec797639 100644 --- a/public/app/features/plugins/extensions/validators.ts +++ b/public/app/features/plugins/extensions/validators.ts @@ -29,10 +29,10 @@ export function assertLinkPathIsValid(pluginId: string, path: string) { } } -export function assertPlacementIsValid(extension: PluginExtensionLinkConfig) { - if (!isPlacementValid(extension)) { +export function assertExtensionPointIdIsValid(extension: PluginExtensionLinkConfig) { + if (!isExtensionPointIdValid(extension)) { throw new Error( - `Invalid extension "${extension.title}". The placement should start with either "grafana/" or "plugins/" (currently: "${extension.placement}"). Skipping the extension.` + `Invalid extension "${extension.title}". The extensionPointId should start with either "grafana/" or "plugins/" (currently: "${extension.extensionPointId}"). Skipping the extension.` ); } } @@ -65,8 +65,10 @@ export function isLinkPathValid(pluginId: string, path: string) { return Boolean(typeof path === 'string' && path.length > 0 && path.startsWith(`/a/${pluginId}/`)); } -export function isPlacementValid(extension: PluginExtensionLinkConfig) { - return Boolean(extension.placement?.startsWith('grafana/') || extension.placement?.startsWith('plugins/')); +export function isExtensionPointIdValid(extension: PluginExtensionLinkConfig) { + return Boolean( + extension.extensionPointId?.startsWith('grafana/') || extension.extensionPointId?.startsWith('plugins/') + ); } export function isConfigureFnValid(extension: PluginExtensionLinkConfig) { @@ -79,8 +81,8 @@ export function isStringPropValid(prop: unknown) { export function isPluginExtensionConfigValid(pluginId: string, extension: PluginExtensionLinkConfig): boolean { try { - assertStringProps(extension, ['title', 'description', 'placement']); - assertPlacementIsValid(extension); + assertStringProps(extension, ['title', 'description', 'extensionPointId']); + assertExtensionPointIdIsValid(extension); assertConfigureIsValid(extension); if (isPluginExtensionLinkConfig(extension)) { diff --git a/public/app/features/sandbox/TestStuffPage.tsx b/public/app/features/sandbox/TestStuffPage.tsx index 64862575cba..c2ecb21ec12 100644 --- a/public/app/features/sandbox/TestStuffPage.tsx +++ b/public/app/features/sandbox/TestStuffPage.tsx @@ -62,7 +62,7 @@ export const TestStuffPage = () => { - + {data && ( @@ -148,8 +148,8 @@ export function getDefaultState(): State { }; } -function LinkToBasicApp({ placement }: { placement: string }) { - const { extensions } = getPluginExtensions({ placement }); +function LinkToBasicApp({ extensionPointId }: { extensionPointId: string }) { + const { extensions } = getPluginExtensions({ extensionPointId }); if (extensions.length === 0) { return null;