156 lines
5.5 KiB
TypeScript
156 lines
5.5 KiB
TypeScript
import { isString } from 'lodash';
|
|
|
|
import {
|
|
PluginExtensionTypes,
|
|
type PluginExtension,
|
|
type PluginExtensionLink,
|
|
type PluginExtensionComponent,
|
|
} from '@grafana/data';
|
|
import { GetPluginExtensions } from '@grafana/runtime';
|
|
|
|
import { log } from './logs/log';
|
|
import { AddedComponentRegistryItem } from './registry/AddedComponentsRegistry';
|
|
import { AddedLinkRegistryItem } from './registry/AddedLinksRegistry';
|
|
import { RegistryType } from './registry/Registry';
|
|
import type { PluginExtensionRegistries } from './registry/types';
|
|
import {
|
|
getReadOnlyProxy,
|
|
generateExtensionId,
|
|
wrapWithPluginContext,
|
|
getLinkExtensionOnClick,
|
|
getLinkExtensionOverrides,
|
|
getLinkExtensionPathWithTracking,
|
|
} from './utils';
|
|
|
|
type GetExtensions = ({
|
|
context,
|
|
extensionPointId,
|
|
limitPerPlugin,
|
|
addedLinksRegistry,
|
|
addedComponentsRegistry,
|
|
}: {
|
|
context?: object | Record<string | symbol, unknown>;
|
|
extensionPointId: string;
|
|
limitPerPlugin?: number;
|
|
addedComponentsRegistry: RegistryType<AddedComponentRegistryItem[]> | undefined;
|
|
addedLinksRegistry: RegistryType<AddedLinkRegistryItem[]> | undefined;
|
|
}) => { extensions: PluginExtension[] };
|
|
|
|
export function createPluginExtensionsGetter(registries: PluginExtensionRegistries): GetPluginExtensions {
|
|
let addedComponentsRegistry: RegistryType<AddedComponentRegistryItem[]>;
|
|
let addedLinksRegistry: RegistryType<Array<AddedLinkRegistryItem<object>>>;
|
|
|
|
// Create registry subscriptions to keep an copy of the registry state for use in the non-async
|
|
// plugin extensions getter.
|
|
registries.addedComponentsRegistry.asObservable().subscribe((componentsRegistry) => {
|
|
addedComponentsRegistry = componentsRegistry;
|
|
});
|
|
|
|
registries.addedLinksRegistry.asObservable().subscribe((linksRegistry) => {
|
|
addedLinksRegistry = linksRegistry;
|
|
});
|
|
|
|
return (options) => getPluginExtensions({ ...options, addedComponentsRegistry, addedLinksRegistry });
|
|
}
|
|
|
|
// Returns with a list of plugin extensions for the given extension point
|
|
export const getPluginExtensions: GetExtensions = ({
|
|
context,
|
|
extensionPointId,
|
|
limitPerPlugin,
|
|
addedLinksRegistry,
|
|
addedComponentsRegistry,
|
|
}) => {
|
|
const frozenContext = context ? getReadOnlyProxy(context) : {};
|
|
// 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[] = [];
|
|
const extensionsByPlugin: Record<string, number> = {};
|
|
|
|
for (const addedLink of addedLinksRegistry?.[extensionPointId] ?? []) {
|
|
try {
|
|
const { pluginId } = addedLink;
|
|
// Only limit if the `limitPerPlugin` is set
|
|
if (limitPerPlugin && extensionsByPlugin[pluginId] >= limitPerPlugin) {
|
|
continue;
|
|
}
|
|
|
|
if (extensionsByPlugin[pluginId] === undefined) {
|
|
extensionsByPlugin[pluginId] = 0;
|
|
}
|
|
|
|
const linkLog = log.child({
|
|
pluginId,
|
|
extensionPointId,
|
|
path: addedLink.path ?? '',
|
|
title: addedLink.title,
|
|
description: addedLink.description ?? '',
|
|
onClick: typeof addedLink.onClick,
|
|
});
|
|
// Run the configure() function with the current context, and apply the ovverides
|
|
const overrides = getLinkExtensionOverrides(pluginId, addedLink, linkLog, frozenContext);
|
|
|
|
// configure() returned an `undefined` -> hide the extension
|
|
if (addedLink.configure && overrides === undefined) {
|
|
continue;
|
|
}
|
|
|
|
const path = overrides?.path || addedLink.path;
|
|
const extension: PluginExtensionLink = {
|
|
id: generateExtensionId(pluginId, extensionPointId, addedLink.title),
|
|
type: PluginExtensionTypes.link,
|
|
pluginId: pluginId,
|
|
onClick: getLinkExtensionOnClick(pluginId, extensionPointId, addedLink, linkLog, frozenContext),
|
|
|
|
// Configurable properties
|
|
icon: overrides?.icon || addedLink.icon,
|
|
title: overrides?.title || addedLink.title,
|
|
description: overrides?.description || addedLink.description || '',
|
|
path: isString(path) ? getLinkExtensionPathWithTracking(pluginId, path, extensionPointId) : undefined,
|
|
category: overrides?.category || addedLink.category,
|
|
};
|
|
|
|
extensions.push(extension);
|
|
extensionsByPlugin[pluginId] += 1;
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
log.error(error.message, {
|
|
stack: error.stack ?? '',
|
|
message: error.message,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
const addedComponents = addedComponentsRegistry?.[extensionPointId] ?? [];
|
|
for (const addedComponent of addedComponents) {
|
|
// Only limit if the `limitPerPlugin` is set
|
|
if (limitPerPlugin && extensionsByPlugin[addedComponent.pluginId] >= limitPerPlugin) {
|
|
continue;
|
|
}
|
|
|
|
if (extensionsByPlugin[addedComponent.pluginId] === undefined) {
|
|
extensionsByPlugin[addedComponent.pluginId] = 0;
|
|
}
|
|
|
|
const componentLog = log.child({
|
|
title: addedComponent.title,
|
|
description: addedComponent.description ?? '',
|
|
pluginId: addedComponent.pluginId,
|
|
});
|
|
|
|
const extension: PluginExtensionComponent = {
|
|
id: generateExtensionId(addedComponent.pluginId, extensionPointId, addedComponent.title),
|
|
type: PluginExtensionTypes.component,
|
|
pluginId: addedComponent.pluginId,
|
|
title: addedComponent.title,
|
|
description: addedComponent.description ?? '',
|
|
component: wrapWithPluginContext(addedComponent.pluginId, addedComponent.component, componentLog),
|
|
};
|
|
|
|
extensions.push(extension);
|
|
extensionsByPlugin[addedComponent.pluginId] += 1;
|
|
}
|
|
|
|
return { extensions };
|
|
};
|