2024-08-30 16:09:01 +08:00
import { useMemo } from 'react' ;
2024-04-24 15:33:16 +08:00
import { useObservable } from 'react-use' ;
2024-10-04 14:41:26 +08:00
import { PluginExtension , usePluginContext } from '@grafana/data' ;
2024-04-24 15:33:16 +08:00
import { GetPluginExtensionsOptions , UsePluginExtensionsResult } from '@grafana/runtime' ;
2024-09-09 20:45:05 +08:00
import { useSidecar } from 'app/core/context/SidecarContext' ;
2024-04-24 15:33:16 +08:00
import { getPluginExtensions } from './getPluginExtensions' ;
2024-08-30 16:09:01 +08:00
import { PluginExtensionRegistries } from './registry/types' ;
2024-10-04 14:41:26 +08:00
import { isExtensionPointMetaInfoMissing , isGrafanaDevMode , logWarning } from './utils' ;
import { isExtensionPointIdValid } from './validators' ;
2024-04-24 15:33:16 +08:00
2024-08-30 16:09:01 +08:00
export function createUsePluginExtensions ( registries : PluginExtensionRegistries ) {
const observableAddedComponentsRegistry = registries . addedComponentsRegistry . asObservable ( ) ;
const observableAddedLinksRegistry = registries . addedLinksRegistry . asObservable ( ) ;
2024-04-24 15:33:16 +08:00
return function usePluginExtensions ( options : GetPluginExtensionsOptions ) : UsePluginExtensionsResult < PluginExtension > {
2024-10-04 14:41:26 +08:00
const pluginContext = usePluginContext ( ) ;
2024-08-30 16:09:01 +08:00
const addedComponentsRegistry = useObservable ( observableAddedComponentsRegistry ) ;
const addedLinksRegistry = useObservable ( observableAddedLinksRegistry ) ;
2024-09-09 20:45:05 +08:00
const { activePluginId } = useSidecar ( ) ;
2024-10-04 14:41:26 +08:00
const { extensionPointId , context , limitPerPlugin } = options ;
2024-08-30 16:09:01 +08:00
const { extensions } = useMemo ( ( ) = > {
2024-10-04 14:41:26 +08:00
// For backwards compatibility we don't enable restrictions in production or when the hook is used in core Grafana.
const enableRestrictions = isGrafanaDevMode ( ) && pluginContext !== null ;
const pluginId = pluginContext ? . meta . id ? ? '' ;
2024-08-30 16:09:01 +08:00
if ( ! addedLinksRegistry && ! addedComponentsRegistry ) {
return { extensions : [ ] , isLoading : false } ;
}
2024-10-04 14:41:26 +08:00
if ( enableRestrictions && ! isExtensionPointIdValid ( { extensionPointId , pluginId } ) ) {
logWarning (
` Extension point usePluginExtensions(" ${ extensionPointId } ") - the id should be prefixed with your plugin id (" ${ pluginId } /"). `
) ;
return {
isLoading : false ,
extensions : [ ] ,
} ;
}
if ( enableRestrictions && isExtensionPointMetaInfoMissing ( extensionPointId , pluginContext ) ) {
logWarning (
` Invalid extension point. Reason: The extension point is not declared in the "plugin.json" file. ExtensionPointId: " ${ extensionPointId } " `
) ;
return {
isLoading : false ,
extensions : [ ] ,
} ;
}
2024-08-30 16:09:01 +08:00
return getPluginExtensions ( {
2024-10-04 14:41:26 +08:00
extensionPointId ,
context ,
limitPerPlugin ,
2024-08-30 16:09:01 +08:00
addedComponentsRegistry ,
addedLinksRegistry ,
} ) ;
2024-09-09 20:45:05 +08:00
// Doing the deps like this instead of just `option` because users probably aren't going to memoize the
// options object so we are checking it's simple value attributes.
// The context though still has to be memoized though and not mutated.
// eslint-disable-next-line react-hooks/exhaustive-deps -- TODO: refactor `getPluginExtensions` to accept service dependencies as arguments instead of relying on the sidecar singleton under the hood
2024-08-30 16:09:01 +08:00
} , [
addedLinksRegistry ,
2024-08-27 17:14:04 +08:00
addedComponentsRegistry ,
2024-10-04 14:41:26 +08:00
extensionPointId ,
context ,
limitPerPlugin ,
2024-09-09 20:45:05 +08:00
activePluginId ,
2024-10-04 14:41:26 +08:00
pluginContext ,
2024-08-30 16:09:01 +08:00
] ) ;
2024-04-24 15:33:16 +08:00
2024-08-30 16:09:01 +08:00
return { extensions , isLoading : false } ;
2024-04-24 15:33:16 +08:00
} ;
}