diff --git a/public/app/features/alerting/unified/api/incidentsApi.ts b/public/app/features/alerting/unified/api/incidentsApi.ts index 3a449ca3fdb..737529c18d4 100644 --- a/public/app/features/alerting/unified/api/incidentsApi.ts +++ b/public/app/features/alerting/unified/api/incidentsApi.ts @@ -1,4 +1,4 @@ -import { SupportedPlugin } from '../types/pluginBridges'; +import { getIrmIfPresentOrIncidentPluginId } from '../utils/config'; import { alertingApi } from './alertingApi'; @@ -7,7 +7,7 @@ interface IncidentsPluginConfigDto { isIncidentCreated: boolean; } -const getProxyApiUrl = (path: string) => `/api/plugins/${SupportedPlugin.Incident}/resources${path}`; +const getProxyApiUrl = (path: string) => `/api/plugins/${getIrmIfPresentOrIncidentPluginId()}/resources${path}`; export const incidentsApi = alertingApi.injectEndpoints({ endpoints: (build) => ({ diff --git a/public/app/features/alerting/unified/api/onCallApi.test.ts b/public/app/features/alerting/unified/api/onCallApi.test.ts new file mode 100644 index 00000000000..a403f79b4a2 --- /dev/null +++ b/public/app/features/alerting/unified/api/onCallApi.test.ts @@ -0,0 +1,27 @@ +import { config } from '@grafana/runtime'; + +import { pluginMeta, pluginMetaToPluginConfig } from '../testSetup/plugins'; +import { SupportedPlugin } from '../types/pluginBridges'; + +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]) }; + + expect(getProxyApiUrl('/alert_receive_channels/')).toBe( + '/api/plugins/grafana-irm-app/resources/alert_receive_channels/' + ); + }); + + it('should return URL with OnCall plugin ID when IRM plugin is not present', () => { + config.apps = { + [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/' + ); + }); +}); diff --git a/public/app/features/alerting/unified/api/onCallApi.ts b/public/app/features/alerting/unified/api/onCallApi.ts index fdb08f9f991..0e35ae2f1b6 100644 --- a/public/app/features/alerting/unified/api/onCallApi.ts +++ b/public/app/features/alerting/unified/api/onCallApi.ts @@ -1,7 +1,7 @@ import { FetchError, isFetchError } from '@grafana/runtime'; import { GRAFANA_ONCALL_INTEGRATION_TYPE } from '../components/receivers/grafanaAppReceivers/onCall/onCall'; -import { SupportedPlugin } from '../types/pluginBridges'; +import { getIrmIfPresentOrOnCallPluginId } from '../utils/config'; import { alertingApi } from './alertingApi'; @@ -38,7 +38,9 @@ export interface OnCallConfigChecks { is_integration_chatops_connected: boolean; } -const getProxyApiUrl = (path: string) => `/api/plugins/${SupportedPlugin.OnCall}/resources${path}`; +export function getProxyApiUrl(path: string) { + return `/api/plugins/${getIrmIfPresentOrOnCallPluginId()}/resources${path}`; +} export const onCallApi = alertingApi.injectEndpoints({ endpoints: (build) => ({ diff --git a/public/app/features/alerting/unified/components/bridges/DeclareIncidentButton.tsx b/public/app/features/alerting/unified/components/bridges/DeclareIncidentButton.tsx index 1625cb449f7..b0c21ca66fc 100644 --- a/public/app/features/alerting/unified/components/bridges/DeclareIncidentButton.tsx +++ b/public/app/features/alerting/unified/components/bridges/DeclareIncidentButton.tsx @@ -1,7 +1,7 @@ import { Button, LinkButton, Menu, Tooltip } from '@grafana/ui'; import { usePluginBridge } from '../../hooks/usePluginBridge'; -import { SupportedPlugin } from '../../types/pluginBridges'; +import { getIrmIfPresentOrIncidentPluginId } from '../../utils/config'; import { createBridgeURL } from '../PluginBridge'; interface Props { @@ -10,10 +10,16 @@ interface Props { url?: string; } -export const DeclareIncidentButton = ({ title = '', severity = '', url = '' }: Props) => { - const bridgeURL = createBridgeURL(SupportedPlugin.Incident, '/incidents/declare', { title, severity, url }); +const pluginId = getIrmIfPresentOrIncidentPluginId(); - const { loading, installed, settings } = usePluginBridge(SupportedPlugin.Incident); +export const DeclareIncidentButton = ({ title = '', severity = '', url = '' }: Props) => { + const bridgeURL = createBridgeURL(pluginId, '/incidents/declare', { + title, + severity, + url, + }); + + const { loading, installed, settings } = usePluginBridge(pluginId); return ( <> @@ -39,9 +45,13 @@ export const DeclareIncidentButton = ({ title = '', severity = '', url = '' }: P }; export const DeclareIncidentMenuItem = ({ title = '', severity = '', url = '' }: Props) => { - const bridgeURL = createBridgeURL(SupportedPlugin.Incident, '/incidents/declare', { title, severity, url }); + const bridgeURL = createBridgeURL(pluginId, '/incidents/declare', { + title, + severity, + url, + }); - const { loading, installed, settings } = usePluginBridge(SupportedPlugin.Incident); + const { loading, installed, settings } = usePluginBridge(pluginId); return ( <> diff --git a/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts b/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts index 314a64aa852..e1e9cc3f8e8 100644 --- a/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts +++ b/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts @@ -26,7 +26,7 @@ import { useAsync } from '../../hooks/useAsync'; import { usePluginBridge } from '../../hooks/usePluginBridge'; import { useProduceNewAlertmanagerConfiguration } from '../../hooks/useProduceNewAlertmanagerConfig'; import { addReceiverAction, deleteReceiverAction, updateReceiverAction } from '../../reducers/alertmanager/receivers'; -import { SupportedPlugin } from '../../types/pluginBridges'; +import { getIrmIfPresentOrOnCallPluginId } from '../../utils/config'; import { enhanceContactPointsWithMetadata } from './utils'; @@ -67,7 +67,7 @@ const defaultOptions = { * Otherwise, returns no data */ const useOnCallIntegrations = ({ skip }: Skippable = {}) => { - const { installed, loading } = usePluginBridge(SupportedPlugin.OnCall); + const { installed, loading } = usePluginBridge(getIrmIfPresentOrOnCallPluginId()); const oncallIntegrationsResponse = useGrafanaOnCallIntegrationsQuery(undefined, { skip: skip || !installed }); return useMemo(() => { diff --git a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.ts b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.ts index 27adfd1f370..1a22aa3de9b 100644 --- a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.ts +++ b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/onCall/useOnCallIntegration.ts @@ -3,13 +3,13 @@ import { useCallback, useMemo } from 'react'; import { SelectableValue } from '@grafana/data'; import { isFetchError } from '@grafana/runtime'; +import { getIrmIfPresentOrOnCallPluginId } from 'app/features/alerting/unified/utils/config'; import { useAppNotification } from '../../../../../../../core/copy/appNotification'; import { Receiver } from '../../../../../../../plugins/datasource/alertmanager/types'; import { NotifierDTO } from '../../../../../../../types'; import { ONCALL_INTEGRATION_V2_FEATURE, onCallApi } from '../../../../api/onCallApi'; import { usePluginBridge } from '../../../../hooks/usePluginBridge'; -import { SupportedPlugin } from '../../../../types/pluginBridges'; import { option } from '../../../../utils/notifier-types'; import { GRAFANA_APP_RECEIVERS_SOURCE_IMAGE } from '../types'; @@ -41,7 +41,7 @@ function useOnCallPluginStatus() { installed: isOnCallEnabled, loading: isPluginBridgeLoading, error: pluginError, - } = usePluginBridge(SupportedPlugin.OnCall); + } = usePluginBridge(getIrmIfPresentOrOnCallPluginId()); const { data: onCallFeatures = [], @@ -240,7 +240,7 @@ export function useOnCallIntegration() { description: isOnCallEnabled ? 'Connect effortlessly to Grafana OnCall' : 'Enable Grafana OnCall plugin to use this integration', - iconUrl: GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[SupportedPlugin.OnCall], + iconUrl: GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[getIrmIfPresentOrOnCallPluginId()], }, extendOnCallNotifierFeatures, extendOnCallReceivers, diff --git a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts index d769ca4739e..5ac5aa95219 100644 --- a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts +++ b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts @@ -2,7 +2,7 @@ import { SupportedPlugin } from '../../../types/pluginBridges'; export const GRAFANA_APP_RECEIVERS_SOURCE_IMAGE: Record = { [SupportedPlugin.OnCall]: 'public/img/alerting/oncall_logo.svg', - + [SupportedPlugin.Irm]: 'public/img/alerting/irm_logo.svg', [SupportedPlugin.Incident]: '', [SupportedPlugin.MachineLearning]: '', [SupportedPlugin.Labels]: '', diff --git a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts index 463d092fcc9..404e86e24c7 100644 --- a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts +++ b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/useReceiversMetadata.ts @@ -1,6 +1,6 @@ import { GrafanaManagedReceiverConfig } from '../../../../../../plugins/datasource/alertmanager/types'; import { OnCallIntegrationDTO } from '../../../api/onCallApi'; -import { SupportedPlugin } from '../../../types/pluginBridges'; +import { getIrmIfPresentOrOnCallPluginId, getIsIrmPluginPresent } from '../../../utils/config'; import { createBridgeURL } from '../../PluginBridge'; import { GRAFANA_APP_RECEIVERS_SOURCE_IMAGE } from './types'; @@ -13,7 +13,7 @@ export interface ReceiverPluginMetadata { warning?: string; } -const onCallReceiverICon = GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[SupportedPlugin.OnCall]; +const onCallReceiverICon = GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[getIrmIfPresentOrOnCallPluginId()]; const onCallReceiverTitle = 'Grafana OnCall'; export const onCallReceiverMeta: ReceiverPluginMetadata = { @@ -26,6 +26,8 @@ export function getOnCallMetadata( receiver: GrafanaManagedReceiverConfig, hasAlertManagerConfigData = true ): ReceiverPluginMetadata { + const pluginName = getIsIrmPluginPresent() ? 'IRM' : 'OnCall'; + if (!hasAlertManagerConfigData) { return onCallReceiverMeta; } @@ -43,7 +45,7 @@ export function getOnCallMetadata( if (onCallIntegrations == null) { return { ...onCallReceiverMeta, - warning: 'Grafana OnCall is not installed or is disabled', + warning: `Grafana ${pluginName} is not installed or is disabled`, }; } @@ -55,8 +57,8 @@ export function getOnCallMetadata( ...onCallReceiverMeta, description: matchingOnCallIntegration?.display_name, externalUrl: matchingOnCallIntegration - ? createBridgeURL(SupportedPlugin.OnCall, `/integrations/${matchingOnCallIntegration.value}`) + ? createBridgeURL(getIrmIfPresentOrOnCallPluginId(), `/integrations/${matchingOnCallIntegration.value}`) : undefined, - warning: matchingOnCallIntegration ? undefined : 'OnCall Integration no longer exists', + warning: matchingOnCallIntegration ? undefined : `${pluginName} Integration no longer exists`, }; } diff --git a/public/app/features/alerting/unified/testSetup/plugins.ts b/public/app/features/alerting/unified/testSetup/plugins.ts index 93d92565750..dd3fb82ee76 100644 --- a/public/app/features/alerting/unified/testSetup/plugins.ts +++ b/public/app/features/alerting/unified/testSetup/plugins.ts @@ -1,5 +1,5 @@ -import { PluginMeta, PluginType } from '@grafana/data'; -import { setPluginComponentsHook, setPluginExtensionsHook } from '@grafana/runtime'; +import { PluginLoadingStrategy, PluginMeta, PluginType } from '@grafana/data'; +import { AppPluginConfig, setPluginComponentsHook, setPluginExtensionsHook } from '@grafana/runtime'; import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges'; import { mockPluginLinkExtension } from '../mocks'; @@ -21,8 +21,8 @@ export function setupPluginsExtensionsHook() { })); } -export const plugins: PluginMeta[] = [ - { +export const pluginMeta = { + [SupportedPlugin.Slo]: { id: SupportedPlugin.Slo, name: 'SLO dashboard', type: PluginType.app, @@ -44,8 +44,28 @@ export const plugins: PluginMeta[] = [ }, module: 'public/plugins/grafana-slo-app/module.js', baseUrl: 'public/plugins/grafana-slo-app', - }, - { + } satisfies PluginMeta, + [SupportedPlugin.Irm]: { + id: SupportedPlugin.Irm, + name: 'Grafana IRM', + type: PluginType.app, + enabled: true, + info: { + author: { name: 'Grafana Labs', url: '' }, + description: 'Grafana IRM', + links: [], + logos: { + small: 'public/plugins/grafana-irm-app/img/logo.svg', + large: 'public/plugins/grafana-irm-app/img/logo.svg', + }, + screenshots: [], + version: 'local-dev', + updated: '2024-04-09', + }, + module: 'public/plugins/grafana-irm-app/module.js', + baseUrl: 'public/plugins/grafana-irm-app', + } satisfies PluginMeta, + [SupportedPlugin.Incident]: { id: SupportedPlugin.Incident, name: 'Incident management', type: PluginType.app, @@ -67,31 +87,8 @@ export const plugins: PluginMeta[] = [ }, module: 'public/plugins/grafana-incident-app/module.js', baseUrl: 'public/plugins/grafana-incident-app', - }, - { - id: 'grafana-asserts-app', - name: 'Asserts', - type: PluginType.app, - enabled: true, - info: { - author: { - name: 'Grafana Labs', - url: '', - }, - description: 'Asserts', - links: [], - logos: { - small: 'public/plugins/grafana-asserts-app/img/logo.svg', - large: 'public/plugins/grafana-asserts-app/img/logo.svg', - }, - screenshots: [], - version: 'local-dev', - updated: '2024-04-09', - }, - module: 'public/plugins/grafana-asserts-app/module.js', - baseUrl: 'public/plugins/grafana-asserts-app', - }, - { + } satisfies PluginMeta, + [SupportedPlugin.OnCall]: { id: SupportedPlugin.OnCall, name: 'OnCall', type: PluginType.app, @@ -113,5 +110,59 @@ export const plugins: PluginMeta[] = [ }, module: 'public/plugins/grafana-oncall-app/module.js', baseUrl: 'public/plugins/grafana-oncall-app', - }, + } satisfies PluginMeta, + ['grafana-asserts-app']: { + id: 'grafana-asserts-app', + name: 'Asserts', + type: PluginType.app, + enabled: true, + info: { + author: { + name: 'Grafana Labs', + url: '', + }, + description: 'Asserts', + links: [], + logos: { + small: 'public/plugins/grafana-asserts-app/img/logo.svg', + large: 'public/plugins/grafana-asserts-app/img/logo.svg', + }, + screenshots: [], + version: 'local-dev', + updated: '2024-04-09', + }, + module: 'public/plugins/grafana-asserts-app/module.js', + baseUrl: 'public/plugins/grafana-asserts-app', + } satisfies PluginMeta, +}; + +export const plugins: PluginMeta[] = [ + pluginMeta[SupportedPlugin.Slo], + pluginMeta[SupportedPlugin.Incident], + pluginMeta[SupportedPlugin.OnCall], + pluginMeta['grafana-asserts-app'], ]; + +export function pluginMetaToPluginConfig(pluginMeta: PluginMeta): AppPluginConfig { + return { + id: pluginMeta.id, + path: pluginMeta.baseUrl, + preload: true, + version: pluginMeta.info.version, + angular: { detected: false, hideDeprecation: false }, + loadingStrategy: PluginLoadingStrategy.script, + dependencies: { + plugins: [], + grafanaVersion: 'local-dev', + extensions: { + exposedComponents: [], + }, + }, + extensions: { + addedLinks: [], + addedComponents: [], + extensionPoints: [], + exposedComponents: [], + }, + }; +} diff --git a/public/app/features/alerting/unified/types/pluginBridges.ts b/public/app/features/alerting/unified/types/pluginBridges.ts index 926a75ff41d..a1d0b9b20cc 100644 --- a/public/app/features/alerting/unified/types/pluginBridges.ts +++ b/public/app/features/alerting/unified/types/pluginBridges.ts @@ -1,6 +1,7 @@ export enum SupportedPlugin { Incident = 'grafana-incident-app', OnCall = 'grafana-oncall-app', + Irm = 'grafana-irm-app', MachineLearning = 'grafana-ml-app', Labels = 'grafana-labels-app', Slo = 'grafana-slo-app', diff --git a/public/app/features/alerting/unified/utils/config.test.ts b/public/app/features/alerting/unified/utils/config.test.ts index cab4615eb7a..34a4734b55e 100644 --- a/public/app/features/alerting/unified/utils/config.test.ts +++ b/public/app/features/alerting/unified/utils/config.test.ts @@ -1,6 +1,14 @@ import { config } from '@grafana/runtime'; -import { checkEvaluationIntervalGlobalLimit } from './config'; +import { pluginMeta, pluginMetaToPluginConfig } from '../testSetup/plugins'; +import { SupportedPlugin } from '../types/pluginBridges'; + +import { + checkEvaluationIntervalGlobalLimit, + getIrmIfPresentOrIncidentPluginId, + getIrmIfPresentOrOnCallPluginId, + getIsIrmPluginPresent, +} from './config'; describe('checkEvaluationIntervalGlobalLimit', () => { it('should NOT exceed limit if evaluate every is not valid duration', () => { @@ -51,3 +59,48 @@ describe('checkEvaluationIntervalGlobalLimit', () => { expect(exceedsLimit).toBe(false); }); }); + +describe('getIsIrmPluginPresent', () => { + it('should return true when IRM plugin is present in config.apps', () => { + config.apps = { [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 = { + [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]) }; + expect(getIrmIfPresentOrIncidentPluginId()).toBe(SupportedPlugin.Irm); + }); + + it('should return Incident plugin ID when IRM plugin is not present', () => { + config.apps = { + [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]) }; + expect(getIrmIfPresentOrOnCallPluginId()).toBe(SupportedPlugin.Irm); + }); + + it('should return OnCall plugin ID when IRM plugin is not present', () => { + config.apps = { + [SupportedPlugin.OnCall]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.OnCall]), + [SupportedPlugin.Incident]: pluginMetaToPluginConfig(pluginMeta[SupportedPlugin.Incident]), + }; + expect(getIrmIfPresentOrOnCallPluginId()).toBe(SupportedPlugin.OnCall); + }); +}); diff --git a/public/app/features/alerting/unified/utils/config.ts b/public/app/features/alerting/unified/utils/config.ts index 2183ebd5ce1..6a85614622a 100644 --- a/public/app/features/alerting/unified/utils/config.ts +++ b/public/app/features/alerting/unified/utils/config.ts @@ -1,6 +1,8 @@ import { DataSourceInstanceSettings, DataSourceJsonData } from '@grafana/data'; import { config } from '@grafana/runtime'; +import { SupportedPlugin } from '../types/pluginBridges'; + import { isValidPrometheusDuration, safeParsePrometheusDuration } from './time'; export function getAllDataSources(): Array> { @@ -26,3 +28,15 @@ export function checkEvaluationIntervalGlobalLimit(alertGroupEvaluateEvery?: str return { globalLimit: evaluateEveryGlobalLimitMs, exceedsLimit }; } + +export function getIsIrmPluginPresent() { + return SupportedPlugin.Irm in config.apps; +} + +export function getIrmIfPresentOrIncidentPluginId() { + return getIsIrmPluginPresent() ? SupportedPlugin.Irm : SupportedPlugin.Incident; +} + +export function getIrmIfPresentOrOnCallPluginId() { + return getIsIrmPluginPresent() ? SupportedPlugin.Irm : SupportedPlugin.OnCall; +} diff --git a/public/app/features/dashboard-scene/serialization/buildNewDashboardSaveModel.test.ts b/public/app/features/dashboard-scene/serialization/buildNewDashboardSaveModel.test.ts index f425f2a7a5a..27b8e60c68e 100644 --- a/public/app/features/dashboard-scene/serialization/buildNewDashboardSaveModel.test.ts +++ b/public/app/features/dashboard-scene/serialization/buildNewDashboardSaveModel.test.ts @@ -44,6 +44,7 @@ jest.mock('@grafana/runtime', () => ({ featureToggles: { newDashboardWithFiltersAndGroupBy: false, }, + apps: {}, bootData: { ...jest.requireActual('@grafana/runtime').config.bootData, user: { diff --git a/public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts b/public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts index 15b958bc505..7c1256fb54f 100644 --- a/public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts +++ b/public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts @@ -36,6 +36,7 @@ jest.mock('@grafana/runtime', () => ({ config: { buildInfo: {}, panels: {}, + apps: {}, featureToggles: { newVariables: false, }, diff --git a/public/app/features/plugins/components/AppRootPage.test.tsx b/public/app/features/plugins/components/AppRootPage.test.tsx index d9b3bd9d77d..84736aed8de 100644 --- a/public/app/features/plugins/components/AppRootPage.test.tsx +++ b/public/app/features/plugins/components/AppRootPage.test.tsx @@ -32,6 +32,7 @@ jest.mock('@grafana/runtime', () => ({ featureToggles: { accessControlOnCall: true, }, + apps: {}, theme2: { breakpoints: { values: { diff --git a/public/img/alerting/irm_logo.svg b/public/img/alerting/irm_logo.svg new file mode 100644 index 00000000000..aa300c81f17 --- /dev/null +++ b/public/img/alerting/irm_logo.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +