mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Plugins: Pass hashes for SRI to frontend
* Add SRI hashes to frontendsettings DTOs
* Add docstring
* TestSriHashes
* Fix typo
* Changed SriHashes to ModuleHash
* update loader_test compareOpts
* update ModuleHash error message
* Add TestModuleHash/no_module.js
* Add omitEmpty to moduleHash
* Add ModuleHash to api/plugins/${pluginId}/settings
* moved ModuleHash field
* feat(plugins): add moduleHash to bootData and plugin types
* feat(plugins): if moduleHash is available apply it to systemjs importmap
* Calculate ModuleHash for CDN provisioned plugins
* Add ModuleHash tests for TestCalculate
* adjust test case name
* removed .envrc
* Fix signature verification failing for internal plugins
* fix tests
* Add pluginsFilesystemSriChecks feature togglemk
* renamed FilesystemSriChecksEnabled
* refactor(plugin_loader): prefer extending type declaration over ts-error
* added a couple more tests
* Removed unused features
* Removed unused argument from signature.DefaultCalculator call
* Removed unused argument from bootstrap.DefaultConstructFunc
* Moved ModuleHash to pluginassets service
* update docstring
* lint
* Removed cdn dependency from manifest.Signature
* add tests
* fix extra parameters in tests
* "fix" tests
* removed outdated test
* removed unused cdn dependency in signature.DefaultCalculator
* reduce diff
* Cache returned values
* Add support for deeply nested plugins (more than 1 hierarchy level)
* simplify cache usage
* refactor TestService_ModuleHash_Cache
* removed unused testdata
* re-generate feature toggles
* use version for module hash cache
* Renamed feature toggle to pluginsSriChecks and use it for both cdn and filesystem
* Removed app/types/system-integrity.d.ts
* re-generate feature toggles
* re-generate feature toggles
* feat(plugins): put systemjs integrity hash behind feature flag
---------
Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
100 lines
2.8 KiB
TypeScript
100 lines
2.8 KiB
TypeScript
import { ComponentType } from 'react';
|
|
|
|
import { PanelPlugin, PanelPluginMeta, PanelProps, PluginLoadingStrategy } from '@grafana/data';
|
|
import config from 'app/core/config';
|
|
|
|
import { getPanelPluginLoadError } from '../panel/components/PanelPluginError';
|
|
|
|
import { importPluginModule } from './plugin_loader';
|
|
|
|
const promiseCache: Record<string, Promise<PanelPlugin>> = {};
|
|
const panelPluginCache: Record<string, PanelPlugin> = {};
|
|
|
|
export function importPanelPlugin(id: string): Promise<PanelPlugin> {
|
|
const loaded = promiseCache[id];
|
|
if (loaded) {
|
|
return loaded;
|
|
}
|
|
|
|
const meta = getPanelPluginMeta(id);
|
|
|
|
if (!meta) {
|
|
throw new Error(`Plugin ${id} not found`);
|
|
}
|
|
|
|
promiseCache[id] = getPanelPlugin(meta);
|
|
if (id !== meta.type) {
|
|
promiseCache[meta.type] = promiseCache[id];
|
|
}
|
|
|
|
return promiseCache[id];
|
|
}
|
|
|
|
export function hasPanelPlugin(id: string): boolean {
|
|
return !!getPanelPluginMeta(id);
|
|
}
|
|
|
|
export function getPanelPluginMeta(id: string): PanelPluginMeta {
|
|
const v = config.panels[id];
|
|
if (!v) {
|
|
// Check alias values before failing
|
|
for (const p of Object.values(config.panels)) {
|
|
if (p.aliasIDs?.includes(id)) {
|
|
return p;
|
|
}
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
|
|
export function importPanelPluginFromMeta(meta: PanelPluginMeta): Promise<PanelPlugin> {
|
|
return getPanelPlugin(meta);
|
|
}
|
|
|
|
export function syncGetPanelPlugin(id: string): PanelPlugin | undefined {
|
|
return panelPluginCache[id];
|
|
}
|
|
|
|
function getPanelPlugin(meta: PanelPluginMeta): Promise<PanelPlugin> {
|
|
const fallbackLoadingStrategy = meta.loadingStrategy ?? PluginLoadingStrategy.fetch;
|
|
return importPluginModule({
|
|
path: meta.module,
|
|
version: meta.info?.version,
|
|
isAngular: meta.angular?.detected,
|
|
loadingStrategy: fallbackLoadingStrategy,
|
|
pluginId: meta.id,
|
|
moduleHash: meta.moduleHash,
|
|
})
|
|
.then((pluginExports) => {
|
|
if (pluginExports.plugin) {
|
|
return pluginExports.plugin;
|
|
} else if (pluginExports.PanelCtrl) {
|
|
const plugin = new PanelPlugin(null);
|
|
plugin.angularPanelCtrl = pluginExports.PanelCtrl;
|
|
return plugin;
|
|
}
|
|
throw new Error('missing export: plugin or PanelCtrl');
|
|
})
|
|
.then((plugin: PanelPlugin) => {
|
|
plugin.meta = meta;
|
|
panelPluginCache[meta.id] = plugin;
|
|
|
|
if (!plugin.panel && plugin.angularPanelCtrl) {
|
|
plugin.panel = getAngularPanelReactWrapper(plugin);
|
|
}
|
|
|
|
return plugin;
|
|
})
|
|
.catch((err) => {
|
|
// TODO, maybe a different error plugin
|
|
console.warn('Error loading panel plugin: ' + meta.id, err);
|
|
return getPanelPluginLoadError(meta, err);
|
|
});
|
|
}
|
|
|
|
let getAngularPanelReactWrapper = (plugin: PanelPlugin): ComponentType<PanelProps> | null => null;
|
|
|
|
export function setAngularPanelReactWrapper(wrapper: typeof getAngularPanelReactWrapper) {
|
|
getAngularPanelReactWrapper = wrapper;
|
|
}
|