Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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: 2 additions & 1 deletion x-pack/platform/plugins/shared/alerting_v2/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"spaces",
"data",
"security",
"encryptedSavedObjects"
"encryptedSavedObjects",
"workflowsManagement"
],
"optionalPlugins": ["management"],
"extraPublicDirs": []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
import type { BulkResponse } from '@elastic/elasticsearch/lib/api/types';
import type { DeeplyMockedApi } from '@kbn/core-elasticsearch-client-server-mocks';
import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server';
import type { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
import type { WorkflowsServerPluginSetup } from '@kbn/workflows-management-plugin/server';
import moment from 'moment';
import { ALERT_ACTIONS_DATA_STREAM, type AlertAction } from '../../resources/alert_actions';
import { RULE_SAVED_OBJECT_TYPE, NOTIFICATION_POLICY_SAVED_OBJECT_TYPE } from '../../saved_objects';
import type { RuleSavedObjectAttributes } from '../../saved_objects';
import { createRuleSoAttributes } from '../test_utils';
import { createLoggerService } from '../services/logger_service/logger_service.mock';
import type { NotificationPolicySavedObjectServiceContract } from '../services/notification_policy_saved_object_service/notification_policy_saved_object_service';
import { createNotificationPolicySavedObjectService } from '../services/notification_policy_saved_object_service/notification_policy_saved_object_service.mock';
import type { QueryServiceContract } from '../services/query_service/query_service';
import { createQueryService } from '../services/query_service/query_service.mock';
import type { RulesSavedObjectServiceContract } from '../services/rules_saved_object_service/rules_saved_object_service';
Expand Down Expand Up @@ -63,42 +63,58 @@ function mockRulesBulkGet(
});
}

function mockNpBulkGet(mockSoClient: jest.Mocked<SavedObjectsClientContract>, policyIds: string[]) {
mockSoClient.bulkGet.mockResolvedValue({
saved_objects: policyIds.map((id) => ({
id,
type: NOTIFICATION_POLICY_SAVED_OBJECT_TYPE,
attributes: {
name: `Policy ${id}`,
description: `Description for ${id}`,
destinations: [{ type: 'workflow', id: 'workflow-test-id' }],
createdBy: null,
updatedBy: null,
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-01-01T00:00:00.000Z',
},
references: [],
})),
});
function createMockEsoClient(policyIds: string[]): jest.Mocked<EncryptedSavedObjectsClient> {
const savedObjects = policyIds.map((id) => ({
id,
type: NOTIFICATION_POLICY_SAVED_OBJECT_TYPE,
attributes: {
name: `Policy ${id}`,
description: `Description for ${id}`,
destinations: [{ type: 'workflow', id: 'workflow-test-id' }],
auth: { apiKey: 'test-api-key', owner: 'elastic', createdByUser: false },
createdBy: null,
updatedBy: null,
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-01-01T00:00:00.000Z',
},
references: [],
}));

const client = {
createPointInTimeFinderDecryptedAsInternalUser: jest.fn().mockResolvedValue({
find: jest.fn().mockImplementation(async function* () {
yield { saved_objects: savedObjects };
}),
close: jest.fn(),
}),
} as unknown as jest.Mocked<EncryptedSavedObjectsClient>;
return client;
}

const createMockWorkflowsManagement = (): jest.Mocked<WorkflowsServerPluginSetup['management']> =>
({
getWorkflow: jest.fn().mockResolvedValue(null),
runWorkflow: jest.fn().mockResolvedValue('exec-1'),
} as unknown as jest.Mocked<WorkflowsServerPluginSetup['management']>);

function buildDispatcherService(deps: {
queryService: QueryServiceContract;
storageService: StorageServiceContract;
rulesSoService: RulesSavedObjectServiceContract;
npSoService: NotificationPolicySavedObjectServiceContract;
esoClient: EncryptedSavedObjectsClient;
workflowsManagement: WorkflowsServerPluginSetup['management'];
}): DispatcherService {
const { loggerService } = createLoggerService();
const pipeline = new DispatcherPipeline(loggerService, [
new FetchEpisodesStep(deps.queryService),
new FetchSuppressionsStep(deps.queryService),
new ApplySuppressionStep(),
new FetchRulesStep(deps.rulesSoService),
new FetchPoliciesStep(deps.npSoService),
new FetchPoliciesStep(deps.esoClient),
new EvaluateMatchersStep(),
new BuildGroupsStep(),
new ApplyThrottlingStep(deps.queryService, loggerService),
new DispatchStep(loggerService),
new DispatchStep(loggerService, deps.workflowsManagement),
new StoreActionsStep(deps.storageService),
]);
return new DispatcherService(pipeline);
Expand All @@ -111,9 +127,9 @@ describe('DispatcherService', () => {
let queryEsClient: DeeplyMockedApi<ElasticsearchClient>;
let storageEsClient: jest.Mocked<ElasticsearchClient>;
let rulesSoService: RulesSavedObjectServiceContract;
let npSoService: NotificationPolicySavedObjectServiceContract;
let rulesMockSoClient: jest.Mocked<SavedObjectsClientContract>;
let npMockSoClient: jest.Mocked<SavedObjectsClientContract>;
let mockEsoClient: jest.Mocked<EncryptedSavedObjectsClient>;
let mockWfm: jest.Mocked<WorkflowsServerPluginSetup['management']>;

beforeEach(() => {
({ queryService, mockEsClient: queryEsClient } = createQueryService());
Expand All @@ -124,16 +140,15 @@ describe('DispatcherService', () => {
rulesMockSoClient = rulesMock.mockSavedObjectsClient;
mockRulesBulkGet(rulesMockSoClient, ['rule-1', 'rule-2']);

const npMock = createNotificationPolicySavedObjectService();
npSoService = npMock.notificationPolicySavedObjectService;
npMockSoClient = npMock.mockSavedObjectsClient;
mockNpBulkGet(npMockSoClient, ['policy_456']);
mockEsoClient = createMockEsoClient(['policy_456']);
mockWfm = createMockWorkflowsManagement();

dispatcherService = buildDispatcherService({
queryService,
storageService,
rulesSoService,
npSoService,
esoClient: mockEsoClient,
workflowsManagement: mockWfm,
});
});

Expand Down Expand Up @@ -358,16 +373,15 @@ describe('DispatcherService', () => {
'rule-005',
]);

const npMock = createNotificationPolicySavedObjectService();
npSoService = npMock.notificationPolicySavedObjectService;
npMockSoClient = npMock.mockSavedObjectsClient;
mockNpBulkGet(npMockSoClient, ['policy_456']);
mockEsoClient = createMockEsoClient(['policy_456']);
mockWfm = createMockWorkflowsManagement();

dispatcherService = buildDispatcherService({
queryService,
storageService,
rulesSoService,
npSoService,
esoClient: mockEsoClient,
workflowsManagement: mockWfm,
});

// Dataset: 5 rules, 9 episodes total
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,30 @@

import type { TestElasticsearchUtils, TestKibanaUtils } from '@kbn/core-test-helpers-kbn-server';
import type { ElasticsearchClient } from '@kbn/core/server';
import type { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
import type { WorkflowsServerPluginSetup } from '@kbn/workflows-management-plugin/server';
import { ALERT_ACTIONS_DATA_STREAM, type AlertAction } from '../../../resources/alert_actions';
import { ALERT_EVENTS_DATA_STREAM, type AlertEvent } from '../../../resources/alert_events';
import { NOTIFICATION_POLICY_SAVED_OBJECT_TYPE } from '../../../saved_objects';
import type {
RuleSavedObjectAttributes,
NotificationPolicySavedObjectAttributes,
} from '../../../saved_objects';
import type { LoggerServiceContract } from '../../services/logger_service/logger_service';
import { createLoggerService } from '../../services/logger_service/logger_service.mock';
import { NotificationPolicySavedObjectService } from '../../services/notification_policy_saved_object_service/notification_policy_saved_object_service';
import type { NotificationPolicySavedObjectServiceContract } from '../../services/notification_policy_saved_object_service/notification_policy_saved_object_service';
import {
QueryService,
type QueryServiceContract,
} from '../../services/query_service/query_service';
import { RulesSavedObjectService } from '../../services/rules_saved_object_service/rules_saved_object_service';
import type { RulesSavedObjectServiceContract } from '../../services/rules_saved_object_service/rules_saved_object_service';
import {
StorageService,
type StorageServiceContract,
} from '../../services/storage_service/storage_service';
import type { SpacesPluginStart } from '@kbn/spaces-plugin/server';
import { NotificationPolicySavedObjectService } from '../../services/notification_policy_saved_object_service/notification_policy_saved_object_service';
import type { NotificationPolicySavedObjectServiceContract } from '../../services/notification_policy_saved_object_service/notification_policy_saved_object_service';
import { RulesSavedObjectService } from '../../services/rules_saved_object_service/rules_saved_object_service';
import type { RulesSavedObjectServiceContract } from '../../services/rules_saved_object_service/rules_saved_object_service';
import type {
RuleSavedObjectAttributes,
NotificationPolicySavedObjectAttributes,
} from '../../../saved_objects';
import { DispatcherService, type DispatcherServiceContract } from '../dispatcher';
import { DispatcherPipeline } from '../execution_pipeline';
import {
Expand Down Expand Up @@ -333,6 +336,37 @@ const SUPPRESSION_USER_ACTIONS: AlertAction[] = [
},
];

const createMockEsoClient = (
npSoService: NotificationPolicySavedObjectServiceContract
): EncryptedSavedObjectsClient =>
({
createPointInTimeFinderDecryptedAsInternalUser: jest.fn().mockImplementation(async () => {
const { saved_objects: allPolicies } = await npSoService.find({
page: 1,
perPage: 1000,
});
return {
async *find() {
yield {
saved_objects: allPolicies.map((doc) => ({
id: doc.id,
type: NOTIFICATION_POLICY_SAVED_OBJECT_TYPE,
attributes: doc.attributes,
references: [],
})),
};
},
close: jest.fn(),
};
}),
} as unknown as EncryptedSavedObjectsClient);

const createMockWorkflowsManagement = (): WorkflowsServerPluginSetup['management'] =>
({
getWorkflow: jest.fn().mockResolvedValue(null),
runWorkflow: jest.fn().mockResolvedValue('exec-1'),
} as unknown as WorkflowsServerPluginSetup['management']);

describe('DispatcherService integration tests', () => {
let esServer: TestElasticsearchUtils;
let kibanaServer: TestKibanaUtils;
Expand All @@ -343,6 +377,8 @@ describe('DispatcherService integration tests', () => {
let mockLoggerService: LoggerServiceContract;
let rulesSoService: RulesSavedObjectServiceContract;
let npSoService: NotificationPolicySavedObjectServiceContract;
let mockEsoClient: EncryptedSavedObjectsClient;
let mockWfm: WorkflowsServerPluginSetup['management'];

beforeAll(async () => {
const servers = await setupTestServers();
Expand Down Expand Up @@ -380,17 +416,19 @@ describe('DispatcherService integration tests', () => {

queryService = new QueryService(esClient, mockLoggerService);
storageService = new StorageService(esClient, mockLoggerService);
mockEsoClient = createMockEsoClient(npSoService);
mockWfm = createMockWorkflowsManagement();

const pipeline = new DispatcherPipeline(mockLoggerService, [
new FetchEpisodesStep(queryService),
new FetchSuppressionsStep(queryService),
new ApplySuppressionStep(),
new FetchRulesStep(rulesSoService),
new FetchPoliciesStep(npSoService),
new FetchPoliciesStep(mockEsoClient),
new EvaluateMatchersStep(),
new BuildGroupsStep(),
new ApplyThrottlingStep(queryService, mockLoggerService),
new DispatchStep(mockLoggerService),
new DispatchStep(mockLoggerService, mockWfm),
new StoreActionsStep(storageService),
]);
dispatcherService = new DispatcherService(pipeline);
Expand Down
Loading