diff --git a/x-pack/plugins/alerting/server/saved_objects/index.ts b/x-pack/plugins/alerting/server/saved_objects/index.ts index eb07a84950d14..a3bb0b4f0afe8 100644 --- a/x-pack/plugins/alerting/server/saved_objects/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/index.ts @@ -23,7 +23,7 @@ import { RawRule } from '../types'; import { getImportWarnings } from './get_import_warnings'; import { isRuleExportable } from './is_rule_exportable'; import { RuleTypeRegistry } from '../rule_type_registry'; -export { partiallyUpdateRule } from './partially_update_rule'; +export { partiallyUpdateRule, partiallyUpdateRuleWithEs } from './partially_update_rule'; import { RULES_SETTINGS_SAVED_OBJECT_TYPE, MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.test.ts b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.test.ts index 5fcf23cbae6fb..294bc81481540 100644 --- a/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.test.ts @@ -10,16 +10,23 @@ import { ISavedObjectsRepository, SavedObjectsErrorHelpers, } from '@kbn/core/server'; - -import { PartiallyUpdateableRuleAttributes, partiallyUpdateRule } from './partially_update_rule'; -import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { + PartiallyUpdateableRuleAttributes, + partiallyUpdateRule, + partiallyUpdateRuleWithEs, +} from './partially_update_rule'; +import { elasticsearchServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; import { RULE_SAVED_OBJECT_TYPE } from '.'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { estypes } from '@elastic/elasticsearch'; +import { RuleExecutionStatuses } from '@kbn/alerting-types'; const MockSavedObjectsClientContract = savedObjectsClientMock.create(); const MockISavedObjectsRepository = MockSavedObjectsClientContract as unknown as jest.Mocked; +const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; -describe('partially_update_rule', () => { +describe('partiallyUpdateRule', () => { beforeEach(() => { jest.resetAllMocks(); }); @@ -104,6 +111,101 @@ describe('partially_update_rule', () => { }); }); +describe('partiallyUpdateRuleWithEs', () => { + beforeEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + }); + + test('should work with no options', async () => { + esClient.update.mockResolvedValueOnce(MockEsUpdateResponse(MockRuleId)); + + await partiallyUpdateRuleWithEs(esClient, MockRuleId, DefaultAttributesForEsUpdate); + expect(esClient.update).toHaveBeenCalledTimes(1); + expect(esClient.update).toHaveBeenCalledWith({ + id: `alert:${MockRuleId}`, + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + doc: { + alert: DefaultAttributesForEsUpdate, + }, + }); + }); + + test('should strip unallowed attributes ', async () => { + const attributes = + AttributesForEsUpdateWithUnallowedFields as unknown as PartiallyUpdateableRuleAttributes; + esClient.update.mockResolvedValueOnce(MockEsUpdateResponse(MockRuleId)); + + await partiallyUpdateRuleWithEs(esClient, MockRuleId, attributes); + expect(esClient.update).toHaveBeenCalledWith({ + id: `alert:${MockRuleId}`, + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + doc: { + alert: DefaultAttributesForEsUpdate, + }, + }); + }); + + test('should handle ES errors', async () => { + esClient.update.mockRejectedValueOnce(new Error('wops')); + + await expect( + partiallyUpdateRuleWithEs(esClient, MockRuleId, DefaultAttributes) + ).rejects.toThrowError('wops'); + }); + + test('should handle the version option', async () => { + esClient.update.mockResolvedValueOnce(MockEsUpdateResponse(MockRuleId)); + + await partiallyUpdateRuleWithEs(esClient, MockRuleId, DefaultAttributesForEsUpdate, { + version: 'WzQsMV0=', + }); + expect(esClient.update).toHaveBeenCalledWith({ + id: `alert:${MockRuleId}`, + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + if_primary_term: 1, + if_seq_no: 4, + doc: { + alert: DefaultAttributesForEsUpdate, + }, + }); + }); + + test('should handle the ignore404 option', async () => { + esClient.update.mockResolvedValueOnce(MockEsUpdateResponse(MockRuleId)); + + await partiallyUpdateRuleWithEs(esClient, MockRuleId, DefaultAttributesForEsUpdate, { + ignore404: true, + }); + expect(esClient.update).toHaveBeenCalledWith( + { + id: `alert:${MockRuleId}`, + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + doc: { + alert: DefaultAttributesForEsUpdate, + }, + }, + { ignore: [404] } + ); + }); + + test('should handle the refresh option', async () => { + esClient.update.mockResolvedValueOnce(MockEsUpdateResponse(MockRuleId)); + + await partiallyUpdateRuleWithEs(esClient, MockRuleId, DefaultAttributesForEsUpdate, { + refresh: 'wait_for', + }); + expect(esClient.update).toHaveBeenCalledWith({ + id: `alert:${MockRuleId}`, + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + doc: { + alert: DefaultAttributesForEsUpdate, + }, + refresh: 'wait_for', + }); + }); +}); + function getMockSavedObjectClients(): Record< string, jest.Mocked @@ -126,6 +228,50 @@ const DefaultAttributes = { const ExtraneousAttributes = { ...DefaultAttributes, foo: 'bar' }; +const DefaultAttributesForEsUpdate = { + running: false, + executionStatus: { + status: 'active' as RuleExecutionStatuses, + lastExecutionDate: '2023-01-01T08:44:40.000Z', + lastDuration: 12, + error: null, + warning: null, + }, + monitoring: { + run: { + calculated_metrics: { + success_ratio: 20, + }, + history: [ + { + success: true, + timestamp: 1640991880000, + duration: 12, + outcome: 'success', + }, + ], + last_run: { + timestamp: '2023-01-01T08:44:40.000Z', + metrics: { + duration: 12, + gap_duration_s: null, + total_alerts_created: null, + total_alerts_detected: null, + total_indexing_duration_ms: null, + total_search_duration_ms: null, + }, + }, + }, + }, +}; + +const AttributesForEsUpdateWithUnallowedFields = { + ...DefaultAttributesForEsUpdate, + alertTypeId: 'foo', + consumer: 'consumer', + randomField: 'bar', +}; + const MockRuleId = 'rule-id'; const MockUpdateValue = { @@ -137,3 +283,13 @@ const MockUpdateValue = { }, references: [], }; + +const MockEsUpdateResponse = (id: string) => ({ + _index: '.kibana_alerting_cases_9.0.0_001', + _id: `alert:${id}`, + _version: 3, + result: 'updated' as estypes.Result, + _shards: { total: 1, successful: 1, failed: 0 }, + _seq_no: 5, + _primary_term: 1, +}); diff --git a/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts index 2665845a1110f..f9b4da5ed767b 100644 --- a/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts +++ b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts @@ -7,10 +7,13 @@ import { omit, pick } from 'lodash'; import { + ElasticsearchClient, SavedObjectsClient, SavedObjectsErrorHelpers, SavedObjectsUpdateOptions, } from '@kbn/core/server'; +import { decodeRequestVersion } from '@kbn/core-saved-objects-base-server-internal'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { RawRule } from '../types'; import { @@ -67,3 +70,50 @@ export async function partiallyUpdateRule( throw err; } } + +// Explicit list of attributes that we allow to be partially updated +// There should be no overlap between this list and RuleAttributesIncludedInAAD or RuleAttributesToEncrypt +const RuleAttributesAllowedForPartialUpdate = [ + 'executionStatus', + 'lastRun', + 'monitoring', + 'nextRun', + 'running', +]; + +// direct, partial update to a rule saved object via ElasticsearchClient + +// we do this direct partial update to avoid the overhead of the SavedObjectsClient for +// only these allow-listed fields which don't impact encryption. in addition, because these +// fields are only updated by the system user at the end of a rule run, they should not +// need to be included in any (user-centric) audit logs. +export async function partiallyUpdateRuleWithEs( + esClient: ElasticsearchClient, + id: string, + attributes: PartiallyUpdateableRuleAttributes, + options: PartiallyUpdateRuleSavedObjectOptions = {} +): Promise { + // ensure we only have the valid attributes that are not encrypted and are excluded from AAD + const attributeUpdates = omit(attributes, [ + ...RuleAttributesToEncrypt, + ...RuleAttributesIncludedInAAD, + ]); + // ensure we only have attributes that we explicitly allow to be updated + const attributesAllowedForUpdate = pick(attributeUpdates, RuleAttributesAllowedForPartialUpdate); + + const updateParams = { + id: `alert:${id}`, + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + ...(options.version ? decodeRequestVersion(options.version) : {}), + doc: { + alert: attributesAllowedForUpdate, + }, + ...(options.refresh ? { refresh: options.refresh } : {}), + }; + + if (options.ignore404) { + await esClient.update(updateParams, { ignore: [404] }); + } else { + await esClient.update(updateParams); + } +} diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index ae8eccfcb1f86..5174aa9b965ec 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -7,6 +7,7 @@ import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { SavedObject } from '@kbn/core/server'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { Rule, RuleTypeParams, @@ -64,7 +65,7 @@ const defaultHistory = [ }, ]; -export const generateSavedObjectParams = ({ +export const generateRuleUpdateParams = ({ error = null, warning = null, status = 'ok', @@ -83,53 +84,59 @@ export const generateSavedObjectParams = ({ history?: RuleMonitoring['run']['history']; alertsCount?: Record; }) => [ - RULE_SAVED_OBJECT_TYPE, - '1', { - monitoring: { - run: { - calculated_metrics: { - success_ratio: successRatio, + id: `alert:1`, + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + doc: { + alert: { + monitoring: { + run: { + calculated_metrics: { + success_ratio: successRatio, + }, + history, + last_run: { + timestamp: '1970-01-01T00:00:00.000Z', + metrics: { + duration: 0, + gap_duration_s: null, + total_alerts_created: null, + total_alerts_detected: null, + total_indexing_duration_ms: null, + total_search_duration_ms: null, + }, + }, + }, }, - history, - last_run: { - timestamp: '1970-01-01T00:00:00.000Z', - metrics: { - duration: 0, - gap_duration_s: null, - total_alerts_created: null, - total_alerts_detected: null, - total_indexing_duration_ms: null, - total_search_duration_ms: null, + executionStatus: { + error, + lastDuration: 0, + lastExecutionDate: '1970-01-01T00:00:00.000Z', + status, + warning, + }, + lastRun: { + outcome, + outcomeOrder: RuleLastRunOutcomeOrderMap[outcome], + outcomeMsg: + (error?.message && [error?.message]) || + (warning?.message && [warning?.message]) || + null, + warning: error?.reason || warning?.reason || null, + alertsCount: { + active: 0, + ignored: 0, + new: 0, + recovered: 0, + ...(alertsCount || {}), }, }, + nextRun, + running: false, }, }, - executionStatus: { - error, - lastDuration: 0, - lastExecutionDate: '1970-01-01T00:00:00.000Z', - status, - warning, - }, - lastRun: { - outcome, - outcomeOrder: RuleLastRunOutcomeOrderMap[outcome], - outcomeMsg: - (error?.message && [error?.message]) || (warning?.message && [warning?.message]) || null, - warning: error?.reason || warning?.reason || null, - alertsCount: { - active: 0, - ignored: 0, - new: 0, - recovered: 0, - ...(alertsCount || {}), - }, - }, - nextRun, - running: false, }, - { refresh: false, namespace: undefined }, + { ignore: [404] }, ]; export const GENERIC_ERROR_MESSAGE = 'GENERIC ERROR MESSAGE'; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 438ffb3685e2a..e06c260109b76 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -59,7 +59,7 @@ import { generateRunnerResult, RULE_ACTIONS, generateEnqueueFunctionInput, - generateSavedObjectParams, + generateRuleUpdateParams, mockTaskInstance, GENERIC_ERROR_MESSAGE, generateAlertInstance, @@ -341,8 +341,8 @@ describe('Task Runner', () => { testAlertingEventLogCalls({ status: 'ok' }); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({}) + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({}) ); expect(taskRunnerFactoryInitializerParams.executionContext.withContext).toBeCalledTimes(1); @@ -2676,8 +2676,8 @@ describe('Task Runner', () => { status: 'ok', }); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({}) + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({}) ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -2789,10 +2789,8 @@ describe('Task Runner', () => { }); await taskRunner.run(); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({ - nextRun: '1970-01-01T00:00:10.000Z', - }) + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({ nextRun: '1970-01-01T00:00:10.000Z' }) ); }); @@ -2825,21 +2823,14 @@ describe('Task Runner', () => { ); await taskRunner.run(); ruleType.executor.mockClear(); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({ - error: { - message: GENERIC_ERROR_MESSAGE, - reason: 'execute', - }, + + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({ + error: { message: GENERIC_ERROR_MESSAGE, reason: 'execute' }, outcome: 'failed', status: 'error', successRatio: 0, - history: [ - { - success: false, - timestamp: 0, - }, - ], + history: [{ success: false, timestamp: 0 }], }) ); }); @@ -2947,15 +2938,12 @@ describe('Task Runner', () => { expect(actionsClient.bulkEnqueueExecution).toHaveBeenCalledTimes(1); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({ + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({ status: 'warning', outcome: 'warning', warning, - alertsCount: { - active: 1, - new: 1, - }, + alertsCount: { active: 1, new: 1 }, }) ); @@ -3117,15 +3105,12 @@ describe('Task Runner', () => { expect(actionsClient.bulkEnqueueExecution).toHaveBeenCalledTimes(1); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({ + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({ status: 'warning', outcome: 'warning', warning, - alertsCount: { - active: 2, - new: 2, - }, + alertsCount: { active: 2, new: 2 }, }) ); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index e01dd73df7e58..b5a1854581bf3 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -42,7 +42,7 @@ import { import { asErr, asOk, isErr, isOk, map, resolveErr, Result } from '../lib/result_type'; import { taskInstanceToAlertTaskInstance } from './alert_task_instance'; import { isAlertSavedObjectNotFoundError, isEsUnavailableError } from '../lib/is_alerting_error'; -import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; +import { partiallyUpdateRuleWithEs, RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; import { AlertInstanceContext, AlertInstanceState, @@ -204,7 +204,6 @@ export class TaskRunner< private async updateRuleSavedObjectPostRun( ruleId: string, - namespace: string | undefined, attributes: { executionStatus?: RawRuleExecutionStatus; monitoring?: RawRuleMonitoring; @@ -212,7 +211,7 @@ export class TaskRunner< lastRun?: RawRuleLastRun | null; } ) { - const client = this.internalSavedObjectsRepository; + const client = this.context.elasticsearch.client.asInternalUser; try { // Future engineer -> Here we are just checking if we need to wait for // the update of the attribute `running` in the rule's saved object @@ -223,13 +222,12 @@ export class TaskRunner< // eslint-disable-next-line no-empty } catch {} try { - await partiallyUpdateRule( + await partiallyUpdateRuleWithEs( client, ruleId, { ...attributes, running: false }, { ignore404: true, - namespace, refresh: false, } ); @@ -548,7 +546,7 @@ export class TaskRunner< const { executionStatus: execStatus, executionMetrics: execMetrics } = await this.timer.runWithTimer(TaskRunnerTimerSpan.ProcessRuleRun, async () => { const { - params: { alertId: ruleId, spaceId }, + params: { alertId: ruleId }, startedAt, schedule: taskSchedule, } = this.taskInstance; @@ -560,8 +558,6 @@ export class TaskRunner< nextRun = getNextRun({ startDate: startedAt, interval: taskSchedule.interval }); } - const namespace = this.context.spaceIdToNamespace(spaceId); - const { executionStatus, executionMetrics, lastRun, outcome } = processRunResults({ logger: this.logger, logPrefix: `${this.ruleType.id}:${ruleId}`, @@ -602,7 +598,7 @@ export class TaskRunner< )} - ${JSON.stringify(lastRun)}` ); } - await this.updateRuleSavedObjectPostRun(ruleId, namespace, { + await this.updateRuleSavedObjectPostRun(ruleId, { executionStatus: ruleExecutionStatusToRaw(executionStatus), nextRun, lastRun: lastRunToRaw(lastRun), @@ -758,11 +754,10 @@ export class TaskRunner< // Write event log entry const { - params: { alertId: ruleId, spaceId, consumer }, + params: { alertId: ruleId, consumer }, schedule: taskSchedule, startedAt, } = this.taskInstance; - const namespace = this.context.spaceIdToNamespace(spaceId); if (consumer && !this.ruleConsumer) { this.ruleConsumer = consumer; @@ -803,7 +798,7 @@ export class TaskRunner< `Updating rule task for ${this.ruleType.id} rule with id ${ruleId} - execution error due to timeout` ); const outcome = 'failed'; - await this.updateRuleSavedObjectPostRun(ruleId, namespace, { + await this.updateRuleSavedObjectPostRun(ruleId, { executionStatus: ruleExecutionStatusToRaw(executionStatus), lastRun: { outcome, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts index c116230016e9b..6c7331de463ea 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_alerts_client.test.ts @@ -46,7 +46,7 @@ import { RULE_NAME, generateRunnerResult, RULE_ACTIONS, - generateSavedObjectParams, + generateRuleUpdateParams, mockTaskInstance, DATE_1970, DATE_1970_5_MIN, @@ -376,8 +376,8 @@ describe('Task Runner', () => { { tags: ['1', 'test'] } ); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({}) + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({}) ); expect(taskRunnerFactoryInitializerParams.executionContext.withContext).toBeCalledTimes(1); @@ -510,8 +510,8 @@ describe('Task Runner', () => { 'ruleRunMetrics for test:1: {"numSearches":3,"totalSearchDurationMs":23423,"esSearchDurationMs":33,"numberOfTriggeredActions":0,"numberOfGeneratedActions":0,"numberOfActiveAlerts":0,"numberOfRecoveredAlerts":0,"numberOfNewAlerts":0,"numberOfDelayedAlerts":0,"hasReachedAlertLimit":false,"hasReachedQueuedActionsLimit":false,"triggeredActionsStatus":"complete"}', { tags: ['1', 'test'] } ); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({}) + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({}) ); expect(taskRunnerFactoryInitializerParams.executionContext.withContext).toBeCalledTimes(1); expect( @@ -708,8 +708,8 @@ describe('Task Runner', () => { tags: ['1', 'test'], }); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({}) + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({}) ); expect(taskRunnerFactoryInitializerParams.executionContext.withContext).toBeCalledTimes(1); @@ -799,8 +799,8 @@ describe('Task Runner', () => { tags: ['1', 'test'], }); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - ...generateSavedObjectParams({}) + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( + ...generateRuleUpdateParams({}) ); expect(taskRunnerFactoryInitializerParams.executionContext.withContext).toBeCalledTimes(1); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index 3a6a9547fb902..e5572707ae6fd 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -63,6 +63,7 @@ import { TaskRunnerContext } from './types'; import { backfillClientMock } from '../backfill_client/backfill_client.mock'; import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { rulesSettingsServiceMock } from '../rules_settings/rules_settings_service.mock'; +import { ALERTING_CASES_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { maintenanceWindowsServiceMock } from './maintenance_windows/maintenance_windows_service.mock'; jest.mock('uuid', () => ({ @@ -225,53 +226,57 @@ describe('Task Runner Cancel', () => { testAlertingEventLogCalls({ status: 'ok' }); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledTimes(1); - expect(internalSavedObjectsRepository.update).toHaveBeenCalledWith( - RULE_SAVED_OBJECT_TYPE, - '1', + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledTimes(1); + expect(elasticsearchService.client.asInternalUser.update).toHaveBeenCalledWith( { - executionStatus: { - error: { - message: `test:1: execution cancelled due to timeout - exceeded rule type timeout of 5m`, - reason: 'timeout', - }, - lastDuration: 0, - lastExecutionDate: '1970-01-01T00:00:00.000Z', - status: 'error', - warning: null, - }, - lastRun: { - alertsCount: {}, - outcome: 'failed', - outcomeMsg: [ - 'test:1: execution cancelled due to timeout - exceeded rule type timeout of 5m', - ], - outcomeOrder: 20, - warning: 'timeout', - }, - monitoring: { - run: { - calculated_metrics: { - success_ratio: 0, + id: `alert:1`, + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + doc: { + alert: { + executionStatus: { + error: { + message: `test:1: execution cancelled due to timeout - exceeded rule type timeout of 5m`, + reason: 'timeout', + }, + lastDuration: 0, + lastExecutionDate: '1970-01-01T00:00:00.000Z', + status: 'error', + warning: null, + }, + lastRun: { + alertsCount: {}, + outcome: 'failed', + outcomeMsg: [ + 'test:1: execution cancelled due to timeout - exceeded rule type timeout of 5m', + ], + outcomeOrder: 20, + warning: 'timeout', }, - history: [], - last_run: { - metrics: { - duration: 0, - gap_duration_s: null, - total_alerts_created: null, - total_alerts_detected: null, - total_indexing_duration_ms: null, - total_search_duration_ms: null, + monitoring: { + run: { + calculated_metrics: { + success_ratio: 0, + }, + history: [], + last_run: { + metrics: { + duration: 0, + gap_duration_s: null, + total_alerts_created: null, + total_alerts_detected: null, + total_indexing_duration_ms: null, + total_search_duration_ms: null, + }, + timestamp: '1970-01-01T00:00:00.000Z', + }, }, - timestamp: '1970-01-01T00:00:00.000Z', }, + nextRun: '1970-01-01T00:00:10.000Z', + running: false, }, }, - nextRun: '1970-01-01T00:00:10.000Z', - running: false, }, - { refresh: false, namespace: undefined } + { ignore: [404] } ); expect(mockUsageCounter.incrementCounter).toHaveBeenCalledTimes(1); expect(mockUsageCounter.incrementCounter).toHaveBeenCalledWith({ diff --git a/x-pack/plugins/alerting/tsconfig.json b/x-pack/plugins/alerting/tsconfig.json index c09816222b010..c0951663a8489 100644 --- a/x-pack/plugins/alerting/tsconfig.json +++ b/x-pack/plugins/alerting/tsconfig.json @@ -72,7 +72,8 @@ "@kbn/alerting-state-types", "@kbn/core-security-server", "@kbn/core-http-server", - "@kbn/zod" + "@kbn/zod", + "@kbn/core-saved-objects-base-server-internal" ], "exclude": [ "target/**/*"