diff --git a/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/app_menu_registry.test.ts b/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/app_menu_registry.test.ts index 7d8aae994c5a8..c28737fdba93b 100644 --- a/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/app_menu_registry.test.ts +++ b/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/app_menu_registry.test.ts @@ -519,6 +519,54 @@ describe('AppMenuRegistry', () => { }); }); + describe('deleteItem', () => { + it('should remove a registered item by ID', () => { + const item: DiscoverAppMenuItemType = { + id: 'to-delete', + order: 1, + label: 'Delete Me', + iconType: 'trash', + run: jest.fn(), + }; + + registry.registerItem(item); + expect(registry.getItem('to-delete')).toBeDefined(); + + registry.deleteItem('to-delete'); + expect(registry.getItem('to-delete')).toBeUndefined(); + + const config = registry.getAppMenuConfig(); + expect(config.items).toHaveLength(0); + }); + + it('should not throw when deleting a non-existent item', () => { + expect(() => registry.deleteItem('non-existent')).not.toThrow(); + }); + + it('should only remove the specified item, leaving others intact', () => { + registry.registerItem({ + id: 'keep', + order: 1, + label: 'Keep', + iconType: 'check', + run: jest.fn(), + }); + registry.registerItem({ + id: 'remove', + order: 2, + label: 'Remove', + iconType: 'trash', + run: jest.fn(), + }); + + registry.deleteItem('remove'); + + const config = registry.getAppMenuConfig(); + expect(config.items).toHaveLength(1); + expect(config.items?.[0].id).toBe('keep'); + }); + }); + describe('mergePopoverItems', () => { it('should merge popover items from source menu into target submenu', () => { const targetMenu: DiscoverAppMenuItemType = { diff --git a/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts b/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts index 5c7c1343ab2e0..7f0969ac39425 100644 --- a/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts +++ b/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/app_menu_registry.ts @@ -95,6 +95,14 @@ export class AppMenuRegistry { } } + /** + * Remove a menu item by ID. + * @param id The ID of the menu item to remove + */ + public deleteItem(id: string): void { + this.items.delete(id); + } + /** * Get a menu item by ID. * @param id The ID of the menu item to retrieve diff --git a/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/types.ts b/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/types.ts index cd6e5facfad4e..e0c45c5655923 100644 --- a/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/types.ts +++ b/src/platform/packages/shared/kbn-discover-utils/src/components/app_menu/types.ts @@ -24,7 +24,6 @@ export enum AppMenuActionId { alerts = 'alerts', inspect = 'inspect', createRule = 'createRule', - legacyRules = 'legacyRules', backgroundsearch = 'backgroundSearch', manageRulesAndConnectors = 'manageRulesAndConnectors', } diff --git a/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.tsx b/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.tsx index 2f68f83dc3b5d..10cb6b87e0981 100644 --- a/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.tsx +++ b/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_alerts.tsx @@ -23,21 +23,21 @@ import { createSearchSource } from '../../../state_management/utils/create_searc import type { DiscoverInternalState } from '../../../state_management/redux'; import { selectTab } from '../../../state_management/redux'; -export const EsQueryValidConsumer: RuleCreationValidConsumer[] = [ +const EsQueryValidConsumer: RuleCreationValidConsumer[] = [ AlertConsumers.INFRASTRUCTURE, AlertConsumers.LOGS, AlertConsumers.OBSERVABILITY, STACK_ALERTS_FEATURE_ID, ]; -export interface EsQueryAlertMetaData extends RuleTypeMetaData { +interface EsQueryAlertMetaData extends RuleTypeMetaData { isManagementPage?: boolean; adHocDataViewList: DataView[]; } const RuleFormFlyoutWithType = RuleFormFlyout; -export const CreateAlertFlyout: React.FC<{ +const CreateAlertFlyout: React.FC<{ discoverParams: AppMenuDiscoverParams; services: DiscoverServices; tabId: string; @@ -193,12 +193,12 @@ export const getAlertsAppMenuItem = ({ }; }; -export function getTimeField(dataView: DataView | undefined) { +function getTimeField(dataView: DataView | undefined) { const dateFields = dataView?.fields.getByType('date'); return dataView?.timeFieldName || dateFields?.[0]?.name; } -export function getManageRulesUrl(services: DiscoverServices) { +function getManageRulesUrl(services: DiscoverServices) { return services.application.getUrlForApp( services.application.isAppRegistered('rules') ? 'rules' diff --git a/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_create_rule.test.tsx b/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_create_rule.test.tsx index 101b03d223c4b..daafada180109 100644 --- a/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_create_rule.test.tsx +++ b/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_create_rule.test.tsx @@ -96,12 +96,12 @@ describe('getCreateRuleMenuItem', () => { }); describe('Nested Items', () => { - it('should have "Create rule" item for ES|QL rules', () => { + it('should have "Create v2 ES|QL rule" item for ES|QL rules', () => { const menuItem = getCreateRuleMenu(); const createRuleItem = menuItem.items?.find((item) => item.id === 'create-rule'); expect(createRuleItem).toBeDefined(); - expect(createRuleItem?.label).toBe('Create rule'); + expect(createRuleItem?.label).toBe('Create v2 ES|QL rule'); expect(createRuleItem?.testId).toBe('discoverCreateRuleButton'); expect(createRuleItem?.iconType).toBe('bell'); expect(createRuleItem?.order).toBe(1); @@ -109,46 +109,16 @@ describe('getCreateRuleMenuItem', () => { expect(typeof createRuleItem?.run).toBe('function'); }); - it('should have "Create legacy rules" submenu with legacy items', () => { + it('should have "Create v1 rules" submenu that starts empty (populated at merge time)', () => { const menuItem = getCreateRuleMenu(); const legacyRulesItem = menuItem.items?.find((item) => item.id === 'legacy-rules'); expect(legacyRulesItem).toBeDefined(); - expect(legacyRulesItem?.label).toBe('Create legacy rules'); + expect(legacyRulesItem?.label).toBe('Create v1 rules'); expect(legacyRulesItem?.testId).toBe('discoverLegacyRulesButton'); expect(legacyRulesItem?.order).toBe(2); expect(legacyRulesItem?.items).toBeDefined(); - expect(legacyRulesItem?.items?.length).toBeGreaterThan(0); - }); - - it('should include "Search threshold rule" in legacy items when ES_QUERY_ID is authorized', () => { - const menuItem = getCreateRuleMenu(); - const legacyRulesItem = menuItem.items?.find((item) => item.id === 'legacy-rules'); - const searchThresholdItem = legacyRulesItem?.items?.find( - (item) => item.id === 'legacy-search-threshold' - ); - - expect(searchThresholdItem).toBeDefined(); - expect(searchThresholdItem?.label).toBe('Search threshold rule'); - expect(searchThresholdItem?.testId).toBe('discoverLegacySearchThresholdButton'); - expect(searchThresholdItem?.iconType).toBe('bell'); - expect(searchThresholdItem?.run).toBeDefined(); - }); - - it('should include "Manage rules and connectors" in legacy items', () => { - const menuItem = getCreateRuleMenu(); - const legacyRulesItem = menuItem.items?.find((item) => item.id === 'legacy-rules'); - const manageRulesItem = legacyRulesItem?.items?.find( - (item) => item.id === 'manage-rules-connectors' - ); - - expect(manageRulesItem).toBeDefined(); - expect(manageRulesItem?.label).toBe('Manage rules and connectors'); - expect(manageRulesItem?.testId).toBe('discoverManageRulesButton'); - expect(manageRulesItem?.iconType).toBe('tableOfContents'); - expect(manageRulesItem?.href).toBe( - '/app/management/insightsAndAlerting/triggersActions/rules' - ); + expect(legacyRulesItem?.items).toHaveLength(0); }); it('should not include "Search threshold rule" when ES_QUERY_ID is not authorized', () => { diff --git a/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_create_rule.tsx b/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_create_rule.tsx index d0e3e468a0f5e..07a9443341a8a 100644 --- a/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_create_rule.tsx +++ b/src/platform/plugins/shared/discover/public/application/main/components/top_nav/app_menu_actions/get_create_rule.tsx @@ -12,12 +12,10 @@ import type { AggregateQuery } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import type { DiscoverAppMenuItemType, DiscoverAppMenuPopoverItem } from '@kbn/discover-utils'; import { AppMenuActionId } from '@kbn/discover-utils'; -import { ES_QUERY_ID } from '@kbn/rule-data-utils'; import type { DiscoverInternalState } from '../../../state_management/redux'; import { selectTab } from '../../../state_management/redux/selectors'; import type { AppMenuDiscoverParams } from './types'; import type { DiscoverServices } from '../../../../../build_services'; -import { CreateAlertFlyout, getManageRulesUrl, getTimeField } from './get_alerts'; export function CreateESQLRuleFlyout({ services, @@ -75,59 +73,11 @@ export const getCreateRuleMenuItem = ({ tabId: string; getState: () => DiscoverInternalState; }): DiscoverAppMenuItemType => { - const { dataView, isEsqlMode } = discoverParams; - const timeField = getTimeField(dataView); - const hasTimeFieldName = !isEsqlMode ? Boolean(dataView?.timeFieldName) : Boolean(timeField); - - const legacyItems: DiscoverAppMenuPopoverItem[] = []; - - if (services.capabilities.management?.insightsAndAlerting?.triggersActions) { - if (discoverParams.authorizedRuleTypeIds.includes(ES_QUERY_ID)) { - legacyItems.push({ - id: 'legacy-search-threshold', - order: 1, - label: i18n.translate('discover.localMenu.legacySearchThresholdTitle', { - defaultMessage: 'Search threshold rule', - }), - iconType: 'bell', - testId: 'discoverLegacySearchThresholdButton', - disableButton: !hasTimeFieldName, - tooltipContent: hasTimeFieldName - ? undefined - : i18n.translate('discover.localMenu.legacyMissedTimeFieldToolTip', { - defaultMessage: 'Data view does not have a time field.', - }), - run: ({ context: { onFinishAction } }) => { - return ( - - ); - }, - }); - } - - legacyItems.push({ - id: 'manage-rules-connectors', - order: Number.MAX_SAFE_INTEGER, - label: i18n.translate('discover.localMenu.manageRulesAndConnectors', { - defaultMessage: 'Manage rules and connectors', - }), - iconType: 'tableOfContents', - testId: 'discoverManageRulesButton', - href: getManageRulesUrl(services), - }); - } - const createRuleItem: DiscoverAppMenuPopoverItem = { id: 'create-rule', order: 1, label: i18n.translate('discover.localMenu.createRuleTitle', { - defaultMessage: 'Create rule', + defaultMessage: 'Create v2 ES|QL rule', }), iconType: 'bell', testId: 'discoverCreateRuleButton', @@ -148,17 +98,13 @@ export const getCreateRuleMenuItem = ({ id: 'legacy-rules', order: 2, label: i18n.translate('discover.localMenu.legacyRulesTitle', { - defaultMessage: 'Create legacy rules', + defaultMessage: 'Create v1 rules', }), testId: 'discoverLegacyRulesButton', - items: legacyItems, + items: [], }; - const items: DiscoverAppMenuPopoverItem[] = [createRuleItem]; - - if (legacyItems.length > 0) { - items.push(legacyRulesItem); - } + const items: DiscoverAppMenuPopoverItem[] = [createRuleItem, legacyRulesItem]; return { id: AppMenuActionId.createRule, diff --git a/src/platform/plugins/shared/discover/public/application/main/components/top_nav/use_top_nav_links.tsx b/src/platform/plugins/shared/discover/public/application/main/components/top_nav/use_top_nav_links.tsx index f53dfa2d452ba..bcdb0068686ac 100644 --- a/src/platform/plugins/shared/discover/public/application/main/components/top_nav/use_top_nav_links.tsx +++ b/src/platform/plugins/shared/discover/public/application/main/components/top_nav/use_top_nav_links.tsx @@ -127,11 +127,6 @@ export const useTopNavLinks = ({ const inspectAppMenuItem = getInspectAppMenuItem({ onOpenInspector }); items.push(inspectAppMenuItem); - const showLegacyAlerts = - services.triggersActionsUi && - discoverParams.authorizedRuleTypeIds.length && - (!canCreateESQLRule || !discoverParams.isEsqlMode); - if (showCreateRuleV2) { const createRuleV2 = getCreateRuleMenuItem({ discoverParams, @@ -142,7 +137,7 @@ export const useTopNavLinks = ({ items.push(createRuleV2); } - if (showLegacyAlerts) { + if (services.triggersActionsUi && discoverParams.authorizedRuleTypeIds.length) { const alertsAppMenuItem = getAlertsAppMenuItem({ discoverParams, services, @@ -233,7 +228,6 @@ export const useTopNavLinks = ({ hasUnsavedChanges, totalHitsState, intl, - canCreateESQLRule, showCreateRuleV2, ]); @@ -248,20 +242,6 @@ export const useTopNavLinks = ({ newAppMenuRegistry.registerItems(appMenuItems); - // Register legacyRules as a top-level item when v2 rules are enabled - // This allows profile extensions to add items to it via registerPopoverItem - // The items are then merged into the createRule menu's legacy submenu - if (showCreateRuleV2) { - newAppMenuRegistry.registerItem({ - id: AppMenuActionId.legacyRules, - label: '', // Not displayed directly - items are pulled into the v2 menu's submenu - iconType: 'empty', - order: Number.MAX_SAFE_INTEGER, - hidden: 'all', // Hide at all breakpoints since this is just a container for registry items - items: [], - }); - } - // Only show the ES|QL button in classic mode (not in ES|QL mode) // The "Switch to Classic" option is now in the tab menu when in ES|QL mode if (services.uiSettings.get(ENABLE_ESQL) && !isEsqlMode) { @@ -404,14 +384,16 @@ export const useTopNavLinks = ({ const registry = getAppMenu(discoverParams).appMenuRegistry(newAppMenuRegistry); - // Merge items from legacyRules into the createRule menu's legacy-rules submenu - // This allows profile extensions to add rule types to the v2 rules menu + // When v2 rules are enabled, profile extensions have registered their rule types + // into the alerts menu as usual. Move those items into the v2 createRule menu's + // legacy-rules submenu, then remove the alerts menu since v2 replaces it. if (showCreateRuleV2) { registry.mergePopoverItems( AppMenuActionId.createRule, 'legacy-rules', - AppMenuActionId.legacyRules + AppMenuActionId.alerts ); + registry.deleteItem(AppMenuActionId.alerts); } return registry; diff --git a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.tsx b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.tsx index 963e1a7c96890..eb20457cf075b 100644 --- a/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.tsx +++ b/src/platform/plugins/shared/discover/public/context_awareness/profile_providers/observability/observability_root_profile/accessors/get_app_menu.tsx @@ -139,10 +139,7 @@ const registerCustomThresholdRuleAction = ( }, }; - // Register to legacy alerts menu registry.registerPopoverItem(AppMenuActionId.alerts, popoverItem); - // Register to v2 rules menu's legacy submenu - registry.registerPopoverItem(AppMenuActionId.legacyRules, popoverItem); }; const registerCreateSLOAction = ( @@ -191,9 +188,6 @@ const registerCreateSLOAction = ( }, }; - // Register to legacy alerts menu registry.registerPopoverItem(AppMenuActionId.alerts, popoverItem); - // Register to v2 rules menu's legacy submenu - registry.registerPopoverItem(AppMenuActionId.legacyRules, popoverItem); } };