PluginExtensions: Added support for sharing functions (#98888)

* feat: add generic plugin extension functions

* updated betterer.

* Fixed type issues after sync with main.

* Remved extensions from datasource and panel.

* Added validation for extension function registry.

* Added tests and validation logic for function extensions registry.

* removed prop already existing on base.

* fixed lint error.

---------

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
This commit is contained in:
Dominik Süß
2025-02-13 10:18:55 +01:00
committed by GitHub
co-authored by Marcus Andersson
parent fbf96916aa
commit 8a8e47fcea
40 changed files with 1182 additions and 69 deletions
@@ -22,6 +22,8 @@ export {
type UsePluginExtensions,
type UsePluginExtensionsResult,
type UsePluginComponentResult,
type UsePluginFunctionsOptions,
type UsePluginFunctionsResult,
} from './pluginExtensions/getPluginExtensions';
export {
setPluginExtensionsHook,
@@ -33,6 +35,7 @@ export {
export { setPluginComponentHook, usePluginComponent } from './pluginExtensions/usePluginComponent';
export { setPluginComponentsHook, usePluginComponents } from './pluginExtensions/usePluginComponents';
export { setPluginLinksHook, usePluginLinks } from './pluginExtensions/usePluginLinks';
export { setPluginFunctionsHook, usePluginFunctions } from './pluginExtensions/usePluginFunctions';
export { isPluginExtensionLink, isPluginExtensionComponent } from './pluginExtensions/utils';
export { setCurrentUser } from './user';
@@ -1,4 +1,9 @@
import type { PluginExtension, PluginExtensionLink, PluginExtensionComponent } from '@grafana/data';
import type {
PluginExtension,
PluginExtensionLink,
PluginExtensionComponent,
PluginExtensionFunction,
} from '@grafana/data';
import { isPluginExtensionComponent, isPluginExtensionLink } from './utils';
@@ -52,6 +57,16 @@ export type UsePluginLinksResult = {
links: PluginExtensionLink[];
};
export type UsePluginFunctionsOptions = {
extensionPointId: string;
limitPerPlugin?: number;
};
export type UsePluginFunctionsResult<Signature> = {
isLoading: boolean;
functions: Array<PluginExtensionFunction<Signature>>;
};
let singleton: GetPluginExtensions | undefined;
export function setPluginExtensionGetter(instance: GetPluginExtensions): void {
@@ -0,0 +1,20 @@
import { UsePluginFunctionsOptions, UsePluginFunctionsResult } from './getPluginExtensions';
export type UsePluginFunctions<T> = (options: UsePluginFunctionsOptions) => UsePluginFunctionsResult<T>;
let singleton: UsePluginFunctions<unknown> | undefined;
export function setPluginFunctionsHook(hook: UsePluginFunctions<unknown>): void {
// We allow overriding the registry in tests
if (singleton && process.env.NODE_ENV !== 'test') {
throw new Error('setUsePluginFunctionsHook() function should only be called once, when Grafana is starting.');
}
singleton = hook;
}
export function usePluginFunctions<T>(options: UsePluginFunctionsOptions): UsePluginFunctionsResult<T> {
if (!singleton) {
throw new Error('usePluginFunctions(options) can only be used after the Grafana instance has started.');
}
return singleton(options) as UsePluginFunctionsResult<T>;
}