bootdata: decouples config.apps
This commit is contained in:
@@ -652,6 +652,7 @@ i18next.config.ts @grafana/grafana-frontend-platform
|
||||
/packages/grafana-runtime/src/components/QueryEditorWithMigration* @grafana/plugins-platform-frontend @grafana/plugins-platform-backend
|
||||
/packages/grafana-runtime/src/config.ts @grafana/grafana-frontend-platform
|
||||
/packages/grafana-runtime/src/services/ @grafana/grafana-frontend-platform
|
||||
/packages/grafana-runtime/src/services/plugins.ts @grafana/plugins-platform-frontend
|
||||
/packages/grafana-runtime/src/services/pluginExtensions @grafana/plugins-platform-frontend
|
||||
/packages/grafana-runtime/src/services/CorrelationsService.ts @grafana/datapro
|
||||
/packages/grafana-runtime/src/services/LocationService.test.tsx @grafana/grafana-search-navigate-organise
|
||||
|
||||
@@ -575,6 +575,42 @@ module.exports = [
|
||||
"Property[key.name='a11y'][value.type='ObjectExpression'] Property[key.name='test'][value.value='off']",
|
||||
message: 'Skipping a11y tests is not allowed. Please fix the component or story instead.',
|
||||
},
|
||||
{
|
||||
selector: 'MemberExpression[object.name="config"][property.name="apps"]',
|
||||
message:
|
||||
'Usage of config.apps is not allowed. Use the function getAppPluginMetas() from @grafana/runtime instead',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [...commonTestIgnores],
|
||||
ignores: [
|
||||
// FIXME: Remove once all enterprise issues are fixed -
|
||||
// we don't have a suppressions file/approach for enterprise code yet
|
||||
...enterpriseIgnores,
|
||||
],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'MemberExpression[object.name="config"][property.name="apps"]',
|
||||
message:
|
||||
'Usage of config.apps is not allowed. Use the function getAppPluginMetas() from @grafana/runtime instead',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [...enterpriseIgnores],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'MemberExpression[object.name="config"][property.name="apps"]',
|
||||
message:
|
||||
'Usage of config.apps is not allowed. Use the function getAppPluginMetas() from @grafana/runtime instead',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -69,3 +69,4 @@ export {
|
||||
getCorrelationsService,
|
||||
setCorrelationsService,
|
||||
} from './services/CorrelationsService';
|
||||
export { getAppPluginMetas, getAppPluginMeta, type AppPluginMetas } from './services/plugins';
|
||||
|
||||
@@ -29,3 +29,4 @@ export {
|
||||
export { UserStorage } from '../utils/userStorage';
|
||||
|
||||
export { initOpenFeature, evaluateBooleanFlag } from './openFeature';
|
||||
export { initPluginMetas, setAppPluginMetas } from '../services/plugins';
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
import { AppPluginConfig } from '@grafana/data';
|
||||
|
||||
import { config } from '../config';
|
||||
|
||||
export type AppPluginMetas = Record<string, AppPluginConfig>;
|
||||
|
||||
let apps: AppPluginMetas = {};
|
||||
|
||||
export async function initPluginMetas(): Promise<void> {
|
||||
if (config.featureToggles.useMTPlugins) {
|
||||
// add loading app configs from MT API here
|
||||
apps = {};
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
apps = config.apps;
|
||||
}
|
||||
|
||||
export function getAppPluginMetas(): AppPluginMetas {
|
||||
return cloneDeep(apps);
|
||||
}
|
||||
|
||||
export function getAppPluginMeta(id: string): AppPluginConfig {
|
||||
return getAppPluginMetas()[id];
|
||||
}
|
||||
|
||||
export function setAppPluginMetas(override: AppPluginMetas) {
|
||||
// We allow overriding apps in tests
|
||||
if (override && process.env.NODE_ENV !== 'test') {
|
||||
throw new Error('setAppPluginMetas() function can only be called from tests.');
|
||||
}
|
||||
|
||||
apps = { ...override };
|
||||
}
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
} from '@grafana/runtime';
|
||||
import {
|
||||
initOpenFeature,
|
||||
initPluginMetas,
|
||||
setGetObservablePluginComponents,
|
||||
setGetObservablePluginLinks,
|
||||
setPanelDataErrorView,
|
||||
@@ -176,6 +177,13 @@ export class GrafanaApp {
|
||||
// This needs to be done after the `initEchoSrv` since it is being used under the hood.
|
||||
startMeasure('frontend_app_init');
|
||||
|
||||
try {
|
||||
startMeasure('frontend_app_init_plugins');
|
||||
await initPluginMetas();
|
||||
} finally {
|
||||
stopMeasure('frontend_app_init_plugins');
|
||||
}
|
||||
|
||||
setLocale(config.regionalFormat);
|
||||
setWeekStart(contextSrv.user.weekStart);
|
||||
setPanelRenderer(PanelRenderer);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { pluginMeta, pluginMetaToPluginConfig } from '../testSetup/plugins';
|
||||
import { SupportedPlugin } from '../types/pluginBridges';
|
||||
@@ -7,7 +7,7 @@ import { getProxyApiUrl } from './onCallApi';
|
||||
|
||||
describe('getProxyApiUrl', () => {
|
||||
it('should return URL with IRM plugin ID when IRM plugin is present', () => {
|
||||
config.apps = { [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) };
|
||||
setAppPluginMetas({ [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) });
|
||||
|
||||
expect(getProxyApiUrl('/alert_receive_channels/')).toBe(
|
||||
'/api/plugins/grafana-irm-app/resources/alert_receive_channels/'
|
||||
@@ -15,10 +15,10 @@ describe('getProxyApiUrl', () => {
|
||||
});
|
||||
|
||||
it('should return URL with OnCall plugin ID when IRM plugin is not present', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
[SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]),
|
||||
[SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]),
|
||||
};
|
||||
});
|
||||
|
||||
expect(getProxyApiUrl('/alert_receive_channels/')).toBe(
|
||||
'/api/plugins/grafana-oncall-app/resources/alert_receive_channels/'
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
import { RulerRulesConfigDTO } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { pluginMeta, pluginMetaToPluginConfig } from '../../testSetup/plugins';
|
||||
@@ -67,7 +67,7 @@ describe('filterRulerRulesConfig', () => {
|
||||
};
|
||||
|
||||
it('should filter by namespace', () => {
|
||||
config.apps = { [SupportedPlugin.Slo]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Slo]) };
|
||||
setAppPluginMetas({ [SupportedPlugin.Slo]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Slo]) });
|
||||
const { filteredConfig, someRulesAreSkipped } = filterRulerRulesConfig(mockRulesConfig, 'namespace1');
|
||||
|
||||
expect(filteredConfig).toEqual({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type DefaultBodyType, HttpResponse, HttpResponseResolver, PathParams, http } from 'msw';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMetas } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
import server from '@grafana/test-utils/server';
|
||||
import { mockDataSource, mockFolder } from 'app/features/alerting/unified/mocks';
|
||||
import {
|
||||
@@ -214,7 +215,9 @@ export function setGrafanaPromRules(groups: GrafanaPromRuleGroupDTO[]) {
|
||||
|
||||
/** Make a given plugin ID respond with a 404, as if it isn't installed at all */
|
||||
export const removePlugin = (pluginId: string) => {
|
||||
delete config.apps[pluginId];
|
||||
const apps = getAppPluginMetas();
|
||||
delete apps[pluginId];
|
||||
setAppPluginMetas(apps);
|
||||
server.use(getPluginMissingHandler(pluginId));
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { HttpResponse, http } from 'msw';
|
||||
|
||||
import { PluginLoadingStrategy, PluginMeta } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMetas } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
import { plugins } from 'app/features/alerting/unified/testSetup/plugins';
|
||||
|
||||
const PLUGIN_NOT_FOUND_RESPONSE = { message: 'Plugin not found, no installed plugin with that id' };
|
||||
@@ -12,28 +13,32 @@ const PLUGIN_NOT_FOUND_RESPONSE = { message: 'Plugin not found, no installed plu
|
||||
*/
|
||||
export const getPluginsHandler = (pluginsArray: PluginMeta[] = plugins) => {
|
||||
plugins.forEach(({ id, baseUrl, info, angular }) => {
|
||||
config.apps[id] = {
|
||||
id,
|
||||
path: baseUrl,
|
||||
preload: true,
|
||||
version: info.version,
|
||||
angular: angular ?? { detected: false, hideDeprecation: false },
|
||||
loadingStrategy: PluginLoadingStrategy.script,
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
extensionPoints: [],
|
||||
exposedComponents: [],
|
||||
addedFunctions: [],
|
||||
},
|
||||
dependencies: {
|
||||
grafanaVersion: '',
|
||||
plugins: [],
|
||||
const apps = getAppPluginMetas();
|
||||
setAppPluginMetas({
|
||||
...apps,
|
||||
[id]: {
|
||||
id,
|
||||
path: baseUrl,
|
||||
preload: true,
|
||||
version: info.version,
|
||||
angular: angular ?? { detected: false, hideDeprecation: false },
|
||||
loadingStrategy: PluginLoadingStrategy.script,
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
extensionPoints: [],
|
||||
exposedComponents: [],
|
||||
addedFunctions: [],
|
||||
},
|
||||
dependencies: {
|
||||
grafanaVersion: '',
|
||||
plugins: [],
|
||||
extensions: {
|
||||
exposedComponents: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return http.get<{ pluginId: string }>(`/api/plugins/:pluginId/settings`, ({ params: { pluginId } }) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
import { RuleWithLocation } from 'app/types/unified-alerting';
|
||||
import {
|
||||
RulerAlertingRuleDTO,
|
||||
@@ -137,9 +137,7 @@ describe('cloneRuleDefinition', () => {
|
||||
|
||||
it('Should remove the origin label when cloning data source plugin-provided rules', () => {
|
||||
// Mock the plugin as installed
|
||||
config.apps = {
|
||||
[SupportedPlugin.Slo]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Slo]),
|
||||
};
|
||||
setAppPluginMetas({ [SupportedPlugin.Slo]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Slo]) });
|
||||
|
||||
const rule: RulerAlertingRuleDTO = mockRulerAlertingRule({
|
||||
alert: 'slo-provider-alert',
|
||||
@@ -174,9 +172,7 @@ describe('cloneRuleDefinition', () => {
|
||||
});
|
||||
|
||||
it('Should remove the origin label when cloning Grafana-managed plugin-provided rules', () => {
|
||||
config.apps = {
|
||||
[SupportedPlugin.Slo]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Slo]),
|
||||
};
|
||||
setAppPluginMetas({ [SupportedPlugin.Slo]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Slo]) });
|
||||
|
||||
const rule: RulerGrafanaRuleDTO = mockRulerGrafanaRule(
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { config } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { pluginMeta, pluginMetaToPluginConfig } from '../testSetup/plugins';
|
||||
import { SupportedPlugin } from '../types/pluginBridges';
|
||||
@@ -62,45 +63,45 @@ describe('checkEvaluationIntervalGlobalLimit', () => {
|
||||
|
||||
describe('getIsIrmPluginPresent', () => {
|
||||
it('should return true when IRM plugin is present in config.apps', () => {
|
||||
config.apps = { [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) };
|
||||
setAppPluginMetas({ [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) });
|
||||
expect(getIsIrmPluginPresent()).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when IRM plugin is not present in config.apps', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
[SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]),
|
||||
[SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]),
|
||||
};
|
||||
});
|
||||
expect(getIsIrmPluginPresent()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getIrmIfPresentOrIncidentPluginId', () => {
|
||||
it('should return IRM plugin ID when IRM plugin is present', () => {
|
||||
config.apps = { [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) };
|
||||
setAppPluginMetas({ [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) });
|
||||
expect(getIrmIfPresentOrIncidentPluginId()).toBe(SupportedPlugin.Irm);
|
||||
});
|
||||
|
||||
it('should return Incident plugin ID when IRM plugin is not present', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
[SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]),
|
||||
[SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]),
|
||||
};
|
||||
});
|
||||
expect(getIrmIfPresentOrIncidentPluginId()).toBe(SupportedPlugin.Incident);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getIrmIfPresentOrOnCallPluginId', () => {
|
||||
it('should return IRM plugin ID when IRM plugin is present', () => {
|
||||
config.apps = { [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) };
|
||||
setAppPluginMetas({ [SupportedPlugin.Irm]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Irm]) });
|
||||
expect(getIrmIfPresentOrOnCallPluginId()).toBe(SupportedPlugin.Irm);
|
||||
});
|
||||
|
||||
it('should return OnCall plugin ID when IRM plugin is not present', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
[SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]),
|
||||
[SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]),
|
||||
};
|
||||
});
|
||||
expect(getIrmIfPresentOrOnCallPluginId()).toBe(SupportedPlugin.OnCall);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DataSourceInstanceSettings, DataSourceJsonData } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { config, getAppPluginMetas } from '@grafana/runtime';
|
||||
|
||||
import { SupportedPlugin } from '../types/pluginBridges';
|
||||
|
||||
@@ -30,7 +30,7 @@ export function checkEvaluationIntervalGlobalLimit(alertGroupEvaluateEvery?: str
|
||||
}
|
||||
|
||||
export function getIsIrmPluginPresent() {
|
||||
return SupportedPlugin.Irm in config.apps;
|
||||
return SupportedPlugin.Irm in getAppPluginMetas();
|
||||
}
|
||||
|
||||
export function getIrmIfPresentOrIncidentPluginId() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PluginLoadingStrategy } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
import { RuleGroupIdentifier } from 'app/types/unified-alerting';
|
||||
|
||||
import {
|
||||
@@ -42,7 +42,7 @@ describe('getRuleOrigin', () => {
|
||||
});
|
||||
|
||||
it('returns pluginId when origin label matches expected format and plugin is installed', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
installed_plugin: {
|
||||
id: 'installed_plugin',
|
||||
version: '',
|
||||
@@ -65,7 +65,7 @@ describe('getRuleOrigin', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
const rule = mockPromAlertingRule({
|
||||
labels: { [GRAFANA_ORIGIN_LABEL]: 'plugin/installed_plugin' },
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { capitalize } from 'lodash';
|
||||
|
||||
import { AlertState } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMeta } from '@grafana/runtime';
|
||||
import {
|
||||
Alert,
|
||||
AlertingRule,
|
||||
@@ -273,7 +273,7 @@ export function getRulePluginOrigin(rule?: Rule | PromRuleDTO | RulerRuleDTO): R
|
||||
}
|
||||
|
||||
function isPluginInstalled(pluginId: string) {
|
||||
return Boolean(config.apps[pluginId]);
|
||||
return Boolean(getAppPluginMeta(pluginId));
|
||||
}
|
||||
|
||||
export function isPluginProvidedGroup(group: RulerRuleGroupDTO): boolean {
|
||||
|
||||
+25
-21
@@ -3,12 +3,14 @@ import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { PluginLoadingStrategy } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
|
||||
import { AdvisorRedirectNotice } from './AdvisorRedirectNotice';
|
||||
|
||||
const originalFeatureToggleValue = config.featureToggles.grafanaAdvisor;
|
||||
jest.mock('@grafana/runtime/internal', () => ({
|
||||
...jest.requireActual('@grafana/runtime/internal'),
|
||||
UserStorage: jest.fn().mockImplementation(() => ({
|
||||
getItem: jest.fn().mockResolvedValue('true'),
|
||||
setItem: jest.fn().mockResolvedValue(undefined),
|
||||
@@ -24,27 +26,29 @@ describe('AdvisorRedirectNotice', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
config.featureToggles.grafanaAdvisor = originalFeatureToggleValue;
|
||||
config.apps['grafana-advisor-app'] = {
|
||||
id: 'grafana-advisor-app',
|
||||
path: '/a/grafana-advisor-app',
|
||||
version: '1.0.0',
|
||||
preload: false,
|
||||
angular: { detected: false, hideDeprecation: false },
|
||||
loadingStrategy: PluginLoadingStrategy.fetch,
|
||||
dependencies: {
|
||||
grafanaDependency: '*',
|
||||
grafanaVersion: '*',
|
||||
plugins: [],
|
||||
extensions: { exposedComponents: [] },
|
||||
setAppPluginMetas({
|
||||
'grafana-advisor-app': {
|
||||
id: 'grafana-advisor-app',
|
||||
path: '/a/grafana-advisor-app',
|
||||
version: '1.0.0',
|
||||
preload: false,
|
||||
angular: { detected: false, hideDeprecation: false },
|
||||
loadingStrategy: PluginLoadingStrategy.fetch,
|
||||
dependencies: {
|
||||
grafanaDependency: '*',
|
||||
grafanaVersion: '*',
|
||||
plugins: [],
|
||||
extensions: { exposedComponents: [] },
|
||||
},
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
exposedComponents: [],
|
||||
extensionPoints: [],
|
||||
addedFunctions: [],
|
||||
},
|
||||
},
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
exposedComponents: [],
|
||||
extensionPoints: [],
|
||||
addedFunctions: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render when user is not admin', async () => {
|
||||
@@ -60,7 +64,7 @@ describe('AdvisorRedirectNotice', () => {
|
||||
});
|
||||
|
||||
it('should not render when app is not installed', async () => {
|
||||
delete config.apps['grafana-advisor-app'];
|
||||
setAppPluginMetas({});
|
||||
render(<AdvisorRedirectNotice />);
|
||||
expect(screen.queryByRole('status')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
+3
-2
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { config, getAppPluginMeta } from '@grafana/runtime';
|
||||
import { UserStorage } from '@grafana/runtime/internal';
|
||||
import { Alert, LinkButton, useStyles2 } from '@grafana/ui';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
@@ -28,7 +28,8 @@ export function AdvisorRedirectNotice() {
|
||||
const hasAdminRights = contextSrv.hasRole('Admin') || contextSrv.isGrafanaAdmin;
|
||||
const [showNotice, setShowNotice] = useState(false);
|
||||
|
||||
const canUseAdvisor = hasAdminRights && config.featureToggles.grafanaAdvisor && !!config.apps['grafana-advisor-app'];
|
||||
const canUseAdvisor =
|
||||
hasAdminRights && config.featureToggles.grafanaAdvisor && !!getAppPluginMeta('grafana-advisor-app');
|
||||
|
||||
useEffect(() => {
|
||||
if (canUseAdvisor) {
|
||||
|
||||
@@ -19,12 +19,7 @@ jest.mock('@grafana/llm', () => ({
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
config: {
|
||||
...jest.requireActual('@grafana/runtime').config,
|
||||
apps: {
|
||||
'grafana-llm-app': true,
|
||||
},
|
||||
},
|
||||
getAppPluginMeta: (id: string) => ({ [id]: {} }),
|
||||
}));
|
||||
|
||||
describe('getDashboardChanges', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { llm } from '@grafana/llm';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMeta } from '@grafana/runtime';
|
||||
import { Panel } from '@grafana/schema';
|
||||
|
||||
import { DashboardModel } from '../../state/DashboardModel';
|
||||
@@ -70,7 +70,7 @@ let llmHealthCheck: Promise<boolean> | undefined;
|
||||
* @returns true if the LLM plugin is enabled.
|
||||
*/
|
||||
export async function isLLMPluginEnabled(): Promise<boolean> {
|
||||
if (!config.apps['grafana-llm-app']) {
|
||||
if (!getAppPluginMeta('grafana-llm-app')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ import React from 'react';
|
||||
import { firstValueFrom, take } from 'rxjs';
|
||||
|
||||
import { PluginLoadingStrategy } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMeta, getAppPluginMetas } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { log } from '../logs/log';
|
||||
import { resetLogMock } from '../logs/testUtils';
|
||||
@@ -30,7 +31,7 @@ jest.mock('../logs/log', () => {
|
||||
});
|
||||
|
||||
describe('AddedComponentsRegistry', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const pluginId = 'grafana-basic-app';
|
||||
const appPluginConfig = {
|
||||
id: pluginId,
|
||||
@@ -61,13 +62,11 @@ describe('AddedComponentsRegistry', () => {
|
||||
beforeEach(() => {
|
||||
resetLogMock(log);
|
||||
jest.mocked(isGrafanaDevMode).mockReturnValue(false);
|
||||
config.apps = {
|
||||
[pluginId]: appPluginConfig,
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: appPluginConfig });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return empty registry when no extensions registered', async () => {
|
||||
@@ -450,7 +449,9 @@ describe('AddedComponentsRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedComponents = [];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedComponents: [] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
@@ -499,7 +500,9 @@ describe('AddedComponentsRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedComponents = [];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedComponents: [] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
@@ -525,7 +528,9 @@ describe('AddedComponentsRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedComponents = [componentConfig];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedComponents: [componentConfig] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { firstValueFrom, take } from 'rxjs';
|
||||
|
||||
import { PluginLoadingStrategy } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMeta, getAppPluginMetas } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { log } from '../logs/log';
|
||||
import { resetLogMock } from '../logs/testUtils';
|
||||
@@ -29,7 +30,7 @@ jest.mock('../logs/log', () => {
|
||||
});
|
||||
|
||||
describe('addedFunctionsRegistry', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const pluginId = 'grafana-basic-app';
|
||||
const appPluginConfig = {
|
||||
id: pluginId,
|
||||
@@ -60,13 +61,11 @@ describe('addedFunctionsRegistry', () => {
|
||||
beforeEach(() => {
|
||||
resetLogMock(log);
|
||||
jest.mocked(isGrafanaDevMode).mockReturnValue(false);
|
||||
config.apps = {
|
||||
[pluginId]: appPluginConfig,
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: appPluginConfig });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return empty registry when no extensions registered', async () => {
|
||||
@@ -642,7 +641,9 @@ describe('addedFunctionsRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedFunctions = [];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedFunctions: [] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
@@ -691,7 +692,9 @@ describe('addedFunctionsRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedFunctions = [];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedFunctions: [] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
@@ -717,7 +720,9 @@ describe('addedFunctionsRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedFunctions = [fnConfig];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedFunctions: [fnConfig] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { firstValueFrom, take } from 'rxjs';
|
||||
|
||||
import { PluginLoadingStrategy } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMeta, getAppPluginMetas } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { log } from '../logs/log';
|
||||
import { resetLogMock } from '../logs/testUtils';
|
||||
@@ -29,7 +30,7 @@ jest.mock('../logs/log', () => {
|
||||
});
|
||||
|
||||
describe('AddedLinksRegistry', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const pluginId = 'grafana-basic-app';
|
||||
const appPluginConfig = {
|
||||
id: pluginId,
|
||||
@@ -60,13 +61,11 @@ describe('AddedLinksRegistry', () => {
|
||||
beforeEach(() => {
|
||||
resetLogMock(log);
|
||||
jest.mocked(isGrafanaDevMode).mockReturnValue(false);
|
||||
config.apps = {
|
||||
[pluginId]: appPluginConfig,
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: appPluginConfig });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return empty registry when no extensions registered', async () => {
|
||||
@@ -624,7 +623,9 @@ describe('AddedLinksRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedLinks = [];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedLinks: [] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
@@ -675,7 +676,9 @@ describe('AddedLinksRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedLinks = [];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedLinks: [] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
@@ -702,7 +705,9 @@ describe('AddedLinksRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.addedLinks = [linkConfig];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedLinks: [linkConfig] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
|
||||
@@ -2,7 +2,8 @@ import React from 'react';
|
||||
import { firstValueFrom, take } from 'rxjs';
|
||||
|
||||
import { PluginLoadingStrategy } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMeta, getAppPluginMetas } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { log } from '../logs/log';
|
||||
import { resetLogMock } from '../logs/testUtils';
|
||||
@@ -30,7 +31,7 @@ jest.mock('../logs/log', () => {
|
||||
});
|
||||
|
||||
describe('ExposedComponentsRegistry', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const pluginId = 'grafana-basic-app';
|
||||
const appPluginConfig = {
|
||||
id: pluginId,
|
||||
@@ -61,13 +62,11 @@ describe('ExposedComponentsRegistry', () => {
|
||||
beforeEach(() => {
|
||||
resetLogMock(log);
|
||||
jest.mocked(isGrafanaDevMode).mockReturnValue(false);
|
||||
config.apps = {
|
||||
[pluginId]: appPluginConfig,
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: appPluginConfig });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return empty registry when no exposed components have been registered', async () => {
|
||||
@@ -423,7 +422,9 @@ describe('ExposedComponentsRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.exposedComponents = [];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, exposedComponents: [] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
@@ -472,7 +473,9 @@ describe('ExposedComponentsRegistry', () => {
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.exposedComponents = [];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, exposedComponents: [] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
@@ -497,8 +500,9 @@ describe('ExposedComponentsRegistry', () => {
|
||||
component: () => React.createElement('div', null, 'Hello World1'),
|
||||
};
|
||||
|
||||
// Make sure that the meta-info is empty
|
||||
config.apps[pluginId].extensions.exposedComponents = [componentConfig];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, exposedComponents: [componentConfig] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
registry.register({
|
||||
pluginId,
|
||||
|
||||
@@ -2,7 +2,8 @@ import { act, render, renderHook, screen, waitFor } from '@testing-library/react
|
||||
import type { JSX } from 'react';
|
||||
|
||||
import { PluginContextProvider, PluginLoadingStrategy, PluginMeta, PluginType } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { config, getAppPluginMetas } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext';
|
||||
import { log } from './logs/log';
|
||||
@@ -51,7 +52,7 @@ describe('usePluginComponent()', () => {
|
||||
let registries: PluginExtensionRegistries;
|
||||
let wrapper: ({ children }: { children: React.ReactNode }) => JSX.Element;
|
||||
let pluginMeta: PluginMeta;
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const pluginId = 'myorg-extensions-app';
|
||||
const exposedComponentId = `${pluginId}/exposed-component/v1`;
|
||||
const exposedComponentConfig = {
|
||||
@@ -135,9 +136,7 @@ describe('usePluginComponent()', () => {
|
||||
},
|
||||
};
|
||||
|
||||
config.apps = {
|
||||
[pluginId]: appPluginConfig,
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: appPluginConfig });
|
||||
|
||||
wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<ExtensionRegistriesProvider registries={registries}>{children}</ExtensionRegistriesProvider>
|
||||
@@ -145,7 +144,7 @@ describe('usePluginComponent()', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return null if there are no component exposed for the id', () => {
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
PluginMeta,
|
||||
PluginType,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { config, getAppPluginMeta } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext';
|
||||
import * as errors from './errors';
|
||||
@@ -115,31 +116,33 @@ describe('usePluginComponents()', () => {
|
||||
},
|
||||
};
|
||||
|
||||
config.apps[pluginId] = {
|
||||
id: pluginId,
|
||||
path: '',
|
||||
version: '',
|
||||
preload: false,
|
||||
angular: {
|
||||
detected: false,
|
||||
hideDeprecation: false,
|
||||
},
|
||||
loadingStrategy: PluginLoadingStrategy.fetch,
|
||||
dependencies: {
|
||||
grafanaVersion: '8.0.0',
|
||||
plugins: [],
|
||||
setAppPluginMetas({
|
||||
[pluginId]: {
|
||||
id: pluginId,
|
||||
path: '',
|
||||
version: '',
|
||||
preload: false,
|
||||
angular: {
|
||||
detected: false,
|
||||
hideDeprecation: false,
|
||||
},
|
||||
loadingStrategy: PluginLoadingStrategy.fetch,
|
||||
dependencies: {
|
||||
grafanaVersion: '8.0.0',
|
||||
plugins: [],
|
||||
extensions: {
|
||||
exposedComponents: [],
|
||||
},
|
||||
},
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
addedFunctions: [],
|
||||
exposedComponents: [],
|
||||
extensionPoints: [],
|
||||
},
|
||||
},
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
addedFunctions: [],
|
||||
exposedComponents: [],
|
||||
extensionPoints: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<PluginContextProvider meta={pluginMeta}>
|
||||
@@ -506,8 +509,10 @@ describe('usePluginComponents()', () => {
|
||||
component: () => <div>Component</div>,
|
||||
};
|
||||
|
||||
// The `AddedComponentsRegistry` is validating if the link is registered in the plugin metadata (config.apps).
|
||||
config.apps[pluginId].extensions.addedComponents = [componentConfig];
|
||||
// The `AddedComponentsRegistry` is validating if the link is registered in the plugin metadata.
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedComponents: [componentConfig] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<PluginContextProvider
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
PluginMeta,
|
||||
PluginType,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMeta } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext';
|
||||
import * as errors from './errors';
|
||||
@@ -107,31 +108,33 @@ describe('usePluginFunctions()', () => {
|
||||
},
|
||||
};
|
||||
|
||||
config.apps[pluginId] = {
|
||||
id: pluginId,
|
||||
path: '',
|
||||
version: '',
|
||||
preload: false,
|
||||
angular: {
|
||||
detected: false,
|
||||
hideDeprecation: false,
|
||||
},
|
||||
loadingStrategy: PluginLoadingStrategy.fetch,
|
||||
dependencies: {
|
||||
grafanaVersion: '8.0.0',
|
||||
plugins: [],
|
||||
setAppPluginMetas({
|
||||
[pluginId]: {
|
||||
id: pluginId,
|
||||
path: '',
|
||||
version: '',
|
||||
preload: false,
|
||||
angular: {
|
||||
detected: false,
|
||||
hideDeprecation: false,
|
||||
},
|
||||
loadingStrategy: PluginLoadingStrategy.fetch,
|
||||
dependencies: {
|
||||
grafanaVersion: '8.0.0',
|
||||
plugins: [],
|
||||
extensions: {
|
||||
exposedComponents: [],
|
||||
},
|
||||
},
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
addedFunctions: [],
|
||||
exposedComponents: [],
|
||||
extensionPoints: [],
|
||||
},
|
||||
},
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
addedFunctions: [],
|
||||
exposedComponents: [],
|
||||
extensionPoints: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<PluginContextProvider meta={pluginMeta}>
|
||||
@@ -328,8 +331,10 @@ describe('usePluginFunctions()', () => {
|
||||
fn: () => 'function1',
|
||||
};
|
||||
|
||||
// The `AddedFunctionsRegistry` is validating if the function is registered in the plugin metadata (config.apps).
|
||||
config.apps[pluginId].extensions.addedFunctions = [functionConfig];
|
||||
// The `AddedFunctionsRegistry` is validating if the function is registered in the plugin metadata.
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedFunctions: [functionConfig] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<PluginContextProvider
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
PluginMeta,
|
||||
PluginType,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMeta } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { ExtensionRegistriesProvider } from './ExtensionRegistriesContext';
|
||||
import * as errors from './errors';
|
||||
@@ -107,31 +108,33 @@ describe('usePluginLinks()', () => {
|
||||
},
|
||||
};
|
||||
|
||||
config.apps[pluginId] = {
|
||||
id: pluginId,
|
||||
path: '',
|
||||
version: '',
|
||||
preload: false,
|
||||
angular: {
|
||||
detected: false,
|
||||
hideDeprecation: false,
|
||||
},
|
||||
loadingStrategy: PluginLoadingStrategy.fetch,
|
||||
dependencies: {
|
||||
grafanaVersion: '8.0.0',
|
||||
plugins: [],
|
||||
setAppPluginMetas({
|
||||
[pluginId]: {
|
||||
id: pluginId,
|
||||
path: '',
|
||||
version: '',
|
||||
preload: false,
|
||||
angular: {
|
||||
detected: false,
|
||||
hideDeprecation: false,
|
||||
},
|
||||
loadingStrategy: PluginLoadingStrategy.fetch,
|
||||
dependencies: {
|
||||
grafanaVersion: '8.0.0',
|
||||
plugins: [],
|
||||
extensions: {
|
||||
exposedComponents: [],
|
||||
},
|
||||
},
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
addedFunctions: [],
|
||||
exposedComponents: [],
|
||||
extensionPoints: [],
|
||||
},
|
||||
},
|
||||
extensions: {
|
||||
addedLinks: [],
|
||||
addedComponents: [],
|
||||
addedFunctions: [],
|
||||
exposedComponents: [],
|
||||
extensionPoints: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<PluginContextProvider meta={pluginMeta}>
|
||||
@@ -266,7 +269,9 @@ describe('usePluginLinks()', () => {
|
||||
};
|
||||
|
||||
// The `AddedLinksRegistry` is validating if the link is registered in the plugin metadata (config.apps).
|
||||
config.apps[pluginId].extensions.addedLinks = [linkConfig];
|
||||
const plugin = getAppPluginMeta(pluginId);
|
||||
const config = { ...plugin, extensions: { ...plugin.extensions, addedLinks: [linkConfig] } };
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<PluginContextProvider
|
||||
|
||||
@@ -2,7 +2,8 @@ import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { type Unsubscribable } from 'rxjs';
|
||||
|
||||
import { dateTime, usePluginContext, PluginLoadingStrategy } from '@grafana/data';
|
||||
import { config, AppPluginConfig } from '@grafana/runtime';
|
||||
import { config, AppPluginConfig, getAppPluginMetas, getAppPluginMeta } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
import { appEvents } from 'app/core/app_events';
|
||||
import { ShowModalReactEvent } from 'app/types/events';
|
||||
|
||||
@@ -997,7 +998,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
});
|
||||
|
||||
describe('getAppPluginConfigs()', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const genereicAppPluginConfig = {
|
||||
path: '',
|
||||
version: '',
|
||||
@@ -1024,11 +1025,11 @@ describe('Plugin Extensions / Utils', () => {
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
test('should return the app plugin configs based on the provided plugin ids', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1041,16 +1042,16 @@ describe('Plugin Extensions / Utils', () => {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-third-app',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
expect(getAppPluginConfigs(['myorg-first-app', 'myorg-third-app'])).toEqual([
|
||||
config.apps['myorg-first-app'],
|
||||
config.apps['myorg-third-app'],
|
||||
getAppPluginMeta('myorg-first-app'),
|
||||
getAppPluginMeta('myorg-third-app'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('should simply ignore the app plugin ids that do not belong to a config', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1063,9 +1064,9 @@ describe('Plugin Extensions / Utils', () => {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-third-app',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
expect(getAppPluginConfigs(['myorg-first-app', 'unknown-app-id'])).toEqual([config.apps['myorg-first-app']]);
|
||||
expect(getAppPluginConfigs(['myorg-first-app', 'unknown-app-id'])).toEqual([getAppPluginMeta('myorg-first-app')]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1076,7 +1077,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
});
|
||||
|
||||
describe('getExtensionPointPluginDependencies()', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const genereicAppPluginConfig = {
|
||||
path: '',
|
||||
version: '',
|
||||
@@ -1103,13 +1104,13 @@ describe('Plugin Extensions / Utils', () => {
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
test('should return the app plugin ids that register extensions to a link extension point', () => {
|
||||
const extensionPointId = 'myorg-first-app/link/v1';
|
||||
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1135,7 +1136,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-third-app',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const appPluginIds = getExtensionPointPluginDependencies(extensionPointId);
|
||||
|
||||
@@ -1145,7 +1146,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
test('should return the app plugin ids that register extensions to a component extension point', () => {
|
||||
const extensionPointId = 'myorg-first-app/component/v1';
|
||||
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1171,7 +1172,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
addedFunctions: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const appPluginIds = getExtensionPointPluginDependencies(extensionPointId);
|
||||
|
||||
@@ -1182,7 +1183,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
const extensionPointId = 'myorg-first-app/component/v1';
|
||||
|
||||
// None of the apps are extending the extension point
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1195,7 +1196,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-third-app',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const appPluginIds = getExtensionPointPluginDependencies(extensionPointId);
|
||||
|
||||
@@ -1205,7 +1206,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
test('should also return (recursively) the app plugin ids that the apps which extend the extension-point depend on', () => {
|
||||
const extensionPointId = 'myorg-first-app/component/v1';
|
||||
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1281,7 +1282,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-sixth-app',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const appPluginIds = getExtensionPointPluginDependencies(extensionPointId);
|
||||
|
||||
@@ -1290,7 +1291,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
});
|
||||
|
||||
describe('getExposedComponentPluginDependencies()', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const genereicAppPluginConfig = {
|
||||
path: '',
|
||||
version: '',
|
||||
@@ -1317,13 +1318,13 @@ describe('Plugin Extensions / Utils', () => {
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
test('should only return the app plugin id that exposes the component, if that component does not depend on anything', () => {
|
||||
const exposedComponentId = 'myorg-second-app/component/v1';
|
||||
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1348,7 +1349,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-third-app',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const appPluginIds = getExposedComponentPluginDependencies(exposedComponentId);
|
||||
|
||||
@@ -1358,7 +1359,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
test('should also return the list of app plugin ids that the plugin - which exposes the component - is depending on', () => {
|
||||
const exposedComponentId = 'myorg-second-app/component/v1';
|
||||
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1427,7 +1428,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
addedFunctions: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const appPluginIds = getExposedComponentPluginDependencies(exposedComponentId);
|
||||
|
||||
@@ -1436,7 +1437,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
});
|
||||
|
||||
describe('getAppPluginDependencies()', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const genereicAppPluginConfig = {
|
||||
path: '',
|
||||
version: '',
|
||||
@@ -1463,11 +1464,11 @@ describe('Plugin Extensions / Utils', () => {
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
test('should not end up in an infinite loop if there are circular dependencies', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1492,7 +1493,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const appPluginIds = getAppPluginDependencies('myorg-second-app');
|
||||
|
||||
@@ -1500,7 +1501,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
});
|
||||
|
||||
test('should not end up in an infinite loop if a plugin depends on itself', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
'myorg-first-app': {
|
||||
...genereicAppPluginConfig,
|
||||
id: 'myorg-first-app',
|
||||
@@ -1517,7 +1518,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const appPluginIds = getAppPluginDependencies('myorg-second-app');
|
||||
|
||||
@@ -1526,7 +1527,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
});
|
||||
|
||||
describe('getExtensionPointPluginMeta()', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const mockExtensionPointId = 'test-extension-point';
|
||||
const mockApp1: AppPluginConfig = {
|
||||
id: 'app1',
|
||||
@@ -1581,28 +1582,28 @@ describe('Plugin Extensions / Utils', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.apps = {};
|
||||
setAppPluginMetas({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return empty map when no plugins have extensions for the point', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
app1: { ...mockApp1, extensions: { ...mockApp1.extensions, addedComponents: [], addedLinks: [] } },
|
||||
app2: { ...mockApp2, extensions: { ...mockApp2.extensions, addedComponents: [], addedLinks: [] } },
|
||||
};
|
||||
});
|
||||
|
||||
const result = getExtensionPointPluginMeta(mockExtensionPointId);
|
||||
expect(result.size).toBe(0);
|
||||
});
|
||||
|
||||
it('should return map with plugins that have components for the extension point', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
app1: mockApp1,
|
||||
app2: mockApp2,
|
||||
};
|
||||
});
|
||||
|
||||
const result = getExtensionPointPluginMeta(mockExtensionPointId);
|
||||
|
||||
@@ -1618,7 +1619,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
});
|
||||
|
||||
it('should filter out plugins that do not have any extensions for the point', () => {
|
||||
config.apps = {
|
||||
setAppPluginMetas({
|
||||
app1: mockApp1,
|
||||
app2: { ...mockApp2, extensions: { ...mockApp2.extensions, addedComponents: [], addedLinks: [] } },
|
||||
app3: {
|
||||
@@ -1630,7 +1631,7 @@ describe('Plugin Extensions / Utils', () => {
|
||||
addedLinks: [{ title: 'Link 3', targets: ['other-point'] }],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const result = getExtensionPointPluginMeta(mockExtensionPointId);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
PluginExtensionPoints,
|
||||
ExtensionInfo,
|
||||
} from '@grafana/data';
|
||||
import { reportInteraction, config, AppPluginConfig } from '@grafana/runtime';
|
||||
import { reportInteraction, config, AppPluginConfig, getAppPluginMetas, getAppPluginMeta } from '@grafana/runtime';
|
||||
import { Modal } from '@grafana/ui';
|
||||
import { appEvents } from 'app/core/app_events';
|
||||
import { getPluginSettings } from 'app/features/plugins/pluginSettings';
|
||||
@@ -607,7 +607,7 @@ export function getLinkExtensionPathWithTracking(pluginId: string, path: string,
|
||||
export const isGrafanaDevMode = () => config.buildInfo.env === 'development';
|
||||
|
||||
export const getAppPluginConfigs = (pluginIds: string[] = []) =>
|
||||
Object.values(config.apps).filter((app) => pluginIds.includes(app.id));
|
||||
Object.values(getAppPluginMetas()).filter((app) => pluginIds.includes(app.id));
|
||||
|
||||
export const getAppPluginIdFromExposedComponentId = (exposedComponentId: string) => {
|
||||
return exposedComponentId.split('/')[0];
|
||||
@@ -617,7 +617,7 @@ export const getAppPluginIdFromExposedComponentId = (exposedComponentId: string)
|
||||
// (These plugins are necessary to be loaded to use the extension point.)
|
||||
// (The function also returns the plugin ids that the plugins - that extend the extension point - depend on.)
|
||||
export const getExtensionPointPluginDependencies = (extensionPointId: string): string[] => {
|
||||
return Object.values(config.apps)
|
||||
return Object.values(getAppPluginMetas())
|
||||
.filter(
|
||||
(app) =>
|
||||
app.extensions.addedLinks.some((link) => link.targets.includes(extensionPointId)) ||
|
||||
@@ -646,7 +646,7 @@ export const getExtensionPointPluginMeta = (extensionPointId: string): Extension
|
||||
return new Map(
|
||||
getExtensionPointPluginDependencies(extensionPointId)
|
||||
.map((pluginId) => {
|
||||
const app = config.apps[pluginId];
|
||||
const app = getAppPluginMeta(pluginId);
|
||||
// if the plugin does not exist or does not expose any components or links to the extension point, return undefined
|
||||
if (
|
||||
!app ||
|
||||
@@ -683,7 +683,7 @@ export const getExposedComponentPluginDependencies = (exposedComponentId: string
|
||||
// metadata field. (For example the plugins that expose components that the app depends on.)
|
||||
// Heads up! This is a recursive function.
|
||||
export const getAppPluginDependencies = (pluginId: string, visited: string[] = []): string[] => {
|
||||
if (!config.apps[pluginId]) {
|
||||
if (!getAppPluginMeta(pluginId)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -692,7 +692,7 @@ export const getAppPluginDependencies = (pluginId: string, visited: string[] = [
|
||||
return [];
|
||||
}
|
||||
|
||||
const pluginIdDependencies = config.apps[pluginId].dependencies.extensions.exposedComponents.map(
|
||||
const pluginIdDependencies = getAppPluginMeta(pluginId).dependencies.extensions.exposedComponents.map(
|
||||
getAppPluginIdFromExposedComponentId
|
||||
);
|
||||
|
||||
@@ -713,7 +713,7 @@ export const getAppPluginsToAwait = () => {
|
||||
'cloud-home-app',
|
||||
];
|
||||
|
||||
return Object.values(config.apps).filter((app) => pluginIds.includes(app.id));
|
||||
return Object.values(getAppPluginMetas()).filter((app) => pluginIds.includes(app.id));
|
||||
};
|
||||
|
||||
// Returns a list of app plugins that has to be preloaded in parallel with the core Grafana initialization.
|
||||
@@ -723,7 +723,7 @@ export const getAppPluginsToPreload = () => {
|
||||
const awaitedPluginIds = getAppPluginsToAwait().map((app) => app.id);
|
||||
const isNotAwaited = (app: AppPluginConfig) => !awaitedPluginIds.includes(app.id);
|
||||
|
||||
return Object.values(config.apps).filter((app) => {
|
||||
return Object.values(getAppPluginMetas()).filter((app) => {
|
||||
return isNotAwaited(app) && (app.preload || dashboardPanelMenuPluginIds.includes(app.id));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
PluginLoadingStrategy,
|
||||
PluginType,
|
||||
} from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { getAppPluginMetas } from '@grafana/runtime';
|
||||
import { setAppPluginMetas } from '@grafana/runtime/internal';
|
||||
|
||||
import { createLogMock } from './logs/testUtils';
|
||||
import {
|
||||
@@ -223,7 +224,7 @@ describe('Plugin Extension Validators', () => {
|
||||
});
|
||||
|
||||
describe('isAddedLinkMetaInfoMissing()', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const pluginId = 'myorg-extensions-app';
|
||||
const appPluginConfig = {
|
||||
id: pluginId,
|
||||
@@ -257,18 +258,20 @@ describe('Plugin Extension Validators', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.apps = {
|
||||
[pluginId]: appPluginConfig,
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: appPluginConfig });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return FALSE if the meta-info in the plugin.json is correct', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedLinks.push(extensionConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedLinks: [extensionConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedLinkMetaInfoMissing(pluginId, extensionConfig, log);
|
||||
|
||||
@@ -278,7 +281,7 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the app config is not found', () => {
|
||||
const log = createLogMock();
|
||||
delete config.apps[pluginId];
|
||||
setAppPluginMetas({});
|
||||
|
||||
const returnValue = isAddedLinkMetaInfoMissing(pluginId, extensionConfig, log);
|
||||
|
||||
@@ -289,7 +292,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the link has no meta-info in the plugin.json', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedLinks = [];
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedLinks: [] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedLinkMetaInfoMissing(pluginId, extensionConfig, log);
|
||||
|
||||
@@ -302,7 +309,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the "targets" do not match', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedLinks.push(extensionConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedLinks: [extensionConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedLinkMetaInfoMissing(
|
||||
pluginId,
|
||||
@@ -322,7 +333,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return FALSE and log a warning if the "description" does not match', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedLinks.push(extensionConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedLinks: [extensionConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedLinkMetaInfoMissing(
|
||||
pluginId,
|
||||
@@ -340,12 +355,15 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return FALSE with links with the same title but different targets', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedLinks.push(extensionConfig);
|
||||
const extensionConfig2 = {
|
||||
...extensionConfig,
|
||||
targets: [PluginExtensionPoints.ExploreToolbarAction],
|
||||
};
|
||||
config.apps[pluginId].extensions.addedLinks.push(extensionConfig2);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedLinks: [extensionConfig, extensionConfig2] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedLinkMetaInfoMissing(pluginId, extensionConfig2, log);
|
||||
|
||||
@@ -355,7 +373,7 @@ describe('Plugin Extension Validators', () => {
|
||||
});
|
||||
|
||||
describe('isAddedComponentMetaInfoMissing()', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const pluginId = 'myorg-extensions-app';
|
||||
const appPluginConfig = {
|
||||
id: pluginId,
|
||||
@@ -390,18 +408,20 @@ describe('Plugin Extension Validators', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.apps = {
|
||||
[pluginId]: appPluginConfig,
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: appPluginConfig });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return FALSE if the meta-info in the plugin.json is correct', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedComponents.push(extensionConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedComponents: [extensionConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedComponentMetaInfoMissing(pluginId, extensionConfig, log);
|
||||
|
||||
@@ -411,7 +431,7 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the app config is not found', () => {
|
||||
const log = createLogMock();
|
||||
delete config.apps[pluginId];
|
||||
setAppPluginMetas({});
|
||||
|
||||
const returnValue = isAddedComponentMetaInfoMissing(pluginId, extensionConfig, log);
|
||||
|
||||
@@ -422,7 +442,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the Component has no meta-info in the plugin.json', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedComponents = [];
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedComponents: [] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedComponentMetaInfoMissing(pluginId, extensionConfig, log);
|
||||
|
||||
@@ -435,7 +459,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the "targets" do not match', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedComponents.push(extensionConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedComponents: [extensionConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedComponentMetaInfoMissing(
|
||||
pluginId,
|
||||
@@ -454,7 +482,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return FALSE and log a warning if the "description" does not match', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedComponents.push(extensionConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedComponents: [extensionConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedComponentMetaInfoMissing(
|
||||
pluginId,
|
||||
@@ -472,12 +504,15 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return FALSE with components with the same title but different targets', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.addedComponents.push(extensionConfig);
|
||||
const extensionConfig2 = {
|
||||
...extensionConfig,
|
||||
targets: [PluginExtensionPoints.ExploreToolbarAction],
|
||||
};
|
||||
config.apps[pluginId].extensions.addedComponents.push(extensionConfig2);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, addedComponents: [extensionConfig, extensionConfig2] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isAddedComponentMetaInfoMissing(pluginId, extensionConfig2, log);
|
||||
|
||||
@@ -487,7 +522,7 @@ describe('Plugin Extension Validators', () => {
|
||||
});
|
||||
|
||||
describe('isExposedComponentMetaInfoMissing()', () => {
|
||||
const originalApps = config.apps;
|
||||
const originalApps = getAppPluginMetas();
|
||||
const pluginId = 'myorg-extensions-app';
|
||||
const appPluginConfig = {
|
||||
id: pluginId,
|
||||
@@ -522,18 +557,20 @@ describe('Plugin Extension Validators', () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
config.apps = {
|
||||
[pluginId]: appPluginConfig,
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: appPluginConfig });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
config.apps = originalApps;
|
||||
setAppPluginMetas(originalApps);
|
||||
});
|
||||
|
||||
it('should return FALSE if the meta-info in the plugin.json is correct', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.exposedComponents.push(exposedComponentConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, exposedComponents: [exposedComponentConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isExposedComponentMetaInfoMissing(pluginId, exposedComponentConfig, log);
|
||||
|
||||
@@ -543,7 +580,7 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the app config is not found', () => {
|
||||
const log = createLogMock();
|
||||
delete config.apps[pluginId];
|
||||
setAppPluginMetas({});
|
||||
|
||||
const returnValue = isExposedComponentMetaInfoMissing(pluginId, exposedComponentConfig, log);
|
||||
|
||||
@@ -554,7 +591,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the exposed component has no meta-info in the plugin.json', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.exposedComponents = [];
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, exposedComponents: [] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isExposedComponentMetaInfoMissing(pluginId, exposedComponentConfig, log);
|
||||
|
||||
@@ -567,7 +608,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return TRUE and log an error if the title does not match', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.exposedComponents.push(exposedComponentConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, exposedComponents: [exposedComponentConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isExposedComponentMetaInfoMissing(
|
||||
pluginId,
|
||||
@@ -587,7 +632,11 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return FALSE and log a warning if the "description" does not match', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.exposedComponents.push(exposedComponentConfig);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: { ...appPluginConfig.extensions, exposedComponents: [exposedComponentConfig] },
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isExposedComponentMetaInfoMissing(
|
||||
pluginId,
|
||||
@@ -605,12 +654,18 @@ describe('Plugin Extension Validators', () => {
|
||||
|
||||
it('should return FALSE with components with the same title but different targets', () => {
|
||||
const log = createLogMock();
|
||||
config.apps[pluginId].extensions.exposedComponents.push(exposedComponentConfig);
|
||||
const exposedComponentConfig2 = {
|
||||
...exposedComponentConfig,
|
||||
targets: [PluginExtensionPoints.ExploreToolbarAction],
|
||||
};
|
||||
config.apps[pluginId].extensions.exposedComponents.push(exposedComponentConfig2);
|
||||
const config = {
|
||||
...appPluginConfig,
|
||||
extensions: {
|
||||
...appPluginConfig.extensions,
|
||||
exposedComponents: [exposedComponentConfig, exposedComponentConfig2],
|
||||
},
|
||||
};
|
||||
setAppPluginMetas({ [pluginId]: config });
|
||||
|
||||
const returnValue = isExposedComponentMetaInfoMissing(pluginId, exposedComponentConfig2, log);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
PluginExtensionPointPatterns,
|
||||
} from '@grafana/data';
|
||||
import { PluginAddedLinksConfigureFunc } from '@grafana/data/internal';
|
||||
import { config, isPluginExtensionLink } from '@grafana/runtime';
|
||||
import { getAppPluginMeta, isPluginExtensionLink } from '@grafana/runtime';
|
||||
|
||||
import * as errors from './errors';
|
||||
import { ExtensionsLog } from './logs/log';
|
||||
@@ -151,7 +151,7 @@ export const isAddedLinkMetaInfoMissing = (
|
||||
log: ExtensionsLog
|
||||
) => {
|
||||
const logPrefix = 'Could not register link extension. Reason:';
|
||||
const app = config.apps[pluginId];
|
||||
const app = getAppPluginMeta(pluginId);
|
||||
const pluginJsonMetaInfo = app ? app.extensions.addedLinks.filter(({ title }) => title === metaInfo.title) : null;
|
||||
|
||||
if (!app) {
|
||||
@@ -183,7 +183,7 @@ export const isAddedFunctionMetaInfoMissing = (
|
||||
log: ExtensionsLog
|
||||
) => {
|
||||
const logPrefix = 'Could not register function extension. Reason:';
|
||||
const app = config.apps[pluginId];
|
||||
const app = getAppPluginMeta(pluginId);
|
||||
const pluginJsonMetaInfo = app ? app.extensions.addedFunctions.filter(({ title }) => title === metaInfo.title) : null;
|
||||
|
||||
if (!app) {
|
||||
@@ -215,7 +215,7 @@ export const isAddedComponentMetaInfoMissing = (
|
||||
log: ExtensionsLog
|
||||
) => {
|
||||
const logPrefix = 'Could not register component extension. Reason:';
|
||||
const app = config.apps[pluginId];
|
||||
const app = getAppPluginMeta(pluginId);
|
||||
const pluginJsonMetaInfo = app
|
||||
? app.extensions.addedComponents.filter(({ title }) => title === metaInfo.title)
|
||||
: null;
|
||||
@@ -249,7 +249,7 @@ export const isExposedComponentMetaInfoMissing = (
|
||||
log: ExtensionsLog
|
||||
) => {
|
||||
const logPrefix = 'Could not register exposed component extension. Reason:';
|
||||
const app = config.apps[pluginId];
|
||||
const app = getAppPluginMeta(pluginId);
|
||||
const pluginJsonMetaInfo = app ? app.extensions.exposedComponents.filter(({ id }) => id === metaInfo.id) : null;
|
||||
|
||||
if (!app) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PluginType, patchArrayVectorProrotypeMethods } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { config, getAppPluginMetas } from '@grafana/runtime';
|
||||
|
||||
import { transformPluginSourceForCDN } from '../cdn/utils';
|
||||
import { resolvePluginUrlWithCache } from '../loader/pluginInfoCache';
|
||||
@@ -138,7 +138,7 @@ export function getPluginLoadData(pluginId: string): SandboxPluginMeta {
|
||||
|
||||
//find it in apps
|
||||
//the information inside the apps object is more limited
|
||||
for (const app of Object.values(config.apps)) {
|
||||
for (const app of Object.values(getAppPluginMetas())) {
|
||||
if (app.id === pluginId) {
|
||||
return {
|
||||
id: pluginId,
|
||||
|
||||
Reference in New Issue
Block a user