grafana_bak/public/app/features/alerting/unified/state/AlertmanagerContext.tsx
2025-04-01 10:38:02 +09:00

126 lines
5.2 KiB
TypeScript

import * as React from 'react';
import { locationService } from '@grafana/runtime';
import store from 'app/core/store';
import { AlertManagerDataSourceJsonData, AlertManagerImplementation } from 'app/plugins/datasource/alertmanager/types';
import { useAlertManagersByPermission } from '../hooks/useAlertManagerSources';
import { ALERTMANAGER_NAME_LOCAL_STORAGE_KEY, ALERTMANAGER_NAME_QUERY_KEY } from '../utils/constants';
import {
AlertManagerDataSource,
GRAFANA_RULES_SOURCE_NAME,
getAlertmanagerDataSourceByName,
} from '../utils/datasource';
interface Context {
selectedAlertmanager: string | undefined;
hasConfigurationAPI: boolean; // returns true when a configuration API is available
isGrafanaAlertmanager: boolean; // returns true if we are dealing with the built-in Alertmanager
selectedAlertmanagerConfig: AlertManagerDataSourceJsonData | undefined;
availableAlertManagers: AlertManagerDataSource[];
setSelectedAlertmanager: (name: string) => void;
}
const AlertmanagerContext = React.createContext<Context | undefined>(undefined);
interface Props extends React.PropsWithChildren {
accessType: 'instance' | 'notification';
// manually setting the alertmanagersource name will override all of the other sources
alertmanagerSourceName?: string;
}
const AlertmanagerProvider = ({ children, accessType, alertmanagerSourceName }: Props) => {
const queryParams = locationService.getSearch();
const updateQueryParams = locationService.partial;
const allAvailableAlertManagers = useAlertManagersByPermission(accessType);
const availableAlertManagers = allAvailableAlertManagers.availableInternalDataSources.concat(
allAvailableAlertManagers.availableExternalDataSources
);
const updateSelectedAlertmanager = React.useCallback(
(selectedAlertManager: string) => {
if (!isAlertManagerAvailable(availableAlertManagers, selectedAlertManager)) {
return;
}
if (selectedAlertManager === GRAFANA_RULES_SOURCE_NAME) {
store.delete(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY);
updateQueryParams({ [ALERTMANAGER_NAME_QUERY_KEY]: undefined });
} else {
store.set(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY, selectedAlertManager);
updateQueryParams({ [ALERTMANAGER_NAME_QUERY_KEY]: selectedAlertManager });
}
},
[availableAlertManagers, updateQueryParams]
);
const sourceFromQuery = queryParams.get(ALERTMANAGER_NAME_QUERY_KEY);
const sourceFromStore = store.get(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY);
const defaultSource = GRAFANA_RULES_SOURCE_NAME;
// This overrides AM in the store to be in sync with the one in the URL
// When the user uses multiple tabs with different AMs, the store will be changing all the time
// It's safest to always use URLs with alertmanager query param
React.useEffect(() => {
if (sourceFromQuery && sourceFromQuery !== sourceFromStore) {
store.set(ALERTMANAGER_NAME_LOCAL_STORAGE_KEY, sourceFromQuery);
}
}, [sourceFromQuery, sourceFromStore]);
// queryParam > localStorage > default
const desiredAlertmanager = alertmanagerSourceName ?? sourceFromQuery ?? sourceFromStore ?? defaultSource;
const selectedAlertmanager = isAlertManagerAvailable(availableAlertManagers, desiredAlertmanager)
? desiredAlertmanager
: undefined;
const selectedAlertmanagerConfig = getAlertmanagerDataSourceByName(selectedAlertmanager)?.jsonData;
// determine if we're dealing with an Alertmanager data source that supports the ruler API
const isGrafanaAlertmanager = selectedAlertmanager === GRAFANA_RULES_SOURCE_NAME;
const isAlertmanagerWithConfigAPI = selectedAlertmanagerConfig
? isAlertManagerWithConfigAPI(selectedAlertmanagerConfig)
: false;
const hasConfigurationAPI = isGrafanaAlertmanager || isAlertmanagerWithConfigAPI;
const value: Context = {
selectedAlertmanager,
hasConfigurationAPI,
isGrafanaAlertmanager,
selectedAlertmanagerConfig,
availableAlertManagers,
setSelectedAlertmanager: updateSelectedAlertmanager,
};
return <AlertmanagerContext.Provider value={value}>{children}</AlertmanagerContext.Provider>;
};
function useAlertmanager() {
const context = React.useContext(AlertmanagerContext);
if (context === undefined) {
throw new Error('useAlertmanager must be used within a AlertmanagerContext');
}
return context;
}
export { AlertmanagerProvider, useAlertmanager };
function isAlertManagerAvailable(availableAlertManagers: AlertManagerDataSource[], alertManagerName: string) {
const availableAlertManagersNames = availableAlertManagers.map((am) => am.name);
return availableAlertManagersNames.includes(alertManagerName);
}
// when the `implementation` is set to `undefined` we assume we're dealing with an AlertManager with config API. The reason for this is because
// our Hosted Grafana stacks provision Alertmanager data sources without `jsonData: { implementation: "mimir" }`.
const CONFIG_API_ENABLED_ALERTMANAGER_FLAVORS = [
AlertManagerImplementation.mimir,
AlertManagerImplementation.cortex,
undefined,
];
export function isAlertManagerWithConfigAPI(dataSourceConfig: AlertManagerDataSourceJsonData): boolean {
return CONFIG_API_ENABLED_ALERTMANAGER_FLAVORS.includes(dataSourceConfig.implementation);
}