diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.test.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.test.ts new file mode 100644 index 0000000000000..6f645186ffd7c --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.test.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AIChatExperience } from '@kbn/ai-assistant-common'; +import { createAiNavigationTree } from './ai_navigation_tree'; + +describe('createAiNavigationTree', () => { + it('returns the Workflows link between Agents and Value report when enabled', () => { + const navigationTree = createAiNavigationTree(AIChatExperience.Agent, true); + + const primaryNavSection = navigationTree.body[4]; + const children = 'children' in primaryNavSection ? primaryNavSection.children : []; + + const workflowsIndex = children?.findIndex( + (item) => 'link' in item && item.link === 'workflows' + ); + const agentsIndex = children?.findIndex( + (item) => 'link' in item && item.link === 'agent_builder' + ); + + expect(workflowsIndex).toBe((agentsIndex ?? 0) + 1); + }); + + it('does not include the Workflows link when disabled', () => { + const navigationTree = createAiNavigationTree(AIChatExperience.Agent, false); + + const primaryNavSection = navigationTree.body[4]; + const children = 'children' in primaryNavSection ? primaryNavSection.children : []; + + const workflowsIndex = children?.findIndex( + (item) => 'link' in item && item.link === 'workflows' + ); + + expect(workflowsIndex).toBe(-1); + }); +}); diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.ts index 467ad3e1ca32c..03e62c32426aa 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/ai_navigation/ai_navigation_tree.ts @@ -24,7 +24,8 @@ const SOLUTION_NAME = i18n.translate( ); export const createAiNavigationTree = ( - chatExperience: AIChatExperience = AIChatExperience.Classic + chatExperience: AIChatExperience = AIChatExperience.Classic, + workflowsUiEnabled: boolean = false ): NavigationTreeDefinition => ({ body: [ { @@ -77,7 +78,7 @@ export const createAiNavigationTree = ( breadcrumbStatus: 'hidden', children: [ { - link: 'discover', + link: 'discover' as AppDeepLinkId, }, ...(chatExperience === AIChatExperience.Agent ? [ @@ -88,6 +89,13 @@ export const createAiNavigationTree = ( }, ] : []), + ...(workflowsUiEnabled + ? [ + { + link: 'workflows' as AppDeepLinkId, + }, + ] + : []), { id: SecurityPageName.aiValue, link: securityLink(SecurityPageName.aiValue), diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.test.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.test.ts index 6625221c2406b..398d7ef34c868 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.test.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.test.ts @@ -7,6 +7,7 @@ import { of } from 'rxjs'; import { AIChatExperience } from '@kbn/ai-assistant-common'; +import { WORKFLOWS_UI_SETTING_ID } from '@kbn/workflows/common/constants'; import type { ProductLine, ProductTier } from '../../common/product'; import { mockServices } from '../common/services/__mocks__/services.mock'; import { registerSolutionNavigation } from './navigation'; @@ -31,8 +32,13 @@ describe('Security Side Nav', () => { beforeEach(() => { jest.clearAllMocks(); initNavigationSpy.mockReset(); - // Mock settings.client.get$ to return Classic chat experience by default - services.settings.client.get$ = jest.fn().mockReturnValue(of(AIChatExperience.Classic)); + services.settings.client.get$ = jest.fn().mockImplementation((key: string) => { + if (key === WORKFLOWS_UI_SETTING_ID) { + return of(false); + } + + return of(AIChatExperience.Classic); + }); }); it('registers the navigation tree definition for serverless security', async () => { @@ -64,7 +70,7 @@ describe('Security Side Nav', () => { ]); expect(initNavigationSpy).toHaveBeenCalled(); - expect(mockedCreateAiNavigationTree).toHaveBeenCalledWith(AIChatExperience.Classic); + expect(mockedCreateAiNavigationTree).toHaveBeenCalledWith(AIChatExperience.Classic, false); expect(mockedCreateNavigationTree).not.toHaveBeenCalled(); const [, navigationTree$] = initNavigationSpy.mock.calls[0]; @@ -77,7 +83,13 @@ describe('Security Side Nav', () => { }); it('passes Agent chat experience when settings return Agent', async () => { - services.settings.client.get$ = jest.fn().mockReturnValue(of(AIChatExperience.Agent)); + services.settings.client.get$ = jest.fn().mockImplementation((key: string) => { + if (key === WORKFLOWS_UI_SETTING_ID) { + return of(false); + } + + return of(AIChatExperience.Agent); + }); await registerSolutionNavigation(services, []); diff --git a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.ts b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.ts index 151c967bec8d1..e75083b3f2d19 100644 --- a/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.ts +++ b/x-pack/solutions/security/plugins/security_solution_serverless/public/navigation/navigation.ts @@ -9,7 +9,8 @@ import * as Rx from 'rxjs'; import { firstValueFrom } from 'rxjs'; import { AI_CHAT_EXPERIENCE_TYPE } from '@kbn/management-settings-ids'; import { AIChatExperience } from '@kbn/ai-assistant-common'; -import { ProductTier } from '../../common/product'; +import { WORKFLOWS_UI_SETTING_ID } from '@kbn/workflows/common/constants'; +import { ProductLine } from '../../common/product'; import type { SecurityProductTypes } from '../../common/config'; import { type Services } from '../common/services'; import { createAiNavigationTree } from './ai_navigation/ai_navigation_tree'; @@ -20,7 +21,7 @@ export const registerSolutionNavigation = async ( productTypes: SecurityProductTypes ) => { const shouldUseAINavigation = productTypes.some( - (productType) => productType.product_tier === ProductTier.searchAiLake + (productType) => productType.product_line === ProductLine.aiSoc ); const chatExperience$ = services.settings.client.get$( @@ -31,8 +32,14 @@ export const registerSolutionNavigation = async ( // Get initial chat experience for setting initial navigation tree const initialChatExperience = await firstValueFrom(chatExperience$); + const workflowsUiEnabled$ = services.settings.client.get$( + WORKFLOWS_UI_SETTING_ID, + false + ); + const workflowsUiEnabled = await firstValueFrom(workflowsUiEnabled$); + const navigationTree = shouldUseAINavigation - ? createAiNavigationTree(initialChatExperience) + ? createAiNavigationTree(initialChatExperience, workflowsUiEnabled) : await createNavigationTree(services, initialChatExperience); services.securitySolution.setSolutionNavigationTree(navigationTree);