From 9399059050adee1a12491be1f9ff75398ac21283 Mon Sep 17 00:00:00 2001 From: rodrigopk Date: Wed, 14 Jan 2026 13:54:39 -0500 Subject: [PATCH] Refactor provenance inference in Policy component --- .../NotificationPoliciesList.tsx | 2 - .../notification-policies/Policy.test.tsx | 37 +++++++++++++++++-- .../notification-policies/Policy.tsx | 9 +---- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.tsx b/public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.tsx index dccaafedd2f..2e0a734a1ed 100644 --- a/public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.tsx @@ -99,7 +99,6 @@ export const NotificationPoliciesList = () => { } return; }, [defaultPolicy]); - const routeProvenance = defaultPolicy?.provenance; // useAsync could also work but it's hard to wait until it's done in the tests // Combining with useEffect gives more predictable results because the condition is in useEffect @@ -245,7 +244,6 @@ export const NotificationPoliciesList = () => { currentRoute={defaults(rootRoute, TIMING_OPTIONS_DEFAULTS)} contactPointsState={contactPointsState.receivers} readOnly={!hasConfigurationAPI} - provenance={routeProvenance} alertManagerSourceName={selectedAlertmanager} onAddPolicy={openAddModal} onEditPolicy={openEditModal} diff --git a/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx b/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx index 1bec449dd6f..1cf56f156e2 100644 --- a/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx @@ -11,6 +11,7 @@ import { AlertmanagerGroup, MatcherOperator, ObjectMatcher, + ROUTES_META_SYMBOL, RouteWithID, } from 'app/plugins/datasource/alertmanager/types'; @@ -338,6 +339,7 @@ describe('Policy', () => { id: 'test-route', receiver: 'test-receiver', routes: [], + [ROUTES_META_SYMBOL]: { provenance: KnownProvenance.File }, }; renderPolicy( @@ -351,7 +353,6 @@ describe('Policy', () => { onAddPolicy={noop} onDeletePolicy={noop} onShowAlertInstances={noop} - provenance={KnownProvenance.File} /> ); @@ -364,6 +365,7 @@ describe('Policy', () => { id: 'test-route', receiver: 'test-receiver', routes: [], + [ROUTES_META_SYMBOL]: { provenance: KnownProvenance.ConvertedPrometheus }, }; renderPolicy( @@ -377,13 +379,38 @@ describe('Policy', () => { onAddPolicy={noop} onDeletePolicy={noop} onShowAlertInstances={noop} - provenance={KnownProvenance.ConvertedPrometheus} /> ); const badge = screen.getByText('Imported'); expect(badge).toBeInTheDocument(); }); + + it('correctly identifies provisioned status from ROUTES_META_SYMBOL', () => { + const mockRoute: RouteWithID = { + id: 'test-route', + receiver: 'test-receiver', + routes: [], + [ROUTES_META_SYMBOL]: { provenance: KnownProvenance.File }, + }; + + renderPolicy( + + ); + + expect(screen.getByText('Provisioned')).toBeInTheDocument(); + // Verify add/edit buttons are disabled + expect(screen.getByRole('button', { name: /new child policy/i })).toBeDisabled(); + }); }); // Doesn't matter which path the routes use, it just needs to match the initialEntries history entry to render the element @@ -510,13 +537,17 @@ describe('useCreateDropdownMenuActions', () => { [true, true], [true, true], ]); + // Create route with provenance in metadata or top-level to match real usage + const routeWithProvenance: RouteWithID = provenance + ? { ...currentRoute, [ROUTES_META_SYMBOL]: { provenance } } + : currentRoute; const { result } = renderHook(() => useCreateDropdownMenuActions( isAutoGenerated, isDefaultPolicy, provenance, openDetailModal, - currentRoute, + routeWithProvenance, toggleShowExportDrawer, onDeletePolicy ) diff --git a/public/app/features/alerting/unified/components/notification-policies/Policy.tsx b/public/app/features/alerting/unified/components/notification-policies/Policy.tsx index adb537b16a3..def301c6df8 100644 --- a/public/app/features/alerting/unified/components/notification-policies/Policy.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/Policy.tsx @@ -62,7 +62,6 @@ interface PolicyComponentProps { receivers?: Receiver[]; contactPointsState?: ReceiversState; readOnly?: boolean; - provenance?: string; inheritedProperties?: InheritableProperties; routesMatchingFilters?: RoutesMatchingFilters; @@ -90,7 +89,6 @@ const Policy = (props: PolicyComponentProps) => { receivers = [], contactPointsState, readOnly = false, - provenance, alertManagerSourceName, currentRoute, inheritedProperties, @@ -109,7 +107,8 @@ const Policy = (props: PolicyComponentProps) => { const styles = useStyles2(getStyles); - // Compute provisioned status from provenance + // Derive provenance from route metadata or top-level (consistent with child handling) + const provenance = currentRoute[ROUTES_META_SYMBOL]?.provenance ?? currentRoute.provenance; const provisioned = isProvisionedResource(provenance); const contactPoint = currentRoute.receiver; @@ -364,9 +363,6 @@ const Policy = (props: PolicyComponentProps) => { then the child policy should not be editable either */ const isThisChildReadOnly = readOnly || provisioned || isAutoGenerated; - // Extract provenance from child route (can be in metadata symbol or top-level) - const childProvenance = child[ROUTES_META_SYMBOL]?.provenance ?? child.provenance; - return ( { routesMatchingFilters={routesMatchingFilters} matchingInstancesPreview={matchingInstancesPreview} isAutoGenerated={isThisChildAutoGenerated} - provenance={childProvenance} /> ); })}