Alerting: Only show DMA button when query datasource supports it (#104158)

This commit is contained in:
Tom Ratcliffe
2025-04-23 12:14:27 +01:00
committed by GitHub
parent f9d2199b12
commit a47a155a89
4 changed files with 74 additions and 62 deletions
@@ -78,6 +78,7 @@ import {
} from './reducer';
import { useAdvancedMode } from './useAdvancedMode';
import { useAlertQueryRunner } from './useAlertQueryRunner';
import { onlyOneDSInQueries } from './utils';
interface Props {
editingExistingRule: boolean;
@@ -474,7 +475,10 @@ export const QueryAndExpressionsStep = ({ editingExistingRule, onDataChange, mod
}
: undefined;
const hasDatasourcesForDataSourceManaged = Boolean(rulesSourcesWithRuler.length);
const canSelectDataSourceManaged =
onlyOneDSInQueries(queries) &&
Boolean(rulesSourcesWithRuler.length) &&
queries.some((query) => rulesSourcesWithRuler.some((source) => source.uid === query.datasourceUid));
return (
<>
@@ -594,7 +598,7 @@ export const QueryAndExpressionsStep = ({ editingExistingRule, onDataChange, mod
</Tooltip>
)}
{/* We only show Switch for Grafana managed alerts */}
{hasDatasourcesForDataSourceManaged && isGrafanaAlertingType && !simplifiedQueryStep && mode === 'edit' && (
{canSelectDataSourceManaged && isGrafanaAlertingType && !simplifiedQueryStep && mode === 'edit' && (
<>
<Divider />
<SmartAlertTypeDetector
@@ -3,72 +3,15 @@ import { useFormContext } from 'react-hook-form';
import { DataSourceInstanceSettings } from '@grafana/data';
import { DataSourceJsonData } from '@grafana/schema';
import { RadioButtonGroup, Stack, Text } from '@grafana/ui';
import { contextSrv } from 'app/core/core';
import { Trans, t } from 'app/core/internationalization';
import { ExpressionDatasourceUID } from 'app/features/expressions/types';
import { AccessControlAction } from 'app/types';
import { AlertQuery } from 'app/types/unified-alerting-dto';
import { RuleFormType, RuleFormValues } from '../../../types/rule-form';
import { NeedHelpInfo } from '../NeedHelpInfo';
function getAvailableRuleTypes() {
const canCreateGrafanaRules = contextSrv.hasPermission(AccessControlAction.AlertingRuleCreate);
const canCreateCloudRules = contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalWrite);
const defaultRuleType = canCreateGrafanaRules ? RuleFormType.grafana : RuleFormType.cloudAlerting;
import { getCanSwitch } from './utils';
const enabledRuleTypes: RuleFormType[] = [];
if (canCreateGrafanaRules) {
enabledRuleTypes.push(RuleFormType.grafana);
}
if (canCreateCloudRules) {
enabledRuleTypes.push(RuleFormType.cloudAlerting, RuleFormType.cloudRecording);
}
return { enabledRuleTypes, defaultRuleType };
}
const onlyOneDSInQueries = (queries: AlertQuery[]) => {
return queries.filter((q) => q.datasourceUid !== ExpressionDatasourceUID).length === 1;
};
const getCanSwitch = ({
queries,
ruleFormType,
rulesSourcesWithRuler,
}: {
rulesSourcesWithRuler: Array<DataSourceInstanceSettings<DataSourceJsonData>>;
queries: AlertQuery[];
ruleFormType: RuleFormType | undefined;
}) => {
// get available rule types
const availableRuleTypes = getAvailableRuleTypes();
// check if we have only one query in queries and if it's a cloud datasource
const onlyOneDS = onlyOneDSInQueries(queries);
const dataSourceIdFromQueries = queries[0]?.datasourceUid ?? '';
const isRecordingRuleType = ruleFormType === RuleFormType.cloudRecording;
//let's check if we switch to cloud type
const canSwitchToCloudRule =
!isRecordingRuleType &&
onlyOneDS &&
rulesSourcesWithRuler.some((dsJsonData) => dsJsonData.uid === dataSourceIdFromQueries);
const canSwitchToGrafanaRule = !isRecordingRuleType;
// check for enabled types
const grafanaTypeEnabled = availableRuleTypes.enabledRuleTypes.includes(RuleFormType.grafana);
const cloudTypeEnabled = availableRuleTypes.enabledRuleTypes.includes(RuleFormType.cloudAlerting);
// can we switch to the other type? (cloud or grafana)
const canSwitchFromCloudToGrafana =
ruleFormType === RuleFormType.cloudAlerting && grafanaTypeEnabled && canSwitchToGrafanaRule;
const canSwitchFromGrafanaToCloud =
ruleFormType === RuleFormType.grafana && canSwitchToCloudRule && cloudTypeEnabled && canSwitchToCloudRule;
return canSwitchFromCloudToGrafana || canSwitchFromGrafanaToCloud;
};
export interface SmartAlertTypeDetectorProps {
interface SmartAlertTypeDetectorProps {
editingExistingRule: boolean;
rulesSourcesWithRuler: Array<DataSourceInstanceSettings<DataSourceJsonData>>;
queries: AlertQuery[];
@@ -0,0 +1,65 @@
import { DataSourceInstanceSettings } from '@grafana/data';
import { DataSourceJsonData } from '@grafana/schema/dist/esm/index';
import { contextSrv } from 'app/core/core';
import { ExpressionDatasourceUID } from 'app/features/expressions/types';
import { AccessControlAction } from 'app/types';
import { AlertQuery } from 'app/types/unified-alerting-dto';
import { RuleFormType } from '../../../types/rule-form';
export const onlyOneDSInQueries = (queries: AlertQuery[]) => {
return queries.filter((q) => q.datasourceUid !== ExpressionDatasourceUID).length === 1;
};
function getAvailableRuleTypes() {
const canCreateGrafanaRules = contextSrv.hasPermission(AccessControlAction.AlertingRuleCreate);
const canCreateCloudRules = contextSrv.hasPermission(AccessControlAction.AlertingRuleExternalWrite);
const defaultRuleType = canCreateGrafanaRules ? RuleFormType.grafana : RuleFormType.cloudAlerting;
const enabledRuleTypes: RuleFormType[] = [];
if (canCreateGrafanaRules) {
enabledRuleTypes.push(RuleFormType.grafana);
}
if (canCreateCloudRules) {
enabledRuleTypes.push(RuleFormType.cloudAlerting, RuleFormType.cloudRecording);
}
return { enabledRuleTypes, defaultRuleType };
}
export const getCanSwitch = ({
queries,
ruleFormType,
rulesSourcesWithRuler,
}: {
rulesSourcesWithRuler: Array<DataSourceInstanceSettings<DataSourceJsonData>>;
queries: AlertQuery[];
ruleFormType: RuleFormType | undefined;
}) => {
// get available rule types
const availableRuleTypes = getAvailableRuleTypes();
// check if we have only one query in queries and if it's a cloud datasource
const onlyOneDS = onlyOneDSInQueries(queries);
const dataSourceIdFromQueries = queries[0]?.datasourceUid ?? '';
const isRecordingRuleType = ruleFormType === RuleFormType.cloudRecording;
//let's check if we switch to cloud type
const canSwitchToCloudRule =
!isRecordingRuleType &&
onlyOneDS &&
rulesSourcesWithRuler.some((dsJsonData) => dsJsonData.uid === dataSourceIdFromQueries);
const canSwitchToGrafanaRule = !isRecordingRuleType;
// check for enabled types
const grafanaTypeEnabled = availableRuleTypes.enabledRuleTypes.includes(RuleFormType.grafana);
const cloudTypeEnabled = availableRuleTypes.enabledRuleTypes.includes(RuleFormType.cloudAlerting);
// can we switch to the other type? (cloud or grafana)
const canSwitchFromCloudToGrafana =
ruleFormType === RuleFormType.cloudAlerting && grafanaTypeEnabled && canSwitchToGrafanaRule;
const canSwitchFromGrafanaToCloud =
ruleFormType === RuleFormType.grafana && canSwitchToCloudRule && cloudTypeEnabled && canSwitchToCloudRule;
return canSwitchFromCloudToGrafana || canSwitchFromGrafanaToCloud;
};
@@ -75,7 +75,7 @@ export const CloudRules = ({ namespaces, expandAll }: Props) => {
<div />
)}
<Stack gap={1}>
{canMigrateToGMA && <MigrateToGMAButton />}
{canMigrateToGMA && hasSomeResults && <MigrateToGMAButton />}
<CreateRecordingRuleButton />
</Stack>
</div>