From ad93bb00b8461c816ce5f18020e18c18f3da8ab4 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Mon, 16 Feb 2026 15:31:35 -0500 Subject: [PATCH 1/4] Update methods --- .../lib/rules_client/rules_client.test.ts | 115 ++++++++++++++++++ .../server/lib/rules_client/rules_client.ts | 17 +++ .../rules_saved_object_service.ts | 34 ++++++ 3 files changed, 166 insertions(+) diff --git a/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.test.ts b/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.test.ts index 765f58a2ddd4b..81741ef5d0e5b 100644 --- a/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.test.ts +++ b/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.test.ts @@ -94,6 +94,9 @@ describe('RulesClient', () => { page: 1, per_page: 20, }); + mockSavedObjectsClient.bulkGet.mockResolvedValue({ + saved_objects: [], + }); ensureRuleExecutorTaskScheduledMock.mockResolvedValue({ id: 'task-123' }); getRuleExecutorTaskIdMock.mockReturnValue('task:fallback'); @@ -400,6 +403,118 @@ describe('RulesClient', () => { }); }); + describe('getRules', () => { + it('returns rules for the provided ids', async () => { + const client = createClient(); + const so1Attrs = createRuleSoAttributes({ + metadata: { name: 'rule-get-many-1' }, + }); + const so2Attrs = createRuleSoAttributes({ + metadata: { name: 'rule-get-many-2' }, + }); + + mockSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: 'rule-id-get-many-1', + type: RULE_SAVED_OBJECT_TYPE, + attributes: so1Attrs, + references: [], + }, + { + id: 'rule-id-get-many-2', + type: RULE_SAVED_OBJECT_TYPE, + attributes: so2Attrs, + references: [], + }, + ], + }); + + const res = await client.getRules(['rule-id-get-many-1', 'rule-id-get-many-2']); + + expect(mockSavedObjectsClient.bulkGet).toHaveBeenCalledWith([ + { type: RULE_SAVED_OBJECT_TYPE, id: 'rule-id-get-many-1' }, + { type: RULE_SAVED_OBJECT_TYPE, id: 'rule-id-get-many-2' }, + ]); + expect(res).toHaveLength(2); + expect(res[0]).toEqual( + expect.objectContaining({ + id: 'rule-id-get-many-1', + metadata: expect.objectContaining({ name: 'rule-get-many-1' }), + }) + ); + expect(res[1]).toEqual( + expect.objectContaining({ + id: 'rule-id-get-many-2', + metadata: expect.objectContaining({ name: 'rule-get-many-2' }), + }) + ); + }); + + it('excludes missing ids returned as bulk get errors', async () => { + const client = createClient(); + const soAttrs = createRuleSoAttributes({ + metadata: { name: 'rule-get-many-success' }, + }); + + mockSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: 'rule-id-get-many-success', + type: RULE_SAVED_OBJECT_TYPE, + attributes: soAttrs, + references: [], + }, + { + id: 'rule-id-get-many-missing', + type: RULE_SAVED_OBJECT_TYPE, + attributes: {} as RuleSavedObjectAttributes, + references: [], + error: { + statusCode: 404, + error: 'Not Found', + message: 'Saved object [alerting-rule/rule-id-get-many-missing] not found', + }, + }, + ], + }); + + const res = await client.getRules(['rule-id-get-many-success', 'rule-id-get-many-missing']); + + expect(res).toHaveLength(1); + expect(res[0]).toEqual( + expect.objectContaining({ + id: 'rule-id-get-many-success', + metadata: expect.objectContaining({ name: 'rule-get-many-success' }), + }) + ); + }); + + it('throws when bulk get returns a non-404 error', async () => { + const client = createClient(); + + mockSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: 'rule-id-get-many-failure', + type: RULE_SAVED_OBJECT_TYPE, + attributes: {} as RuleSavedObjectAttributes, + references: [], + error: { + statusCode: 500, + error: 'Internal Server Error', + message: 'bulk get failed', + }, + }, + ], + }); + + await expect(client.getRules(['rule-id-get-many-failure'])).rejects.toMatchObject({ + output: { statusCode: 500 }, + }); + }); + }); + describe('deleteRule', () => { it('removes the scheduled task and deletes the rule', async () => { const client = createClient(); diff --git a/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.ts b/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.ts index f46609de84ae2..ee8740e5f47f5 100644 --- a/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.ts +++ b/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.ts @@ -187,6 +187,23 @@ export class RulesClient { } } + public async getRules(ids: string[]): Promise { + const result = await this.rulesSavedObjectService.bulkGetByIds(ids); + + return result.flatMap((doc) => { + if ('error' in doc) { + if (doc.error.statusCode === 404) { + return []; + } + throw Boom.boomify(new Error(doc.error.message), { + statusCode: doc.error.statusCode, + }); + } + + return [transformRuleSoAttributesToRuleApiResponse(doc.id, doc.attributes)]; + }); + } + public async deleteRule({ id }: { id: string }): Promise { const { spaceId } = this.getSpaceContext(); diff --git a/x-pack/platform/plugins/shared/alerting_v2/server/lib/services/rules_saved_object_service/rules_saved_object_service.ts b/x-pack/platform/plugins/shared/alerting_v2/server/lib/services/rules_saved_object_service/rules_saved_object_service.ts index ae56470d3f0b3..8b24ff938701b 100644 --- a/x-pack/platform/plugins/shared/alerting_v2/server/lib/services/rules_saved_object_service/rules_saved_object_service.ts +++ b/x-pack/platform/plugins/shared/alerting_v2/server/lib/services/rules_saved_object_service/rules_saved_object_service.ts @@ -16,6 +16,18 @@ import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import type { RuleSavedObjectAttributes } from '../../../saved_objects'; import type { AlertingServerStartDependencies } from '../../../types'; import { spaceIdToNamespace } from '../../space_id_to_namespace'; +import type { SavedObjectError } from '@kbn/core/types'; + +export type RulesSavedObjectsBulkGetResultItem = + | { + id: string; + attributes: RuleSavedObjectAttributes; + version?: string; + } + | { + id: string; + error: SavedObjectError; + }; export interface RulesSavedObjectServiceContract { create(params: { attrs: RuleSavedObjectAttributes; id?: string }): Promise; @@ -23,6 +35,7 @@ export interface RulesSavedObjectServiceContract { id: string, spaceId?: string ): Promise<{ id: string; attributes: RuleSavedObjectAttributes; version?: string }>; + bulkGetByIds(ids: string[], spaceId?: string): Promise; update(params: { id: string; attrs: RuleSavedObjectAttributes; version?: string }): Promise; delete(params: { id: string }): Promise; find(params: { page: number; perPage: number }): Promise<{ @@ -72,6 +85,27 @@ export class RulesSavedObjectService implements RulesSavedObjectServiceContract return { id: doc.id, attributes: doc.attributes, version: doc.version }; } + public async bulkGetByIds( + ids: string[], + spaceId?: string + ): Promise { + const namespace = spaceIdToNamespace(this.spaces, spaceId); + if (ids.length === 0) { + return []; + } + + const result = await this.client.bulkGet( + ids.map((id) => ({ type: RULE_SAVED_OBJECT_TYPE, id }), namespace ? { namespace } : undefined) + ); + + return result.saved_objects.map((doc) => { + if ('error' in doc && doc.error) { + return { id: doc.id, error: doc.error }; + } + return { id: doc.id, attributes: doc.attributes, version: doc.version }; + }); + } + public async update({ id, attrs, From 2bacba4d1780b49448f75c78c1e697a16f430613 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 16 Feb 2026 20:56:20 +0000 Subject: [PATCH 2/4] Changes from node scripts/eslint_all_files --no-cache --fix --- .../rules_saved_object_service/rules_saved_object_service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/alerting_v2/server/lib/services/rules_saved_object_service/rules_saved_object_service.ts b/x-pack/platform/plugins/shared/alerting_v2/server/lib/services/rules_saved_object_service/rules_saved_object_service.ts index 8b24ff938701b..b15b09dc54b83 100644 --- a/x-pack/platform/plugins/shared/alerting_v2/server/lib/services/rules_saved_object_service/rules_saved_object_service.ts +++ b/x-pack/platform/plugins/shared/alerting_v2/server/lib/services/rules_saved_object_service/rules_saved_object_service.ts @@ -12,11 +12,11 @@ import { SavedObjectsClientFactory } from '@kbn/core-di-server'; import { inject, injectable } from 'inversify'; import type { SavedObjectsClientContract } from '@kbn/core/server'; import { SavedObjectsUtils } from '@kbn/core/server'; +import type { SavedObjectError } from '@kbn/core/types'; import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import type { RuleSavedObjectAttributes } from '../../../saved_objects'; import type { AlertingServerStartDependencies } from '../../../types'; import { spaceIdToNamespace } from '../../space_id_to_namespace'; -import type { SavedObjectError } from '@kbn/core/types'; export type RulesSavedObjectsBulkGetResultItem = | { From b92f34b7dce6e7491e3a5208741724d605e87653 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Mon, 16 Feb 2026 16:13:03 -0500 Subject: [PATCH 3/4] add limits --- packages/kbn-optimizer/limits.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ed00f0f59a6cc..6c038e32b3a45 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -6,6 +6,7 @@ pageLoadAssetSize: aiAssistantManagementSelection: 13590 aiops: 15227 alerting: 22371 + alertingVTwo: 361715 apm: 38573 apmSourcesAccess: 2278 automaticImport: 12162 From af035c27710b0a1e0a52769e68a3d8986c213886 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Wed, 18 Feb 2026 09:40:35 -0500 Subject: [PATCH 4/4] Swallow errors in getRules method --- .../lib/rules_client/rules_client.test.ts | 23 +++++++++++++++---- .../server/lib/rules_client/rules_client.ts | 7 +----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.test.ts b/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.test.ts index 81741ef5d0e5b..da3196f25d75b 100644 --- a/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.test.ts +++ b/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.test.ts @@ -490,11 +490,20 @@ describe('RulesClient', () => { ); }); - it('throws when bulk get returns a non-404 error', async () => { + it('ignores documents with non-404 errors and returns valid documents', async () => { const client = createClient(); + const validAttrs = createRuleSoAttributes({ + metadata: { name: 'rule-get-many-valid' }, + }); mockSavedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ + { + id: 'rule-id-get-many-valid', + type: RULE_SAVED_OBJECT_TYPE, + attributes: validAttrs, + references: [], + }, { id: 'rule-id-get-many-failure', type: RULE_SAVED_OBJECT_TYPE, @@ -509,9 +518,15 @@ describe('RulesClient', () => { ], }); - await expect(client.getRules(['rule-id-get-many-failure'])).rejects.toMatchObject({ - output: { statusCode: 500 }, - }); + const res = await client.getRules(['rule-id-get-many-valid', 'rule-id-get-many-failure']); + + expect(res).toHaveLength(1); + expect(res[0]).toEqual( + expect.objectContaining({ + id: 'rule-id-get-many-valid', + metadata: expect.objectContaining({ name: 'rule-get-many-valid' }), + }) + ); }); }); diff --git a/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.ts b/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.ts index ee8740e5f47f5..c75392c558cb2 100644 --- a/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.ts +++ b/x-pack/platform/plugins/shared/alerting_v2/server/lib/rules_client/rules_client.ts @@ -192,12 +192,7 @@ export class RulesClient { return result.flatMap((doc) => { if ('error' in doc) { - if (doc.error.statusCode === 404) { - return []; - } - throw Boom.boomify(new Error(doc.error.message), { - statusCode: doc.error.statusCode, - }); + return []; } return [transformRuleSoAttributesToRuleApiResponse(doc.id, doc.attributes)];