Skip to content
Merged
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
3 changes: 3 additions & 0 deletions config/serverless.security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,6 @@ xpack.alerting.rules.run.ruleTypeOverrides:
# These features are disabled in Serverless until fully tested
xpack.securitySolution.enableExperimental:
- privilegedUserMonitoringDisabled

# AI Assistant config
aiAssistantManagementSelection.preferredAIAssistantType: 'security'
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export const SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING =
'securitySolution:enableAssetInventory' as const;
export const SECURITY_SOLUTION_ENABLE_CLOUD_CONNECTOR_SETTING =
'securitySolution:enableCloudConnector' as const;
export const AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE = 'aiAssistant:preferredAIAssistantType';

// Timelion settings
export const TIMELION_ES_DEFAULT_INDEX_ID = 'timelion:es.default_index';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ export const OBSERVABILITY_PROJECT_SETTINGS = [
export const OBSERVABILITY_AI_ASSISTANT_PROJECT_SETTINGS = [
settings.OBSERVABILITY_AI_ASSISTANT_SIMULATED_FUNCTION_CALLING,
settings.OBSERVABILITY_AI_ASSISTANT_SEARCH_CONNECTOR_INDEX_PATTERN,
settings.AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE,
];
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ export const SECURITY_PROJECT_SETTINGS = [
settings.SECURITY_SOLUTION_ENABLE_GRAPH_VISUALIZATION_SETTING,
settings.SECURITY_SOLUTION_ENABLE_ASSET_INVENTORY_SETTING,
settings.SECURITY_SOLUTION_ENABLE_CLOUD_CONNECTOR_SETTING,
settings.AI_ASSISTANT_PREFERRED_AI_ASSISTANT_TYPE,
];
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

export enum AIAssistantType {
Observability = 'observability',
Security = 'security',
Default = 'default',
Never = 'never',
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
"aiAssistantManagementSelection"
],
"requiredPlugins": [
"management"
"management",
],
"optionalPlugins": [
"home",
"serverless",
"features"
"features",
"cloud"
],
"requiredBundles": [
"kibanaReact"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { CoreStart, PluginInitializerContext } from '@kbn/core/public';
import { AIAssistantManagementPlugin } from './plugin';
import { AIAssistantType } from '../common/ai_assistant_type';
import { PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY } from '../common/ui_setting_keys';

describe('AI Assistant Management Selection Plugin', () => {
it('uses the correct setting key to get the correct value from uiSettings', async () => {
const plugin = new AIAssistantManagementPlugin({
config: {
get: jest.fn(),
},
env: { packageInfo: { buildFlavor: 'traditional', branch: 'main' } },
} as unknown as PluginInitializerContext);

const coreStart = {
uiSettings: {
get: jest.fn((key: string) => {
if (key === PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY) {
return AIAssistantType.Default;
}
}),
},
} as unknown as CoreStart;

const result = plugin.start(coreStart);

const collected: any[] = [];
const subscription = result.aiAssistantType$.subscribe((value) => {
collected.push(value);
});
subscription.unsubscribe();

const allCalls = (coreStart.uiSettings.get as jest.Mock).mock.calls;
expect(allCalls).toEqual([['aiAssistant:preferredAIAssistantType']]);
expect(collected).toEqual([AIAssistantType.Default]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const configSchema = schema.object({
schema.literal(AIAssistantType.Default),
schema.literal(AIAssistantType.Never),
schema.literal(AIAssistantType.Observability),
schema.literal(AIAssistantType.Security),
],
{ defaultValue: AIAssistantType.Default }
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { PluginInitializerContext, CoreSetup } from '@kbn/core/server';
import type { AIAssistantManagementSelectionPluginServerDependenciesSetup } from './types';
import { AIAssistantType } from '../common/ai_assistant_type';
import { PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY } from '../common/ui_setting_keys';
import { classicSetting } from './src/settings/classic_setting';
import { observabilitySolutionSetting } from './src/settings/observability_setting';
import { securitySolutionSetting } from './src/settings/security_setting';
import { AIAssistantManagementSelectionPlugin } from './plugin';

describe('plugin', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('stateless', () => {
beforeEach(() => {
jest.clearAllMocks();
});
const initializerContext = {
env: {
packageInfo: {
buildFlavor: 'serverless',
},
},
config: {
get: jest.fn(),
},
} as unknown as PluginInitializerContext;

const coreSetup = {
uiSettings: {
register: jest.fn(),
},
capabilities: {
registerProvider: jest.fn(),
},
} as unknown as CoreSetup;

const setupDeps = {
management: {
sections: {
getSection: jest.fn(),
},
},
serverless: {
uiSettings: {
register: jest.fn(),
},
},
};

it('registers correct uiSettings for serverless oblt', () => {
(initializerContext.config.get as jest.Mock).mockReturnValue({
preferredAIAssistantType: AIAssistantType.Observability,
});
const aiAssistantManagementSelectionPlugin = new AIAssistantManagementSelectionPlugin(
initializerContext
);
aiAssistantManagementSelectionPlugin.setup(coreSetup, {
...setupDeps,
cloud: {
serverless: {
projectType: 'observability',
},
},
} as unknown as AIAssistantManagementSelectionPluginServerDependenciesSetup);

expect(coreSetup.uiSettings.register).toHaveBeenCalledTimes(1);

expect(coreSetup.uiSettings.register).toHaveBeenCalledWith({
[PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
...observabilitySolutionSetting,
value: AIAssistantType.Observability,
},
});
});

it('registers correct uiSettings for serverless security', () => {
(initializerContext.config.get as jest.Mock).mockReturnValue({
preferredAIAssistantType: AIAssistantType.Security,
});
const aiAssistantManagementSelectionPlugin = new AIAssistantManagementSelectionPlugin(
initializerContext
);
aiAssistantManagementSelectionPlugin.setup(coreSetup, {
...setupDeps,
cloud: {
serverless: {
projectType: 'security',
},
},
} as unknown as AIAssistantManagementSelectionPluginServerDependenciesSetup);

expect(coreSetup.uiSettings.register).toHaveBeenCalledTimes(1);

expect(coreSetup.uiSettings.register).toHaveBeenCalledWith({
[PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
...securitySolutionSetting,
value: AIAssistantType.Security,
},
});
});

it('registers correct uiSettings for serverless search', () => {
(initializerContext.config.get as jest.Mock).mockReturnValue({
preferredAIAssistantType: undefined,
});
const aiAssistantManagementSelectionPlugin = new AIAssistantManagementSelectionPlugin(
initializerContext
);
aiAssistantManagementSelectionPlugin.setup(coreSetup, {
...setupDeps,
cloud: {
serverless: {
projectType: 'search',
},
},
} as unknown as AIAssistantManagementSelectionPluginServerDependenciesSetup);

expect(coreSetup.uiSettings.register).toHaveBeenCalledTimes(1);
expect(coreSetup.uiSettings.register).toHaveBeenCalledWith({
[PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
...classicSetting,
value: AIAssistantType.Default,
},
});
});
});

describe('stateful', () => {
it('uses the correct setting key to get the correct value from uiSettings', async () => {
const initializerContext = {
env: {
packageInfo: {
buildFlavor: 'classic',
},
},
config: {
get: jest.fn().mockReturnValue({
preferredAIAssistantType: AIAssistantType.Observability,
}),
},
} as unknown as PluginInitializerContext;
const aiAssistantManagementSelectionPlugin = new AIAssistantManagementSelectionPlugin(
initializerContext
);

const coreSetup = {
uiSettings: {
register: jest.fn(),
},
capabilities: {
registerProvider: jest.fn(),
},
} as unknown as CoreSetup;

const setupDeps = {
management: {
sections: {
getSection: jest.fn(),
},
},
serverless: {
uiSettings: {
register: jest.fn(),
},
},
} as unknown as AIAssistantManagementSelectionPluginServerDependenciesSetup;

aiAssistantManagementSelectionPlugin.setup(coreSetup, setupDeps);

expect(coreSetup.uiSettings.register).toHaveBeenCalledTimes(1);
expect(coreSetup.uiSettings.register).toHaveBeenCalledWith({
[PREFERRED_AI_ASSISTANT_TYPE_SETTING_KEY]: {
...classicSetting,
value: AIAssistantType.Observability,
},
});
});
});
});
Loading