diff --git a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx index d19ac4bb619..fd6cbf40fae 100644 --- a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx @@ -36,7 +36,11 @@ import { isExpressionQueryInAlert, } from '../../../rule-editor/formProcessing'; import { RuleFormType, RuleFormValues } from '../../../types/rule-form'; -import { GRAFANA_RULES_SOURCE_NAME, getDefaultOrFirstCompatibleDataSource } from '../../../utils/datasource'; +import { + GRAFANA_RULES_SOURCE_NAME, + getDefaultOrFirstCompatibleDataSource, + getRulesDataSources, +} from '../../../utils/datasource'; import { PromOrLokiQuery, isPromOrLokiQuery } from '../../../utils/rule-form'; import { isCloudAlertingRuleByType, @@ -417,7 +421,9 @@ export const QueryAndExpressionsStep = ({ editingExistingRule, onDataChange, mod ]); const { sectionTitle, helpLabel, helpContent, helpLink } = DESCRIPTIONS[type ?? RuleFormType.grafana]; - + // Only show the data source managed option if there are data sources with manageAlerts enabled + const hasAlertEnabledDataSources = useMemo(() => getRulesDataSources().length > 0, []); + const canSelectDataSourceManaged = onlyOneDSInQueries(queries) && hasAlertEnabledDataSources; if (!type) { return null; } @@ -437,8 +443,6 @@ export const QueryAndExpressionsStep = ({ editingExistingRule, onDataChange, mod } : undefined; - const canSelectDataSourceManaged = onlyOneDSInQueries(queries); - return ( <> - {mode === 'edit' && ( + {mode === 'edit' && hasAlertEnabledDataSources && ( <> { ]), }); }); + + it('should not show rule type switch when no data sources have manageAlerts enabled', async () => { + // Setup data source with manageAlerts explicitly disabled + setupDataSources( + mockDataSource( + { + type: 'prometheus', + name: 'Prom-disabled', + uid: 'prometheus-disabled', + isDefault: true, + jsonData: { manageAlerts: false }, + }, + { alerting: true, module: 'core:plugin/prometheus' } + ) + ); + + renderRuleEditor(); + + // Wait for the form to load + await screen.findByRole('textbox', { name: 'name' }); + + // The rule type switch should NOT be visible + expect(screen.queryByText('Rule type')).not.toBeInTheDocument(); + expect(screen.queryByTestId('rule-type-radio-group')).not.toBeInTheDocument(); + }); + + it('should show rule type switch when data sources have manageAlerts enabled', async () => { + // Setup data source with manageAlerts enabled + setupDataSources( + mockDataSource( + { + type: 'prometheus', + name: 'Prom-enabled', + uid: 'prometheus-enabled', + isDefault: true, + jsonData: { manageAlerts: true }, + }, + { alerting: true, module: 'core:plugin/prometheus' } + ) + ); + + renderRuleEditor(); + + // Wait for the form to load + await screen.findByRole('textbox', { name: 'name' }); + + // The rule type section should be visible + expect(await screen.findByText('Rule type')).toBeInTheDocument(); + }); }); diff --git a/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx b/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx index 7985791cfa2..1f33e3476ff 100644 --- a/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx +++ b/public/app/features/alerting/unified/rule-list/RuleList.v2.test.tsx @@ -7,10 +7,11 @@ import { setPluginComponentsHook, setPluginLinksHook } from '@grafana/runtime'; import { AccessControlAction } from 'app/types/accessControl'; import { setupMswServer } from '../mockApi'; -import { grantUserPermissions, grantUserRole } from '../mocks'; +import { grantUserPermissions, grantUserRole, mockDataSource } from '../mocks'; import { setGrafanaRuleGroupExportResolver } from '../mocks/server/configure'; import { alertingFactory } from '../mocks/server/db'; import { RulesFilter } from '../search/rulesSearchParser'; +import { setupDataSources } from '../testSetup/datasources'; import RuleListPage, { RuleListActions } from './RuleList.v2'; import { loadDefaultSavedSearch } from './filter/useSavedSearches'; @@ -365,6 +366,51 @@ describe('RuleListActions', () => { expect(ui.exportDrawer.query()).toBeInTheDocument(); }); }); + + describe('Data source options visibility', () => { + it('should not show "New Data source recording rule" option when no data sources have manageAlerts enabled', async () => { + // Set up only data sources with manageAlerts explicitly set to false + // This replaces the default data sources that have manageAlerts defaulting to true + setupDataSources( + mockDataSource({ + name: 'Prometheus-disabled', + uid: 'prometheus-disabled', + type: 'prometheus', + jsonData: { manageAlerts: false }, + }) + ); + + grantUserPermissions([AccessControlAction.AlertingRuleExternalWrite]); + + const { user } = render(); + + await user.click(ui.moreButton.get()); + const menu = await ui.moreMenu.find(); + + expect(ui.menuOptions.newDataSourceRecordingRule.query(menu)).not.toBeInTheDocument(); + }); + + it('should show "New Data source recording rule" option when data sources have manageAlerts enabled', async () => { + // Set up data source with manageAlerts enabled + setupDataSources( + mockDataSource({ + name: 'Prometheus-enabled', + uid: 'prometheus-enabled', + type: 'prometheus', + jsonData: { manageAlerts: true }, + }) + ); + + grantUserPermissions([AccessControlAction.AlertingRuleExternalWrite]); + + const { user } = render(); + + await user.click(ui.moreButton.get()); + const menu = await ui.moreMenu.find(); + + expect(ui.menuOptions.newDataSourceRecordingRule.query(menu)).toBeInTheDocument(); + }); + }); }); describe('RuleListPage v2 - View switching', () => { diff --git a/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx b/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx index 284bd6ad757..b90663200ce 100644 --- a/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx +++ b/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx @@ -13,6 +13,7 @@ import { useListViewMode } from '../components/rules/Filter/RulesViewModeSelecto import { AIAlertRuleButtonComponent } from '../enterprise-components/AI/AIGenAlertRuleButton/addAIAlertRuleButton'; import { AlertingAction, useAlertingAbility } from '../hooks/useAbilities'; import { useRulesFilter } from '../hooks/useFilteredRules'; +import { getRulesDataSources } from '../utils/datasource'; import { FilterView } from './FilterView'; import { GroupedView } from './GroupedView'; @@ -41,8 +42,11 @@ export function RuleListActions() { const [createCloudRuleSupported, createCloudRuleAllowed] = useAlertingAbility(AlertingAction.CreateExternalAlertRule); const [exportRulesSupported, exportRulesAllowed] = useAlertingAbility(AlertingAction.ExportGrafanaManagedRules); + // Check if there are any data sources with manageAlerts enabled + const hasAlertEnabledDataSources = useMemo(() => getRulesDataSources().length > 0, []); + const canCreateGrafanaRules = createGrafanaRuleSupported && createGrafanaRuleAllowed; - const canCreateCloudRules = createCloudRuleSupported && createCloudRuleAllowed; + const canCreateCloudRules = createCloudRuleSupported && createCloudRuleAllowed && hasAlertEnabledDataSources; const canExportRules = exportRulesSupported && exportRulesAllowed; const canCreateRules = canCreateGrafanaRules || canCreateCloudRules;