Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
*/
import '@testing-library/jest-dom';
import { act, render, screen } from '@testing-library/react';
import {
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED,
DefaultAIConnector,
} from './default_ai_connector';
import { DefaultAIConnector } from './default_ai_connector';
import { QueryClient, QueryClientProvider } from '@kbn/react-query';
import { I18nProvider } from '@kbn/i18n-react';
import userEvent from '@testing-library/user-event';
Expand Down Expand Up @@ -86,16 +83,7 @@ function setupTest({
} as unknown as ApplicationStart
}
docLinks={{} as DocLinksStart}
featureFlags={
{
getBooleanValue: jest.fn().mockImplementation((flag) => {
if (flag === AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED && enabled) {
return true;
}
return false;
}),
} as unknown as FeatureFlagsStart
}
featureFlags={{} as unknown as FeatureFlagsStart}
toast={{} as IToasts}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,6 @@ import { i18n } from '@kbn/i18n';
import { NO_DEFAULT_CONNECTOR } from '../lib/constants';
import { useDefaultAiConnectorSettingContext } from '../context/default_ai_connector_context';

/** Feature flag for the default AI connector setting */
export const AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED =
'aiAssistant.defaultLlmSettingEnabled' as const;

/** The default value for the default AI connector setting */
export const AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE = true as const;

interface ConnectorData {
connectors?: Array<{
id: string;
Expand Down Expand Up @@ -146,7 +139,7 @@ const getOptionsByValues = (
};

export const DefaultAIConnector: React.FC<Props> = ({ connectors, settings }) => {
const { toast, application, docLinks, featureFlags } = useDefaultAiConnectorSettingContext();
const { toast, application, docLinks } = useDefaultAiConnectorSettingContext();
const options = useMemo(() => getOptions(connectors), [connectors]);
const { handleFieldChange, fields, unsavedChanges } = settings;

Expand Down Expand Up @@ -276,15 +269,6 @@ export const DefaultAIConnector: React.FC<Props> = ({ connectors, settings }) =>
);
}, [elasticManagedLlmExists, application, docLinks]);

if (
!featureFlags.getBooleanValue(
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED,
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE
)
) {
return null;
}

return (
<>
<EuiDescribedFormGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,6 @@ describe('SettingsTab', () => {
});
useKibanaMock.mockReturnValue({
services: {
featureFlags: {
getBooleanValue: jest.fn().mockImplementation((flag) => {
if (flag === 'aiAssistant.defaultLlmSettingEnabled') {
return true;
}
return false;
}),
},
application: {
getUrlForApp: getUrlForAppMock,
capabilities: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,6 @@ export const ENABLE_NEWS_FEED_SETTING = 'securitySolution:enableNewsFeed' as con
/** This Kibana Advanced Setting sets a default AI connector for serverless AI features (AI for SOC) */
export const DEFAULT_AI_CONNECTOR = 'securitySolution:defaultAIConnector' as const;

/** Feature flag for the default AI connector setting */
export const AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED =
'aiAssistant.defaultLlmSettingEnabled' as const;

/** The default value for the default AI connector setting */
export const AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE = true as const;

/** This Kibana Advanced Setting allows users to enable/disable querying cold and frozen data tiers in analyzer */
export const EXCLUDE_COLD_AND_FROZEN_TIERS_IN_ANALYZER =
'securitySolution:excludeColdAndFrozenTiersInAnalyzer' as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import { useDefaultAIConnectorId } from './use_default_ai_connector_id';
import { useKibana } from '../lib/kibana';
import { useAIConnectors } from './use_ai_connectors';
import { getDefaultConnector } from '@kbn/elastic-assistant/impl/assistant/helpers';
import {
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED,
DEFAULT_AI_CONNECTOR,
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE,
} from '../../../common/constants';

jest.mock('../lib/kibana');
jest.mock('./use_ai_connectors');
Expand All @@ -26,12 +21,6 @@ const mockGetDefaultConnector = getDefaultConnector as jest.Mock;

describe('useDefaultAIConnectorId', () => {
const mockSettings = {};
const mockUiSettings = {
get: jest.fn(),
};
const mockFeatureFlags = {
getBooleanValue: jest.fn(),
};
const mockConnectors = [
{ id: 'connector-1', name: 'Connector 1' },
{ id: 'connector-2', name: 'Connector 2' },
Expand All @@ -43,8 +32,6 @@ describe('useDefaultAIConnectorId', () => {
mockUseKibana.mockReturnValue({
services: {
settings: mockSettings,
uiSettings: mockUiSettings,
featureFlags: mockFeatureFlags,
},
});

Expand All @@ -53,75 +40,37 @@ describe('useDefaultAIConnectorId', () => {
isLoading: false,
});

mockUiSettings.get.mockReturnValue('legacy-connector-id');
mockFeatureFlags.getBooleanValue.mockReturnValue(false);
mockGetDefaultConnector.mockReturnValue({ id: 'new-connector-id' });
mockGetDefaultConnector.mockReturnValue({ id: 'connector-id' });
});

it('should return legacy connector id when new default connector feature is disabled', () => {
mockFeatureFlags.getBooleanValue.mockReturnValue(false);

const { result } = renderHook(() => useDefaultAIConnectorId());

expect(result.current.defaultConnectorId).toBe('legacy-connector-id');
});

it('should return new connector id when new default connector feature is enabled', () => {
mockFeatureFlags.getBooleanValue.mockReturnValue(true);

it('should return connector id from getDefaultConnector', () => {
const { result } = renderHook(() => useDefaultAIConnectorId());

expect(result.current.defaultConnectorId).toBe('new-connector-id');
expect(result.current.defaultConnectorId).toBe('connector-id');
});

it('should return undefined when new default connector feature is enabled but getDefaultConnector returns undefined', () => {
mockFeatureFlags.getBooleanValue.mockReturnValue(true);
it('should return undefined when getDefaultConnector returns undefined', () => {
mockGetDefaultConnector.mockReturnValue(undefined);

const { result } = renderHook(() => useDefaultAIConnectorId());

expect(result.current.defaultConnectorId).toBeUndefined();
});

it('should return undefined when new default connector feature is enabled but getDefaultConnector returns null', () => {
mockFeatureFlags.getBooleanValue.mockReturnValue(true);
it('should return undefined when getDefaultConnector returns null', () => {
mockGetDefaultConnector.mockReturnValue(null);

const { result } = renderHook(() => useDefaultAIConnectorId());

expect(result.current.defaultConnectorId).toBeUndefined();
});

it('should call getBooleanValue with correct parameters', () => {
renderHook(() => useDefaultAIConnectorId());

expect(mockFeatureFlags.getBooleanValue).toHaveBeenCalledWith(
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED,
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE
);
});

it('should call uiSettings.get with correct parameter', () => {
renderHook(() => useDefaultAIConnectorId());

expect(mockUiSettings.get).toHaveBeenCalledWith(DEFAULT_AI_CONNECTOR);
});

it('should call getDefaultConnector with correct parameters', () => {
renderHook(() => useDefaultAIConnectorId());

expect(mockGetDefaultConnector).toHaveBeenCalledWith(mockConnectors, mockSettings);
});

it('should return undefined when legacy connector id is undefined and new feature is disabled', () => {
mockUiSettings.get.mockReturnValue(undefined);
mockFeatureFlags.getBooleanValue.mockReturnValue(false);

const { result } = renderHook(() => useDefaultAIConnectorId());

expect(result.current.defaultConnectorId).toBeUndefined();
});

it('should return isLoading true when connectors are loading', () => {
mockUseAIConnectors.mockReturnValue({
aiConnectors: mockConnectors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,20 @@

import { getDefaultConnector } from '@kbn/elastic-assistant/impl/assistant/helpers';
import { useMemo } from 'react';
import {
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED,
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE,
DEFAULT_AI_CONNECTOR,
} from '../../../common/constants';
import { useAIConnectors } from './use_ai_connectors';
import { useKibana } from '../lib/kibana';

export const useDefaultAIConnectorId = () => {
const { settings, uiSettings, featureFlags } = useKibana().services;
const { settings } = useKibana().services;

const { aiConnectors: connectors, isLoading: isLoadingConnectors } = useAIConnectors();
const legacyDefaultConnectorId = uiSettings.get<string>(DEFAULT_AI_CONNECTOR);
const useNewDefaultConnector = featureFlags.getBooleanValue(
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED,
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE
);
const newDefaultConnectorId = getDefaultConnector(connectors, settings)?.id;
const defaultConnectorId = getDefaultConnector(connectors, settings)?.id;

return useMemo(
() => ({
defaultConnectorId: useNewDefaultConnector ? newDefaultConnectorId : legacyDefaultConnectorId,
defaultConnectorId,
isLoading: isLoadingConnectors,
}),
[useNewDefaultConnector, newDefaultConnectorId, legacyDefaultConnectorId, isLoadingConnectors]
[defaultConnectorId, isLoadingConnectors]
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,10 @@ import {
} from './connector_missing_callout';
import { useNavigateTo } from '@kbn/security-solution-navigation';

const mockedUseKibana = {
services: {
featureFlags: {
getBooleanValue: jest.fn().mockReturnValue(false),
},
},
};

jest.mock('@kbn/security-solution-navigation');
jest.mock('../../../common/lib/kibana', () => {
return {
...jest.requireActual('../../../common/lib/kibana'),
useKibana: () => mockedUseKibana,
};
});
Comment on lines 18 to 22
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mock for useKibana is now unnecessary since the component no longer imports or uses useKibana. This empty mock can be removed to clean up the test file.

Copilot uses AI. Check for mistakes.

Expand Down Expand Up @@ -57,23 +48,6 @@ describe('ConnectorMissingCallout', () => {

getByTestId(MISSING_CONNECTOR_CALLOUT_LINK_TEST_ID).click();

expect(navigateTo).toHaveBeenCalledWith({
appId: 'management',
path: '/kibana/settings?query=defaultAIConnector',
});
});

it('should call navigateTo genAiSettings when clicking on link and useNewDefaultConnector is true', () => {
const navigateTo = jest.fn();
(useNavigateTo as jest.Mock).mockReturnValue({
navigateTo,
});
mockedUseKibana.services.featureFlags.getBooleanValue.mockReturnValue(true);

const { getByTestId } = render(<ConnectorMissingCallout canSeeAdvancedSettings={true} />);

getByTestId(MISSING_CONNECTOR_CALLOUT_LINK_TEST_ID).click();

expect(navigateTo).toHaveBeenCalledWith({
appId: 'management',
path: '/ai/genAiSettings',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ import React, { memo, useCallback } from 'react';
import { EuiCallOut, EuiLink } from '@elastic/eui';
import { useNavigateTo } from '@kbn/security-solution-navigation';
import { i18n } from '@kbn/i18n';
import { useKibana } from '../../../common/lib/kibana';
import {
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED,
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE,
} from '../../../../common/constants';

const MISSING_CONNECTOR = i18n.translate('xpack.securitySolution.alertSummary.missingConnector', {
defaultMessage: 'Missing connector',
Expand Down Expand Up @@ -56,21 +51,14 @@ export interface ConnectorMissingCalloutProps {
*/
export const ConnectorMissingCallout = memo(
({ canSeeAdvancedSettings }: ConnectorMissingCalloutProps) => {
const { featureFlags } = useKibana().services;
const useNewDefaultConnector = featureFlags.getBooleanValue(
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED,
AI_ASSISTANT_DEFAULT_LLM_SETTING_ENABLED_VALUE
);
const { navigateTo } = useNavigateTo();
const goToKibanaSettings = useCallback(
() =>
navigateTo({
appId: 'management',
path: useNewDefaultConnector
? '/ai/genAiSettings'
: '/kibana/settings?query=defaultAIConnector',
path: '/ai/genAiSettings',
}),
[navigateTo, useNewDefaultConnector]
[navigateTo]
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import { i18n } from '@kbn/i18n';
import { schema } from '@kbn/config-schema';

import type { CoreSetup, UiSettingsParams } from '@kbn/core/server';
import type { Connector } from '@kbn/actions-plugin/server/application/connector/types';
import type { ReadonlyModeType } from '@kbn/core-ui-settings-common';
import {
APP_ID,
DEFAULT_ALERT_TAGS_KEY,
Expand Down Expand Up @@ -44,7 +42,6 @@ import {
NEWS_FEED_URL_SETTING_DEFAULT,
SHOW_RELATED_INTEGRATIONS_SETTING,
ENABLE_PRIVILEGED_USER_MONITORING_SETTING,
DEFAULT_AI_CONNECTOR,
} from '../common/constants';
import type { ExperimentalFeatures } from '../common/experimental_features';
import { LogLevelSetting } from '../common/api/detection_engine/rule_monitoring';
Expand Down Expand Up @@ -532,36 +529,3 @@ export const initUiSettings = (

uiSettings.register(orderSettings(securityUiSettings));
};

export const getDefaultAIConnectorSetting = (
connectors: Connector[],
readonlyMode?: ReadonlyModeType
): SettingsConfig | null =>
connectors.length > 0
? {
[DEFAULT_AI_CONNECTOR]: {
name: i18n.translate('xpack.securitySolution.uiSettings.defaultAIConnectorLabel', {
defaultMessage: 'Default AI Connector',
}),
// TODO, make Elastic LLM the default value once fully available in serverless
value: connectors[0].id,
description: i18n.translate(
'xpack.securitySolution.uiSettings.defaultAIConnectorDescription',
{
// TODO update this copy, waiting on James Spiteri's input
defaultMessage:
'Default AI connector for serverless AI features (Elastic AI SOC Engine)',
}
),
type: 'select',
options: connectors.map(({ id }) => id),
optionLabels: Object.fromEntries(connectors.map(({ id, name }) => [id, name])),
category: [APP_ID],
requiresPageReload: true,
schema: schema.string(),
solution: 'security',
readonlyMode,
readonly: readonlyMode !== undefined,
},
}
: null;
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@
"@kbn/spaces-utils",
"@kbn/core-metrics-server",
"@kbn/ai-assistant-default-llm-setting",
"@kbn/core-ui-settings-common",
"@kbn/react-query"
]
}
Loading