mirror of
https://github.com/grafana/grafana.git
synced 2025-07-31 03:42:39 +08:00

* 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>
83 lines
3.0 KiB
TypeScript
83 lines
3.0 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { useObservable } from 'react-use';
|
|
|
|
import { usePluginContext, PluginExtensionFunction, PluginExtensionTypes } from '@grafana/data';
|
|
import { UsePluginFunctionsOptions, UsePluginFunctionsResult } from '@grafana/runtime';
|
|
|
|
import { useAddedFunctionsRegistry } from './ExtensionRegistriesContext';
|
|
import * as errors from './errors';
|
|
import { log } from './logs/log';
|
|
import { useLoadAppPlugins } from './useLoadAppPlugins';
|
|
import { generateExtensionId, getExtensionPointPluginDependencies, isGrafanaDevMode } from './utils';
|
|
import { isExtensionPointIdValid, isExtensionPointMetaInfoMissing } from './validators';
|
|
|
|
// Returns an array of component extensions for the given extension point
|
|
export function usePluginFunctions<Signature>({
|
|
limitPerPlugin,
|
|
extensionPointId,
|
|
}: UsePluginFunctionsOptions): UsePluginFunctionsResult<Signature> {
|
|
const registry = useAddedFunctionsRegistry();
|
|
const registryState = useObservable(registry.asObservable());
|
|
const pluginContext = usePluginContext();
|
|
const deps = getExtensionPointPluginDependencies(extensionPointId);
|
|
const { isLoading: isLoadingAppPlugins } = useLoadAppPlugins(deps);
|
|
|
|
return useMemo(() => {
|
|
// For backwards compatibility we don't enable restrictions in production or when the hook is used in core Grafana.
|
|
const enableRestrictions = isGrafanaDevMode() && pluginContext;
|
|
const results: Array<PluginExtensionFunction<Signature>> = [];
|
|
const extensionsByPlugin: Record<string, number> = {};
|
|
const pluginId = pluginContext?.meta.id ?? '';
|
|
const pointLog = log.child({
|
|
pluginId,
|
|
extensionPointId,
|
|
});
|
|
if (enableRestrictions && !isExtensionPointIdValid({ extensionPointId, pluginId })) {
|
|
pointLog.error(errors.INVALID_EXTENSION_POINT_ID);
|
|
}
|
|
|
|
if (enableRestrictions && isExtensionPointMetaInfoMissing(extensionPointId, pluginContext)) {
|
|
pointLog.error(errors.EXTENSION_POINT_META_INFO_MISSING);
|
|
return {
|
|
isLoading: false,
|
|
functions: [],
|
|
};
|
|
}
|
|
|
|
if (isLoadingAppPlugins) {
|
|
return {
|
|
isLoading: true,
|
|
functions: [],
|
|
};
|
|
}
|
|
|
|
for (const registryItem of registryState?.[extensionPointId] ?? []) {
|
|
const { pluginId } = registryItem;
|
|
|
|
// Only limit if the `limitPerPlugin` is set
|
|
if (limitPerPlugin && extensionsByPlugin[pluginId] >= limitPerPlugin) {
|
|
continue;
|
|
}
|
|
|
|
if (extensionsByPlugin[pluginId] === undefined) {
|
|
extensionsByPlugin[pluginId] = 0;
|
|
}
|
|
|
|
results.push({
|
|
id: generateExtensionId(pluginId, extensionPointId, registryItem.title),
|
|
type: PluginExtensionTypes.function,
|
|
title: registryItem.title,
|
|
description: registryItem.description ?? '',
|
|
pluginId: pluginId,
|
|
fn: registryItem.fn as Signature,
|
|
});
|
|
extensionsByPlugin[pluginId] += 1;
|
|
}
|
|
|
|
return {
|
|
isLoading: false,
|
|
functions: results,
|
|
};
|
|
}, [extensionPointId, limitPerPlugin, pluginContext, registryState, isLoadingAppPlugins]);
|
|
}
|