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>
82 lines
3.0 KiB
TypeScript
82 lines
3.0 KiB
TypeScript
import type { PluginExtensionAddedLinkConfig, PluginExtensionExposedComponentConfig } from '@grafana/data';
|
|
import { PluginExtensionAddedComponentConfig } from '@grafana/data/src/types/pluginExtensions';
|
|
import type { AppPluginConfig } from '@grafana/runtime';
|
|
import { startMeasure, stopMeasure } from 'app/core/utils/metrics';
|
|
import { getPluginSettings } from 'app/features/plugins/pluginSettings';
|
|
|
|
import { PluginExtensionRegistries } from './extensions/registry/types';
|
|
import { importPluginModule } from './plugin_loader';
|
|
|
|
export type PluginPreloadResult = {
|
|
pluginId: string;
|
|
error?: unknown;
|
|
exposedComponentConfigs: PluginExtensionExposedComponentConfig[];
|
|
addedComponentConfigs?: PluginExtensionAddedComponentConfig[];
|
|
addedLinkConfigs?: PluginExtensionAddedLinkConfig[];
|
|
};
|
|
|
|
export async function preloadPlugins(
|
|
apps: AppPluginConfig[] = [],
|
|
registries: PluginExtensionRegistries,
|
|
eventName = 'frontend_plugins_preload'
|
|
) {
|
|
startMeasure(eventName);
|
|
const promises = apps.filter((config) => config.preload).map((config) => preload(config));
|
|
const preloadedPlugins = await Promise.all(promises);
|
|
|
|
for (const preloadedPlugin of preloadedPlugins) {
|
|
if (preloadedPlugin.error) {
|
|
console.error(`[Plugins] Skip loading extensions for "${preloadedPlugin.pluginId}" due to an error.`);
|
|
continue;
|
|
}
|
|
|
|
registries.exposedComponentsRegistry.register({
|
|
pluginId: preloadedPlugin.pluginId,
|
|
configs: preloadedPlugin.exposedComponentConfigs,
|
|
});
|
|
registries.addedComponentsRegistry.register({
|
|
pluginId: preloadedPlugin.pluginId,
|
|
configs: preloadedPlugin.addedComponentConfigs || [],
|
|
});
|
|
registries.addedLinksRegistry.register({
|
|
pluginId: preloadedPlugin.pluginId,
|
|
configs: preloadedPlugin.addedLinkConfigs || [],
|
|
});
|
|
}
|
|
|
|
stopMeasure(eventName);
|
|
}
|
|
|
|
async function preload(config: AppPluginConfig): Promise<PluginPreloadResult> {
|
|
const { path, version, id: pluginId, loadingStrategy } = config;
|
|
try {
|
|
startMeasure(`frontend_plugin_preload_${pluginId}`);
|
|
const { plugin } = await importPluginModule({
|
|
path,
|
|
version,
|
|
isAngular: config.angular.detected,
|
|
pluginId,
|
|
loadingStrategy,
|
|
moduleHash: config.moduleHash,
|
|
});
|
|
const { exposedComponentConfigs = [], addedComponentConfigs = [], addedLinkConfigs = [] } = plugin;
|
|
|
|
// Fetching meta-information for the preloaded app plugin and caching it for later.
|
|
// (The function below returns a promise, but it's not awaited for a reason: we don't want to block the preload process, we would only like to cache the result for later.)
|
|
getPluginSettings(pluginId);
|
|
|
|
return { pluginId, exposedComponentConfigs, addedComponentConfigs, addedLinkConfigs };
|
|
} catch (error) {
|
|
console.error(`[Plugins] Failed to preload plugin: ${path} (version: ${version})`, error);
|
|
return {
|
|
pluginId,
|
|
error,
|
|
exposedComponentConfigs: [],
|
|
addedComponentConfigs: [],
|
|
addedLinkConfigs: [],
|
|
};
|
|
} finally {
|
|
stopMeasure(`frontend_plugin_preload_${pluginId}`);
|
|
}
|
|
}
|