From f787b852b23139fbc8e9926263d827ded4a1f451 Mon Sep 17 00:00:00 2001 From: Julian Gernun <17549662+jcger@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:18:42 +0200 Subject: [PATCH] [Response Ops][Rules] Version Mute All Rule API (#195572) ## Summary `POST /api/alerting/rule/{id}/_mute_all` in https://github.com/elastic/kibana/issues/195181 --- .../common/routes/rule/apis/mute_all/index.ts | 12 +++ .../rule/apis/mute_all/schemas/latest.ts | 8 ++ .../routes/rule/apis/mute_all/schemas/v1.ts | 16 ++++ .../routes/rule/apis/mute_all/types/latest.ts | 8 ++ .../routes/rule/apis/mute_all/types/v1.ts | 11 +++ .../rule/methods/mute_all/index.ts | 9 +++ .../rule/methods/mute_all/mute_all.test.ts | 75 +++++++++++++++++++ .../rule/methods/mute_all}/mute_all.ts | 35 ++++++--- .../rule/methods/mute_all/schemas/index.ts | 8 ++ .../mute_all/schemas/mute_all_rule_schemas.ts | 12 +++ .../rule/methods/mute_all/types/index.ts | 8 ++ .../mute_all/types/mute_all_rule_types.ts | 11 +++ .../plugins/alerting/server/routes/index.ts | 2 +- .../apis/mute_all}/mute_all_rule.test.ts | 14 ++-- .../{ => rule/apis/mute_all}/mute_all_rule.ts | 27 +++---- .../server/rules_client/rules_client.ts | 2 +- 16 files changed, 222 insertions(+), 36 deletions(-) create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts rename x-pack/plugins/alerting/server/{rules_client/methods => application/rule/methods/mute_all}/mute_all.ts (65%) create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts rename x-pack/plugins/alerting/server/routes/{ => rule/apis/mute_all}/mute_all_rule.test.ts (85%) rename x-pack/plugins/alerting/server/routes/{ => rule/apis/mute_all}/mute_all_rule.ts (72%) diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts new file mode 100644 index 0000000000000..ba1dd568aeeb2 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts @@ -0,0 +1,12 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { muteAllRuleRequestParamsSchema } from './schemas/latest'; +export type { MuteAllRuleRequestParams } from './types/latest'; + +export { muteAllRuleRequestParamsSchema as muteAllRuleRequestParamsSchemaV1 } from './schemas/v1'; +export type { MuteAllRuleRequestParams as MuteAllRuleRequestParamsV1 } from './types/v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts new file mode 100644 index 0000000000000..9305dac3d46eb --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts @@ -0,0 +1,16 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; + +export const muteAllRuleRequestParamsSchema = schema.object({ + id: schema.string({ + meta: { + description: 'The identifier for the rule.', + }, + }), +}); diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts new file mode 100644 index 0000000000000..c18aa22dadd13 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts @@ -0,0 +1,11 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { TypeOf } from '@kbn/config-schema'; +import { muteAllRuleRequestParamsSchemaV1 } from '..'; + +export type MuteAllRuleRequestParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts new file mode 100644 index 0000000000000..c8b85c149314e --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts @@ -0,0 +1,9 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { MuteAllRuleParams } from './types'; +export { muteAll } from './mute_all'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts new file mode 100644 index 0000000000000..eba9fc4cbf7d4 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts @@ -0,0 +1,75 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RulesClientContext } from '../../../../rules_client'; +import { muteAll } from './mute_all'; +import { savedObjectsRepositoryMock } from '@kbn/core-saved-objects-api-server-mocks'; + +jest.mock('../../../../lib/retry_if_conflicts', () => ({ + retryIfConflicts: (_: unknown, id: unknown, asyncFn: () => Promise) => { + return asyncFn(); + }, +})); + +jest.mock('../../../../rules_client/lib', () => ({ + updateMetaAttributes: () => {}, +})); + +jest.mock('../../../../saved_objects', () => ({ + partiallyUpdateRule: async () => {}, +})); + +const loggerErrorMock = jest.fn(); +const getBulkMock = jest.fn(); + +const savedObjectsMock = savedObjectsRepositoryMock.create(); +savedObjectsMock.get = jest.fn().mockReturnValue({ + attributes: { + actions: [], + }, + version: '9.0.0', +}); + +const context = { + logger: { error: loggerErrorMock }, + getActionsClient: () => { + return { + getBulk: getBulkMock, + }; + }, + unsecuredSavedObjectsClient: savedObjectsMock, + authorization: { ensureAuthorized: async () => {} }, + ruleTypeRegistry: { + ensureRuleTypeEnabled: () => {}, + }, + getUserName: async () => {}, +} as unknown as RulesClientContext; + +describe('validateMuteAllParams', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should not throw an error for valid params', () => { + const validParams = { + id: 'ble', + }; + + expect(() => muteAll(context, validParams)).not.toThrow(); + expect(savedObjectsMock.get).toHaveBeenCalled(); + }); + + it('should throw Boom.badRequest for invalid params', async () => { + const invalidParams = { + id: 22 as unknown as string, // type workaround to send wrong data validation + }; + + await expect(muteAll(context, invalidParams)).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error validating mute all parameters - [id]: expected value of type [string] but got [number]"` + ); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts similarity index 65% rename from x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts rename to x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts index 4e647ee6e58ac..73cfe6e26fdce 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts @@ -5,17 +5,23 @@ * 2.0. */ -import { RawRule } from '../../types'; -import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; -import { retryIfConflicts } from '../../lib/retry_if_conflicts'; -import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; -import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; -import { RulesClientContext } from '../types'; -import { updateMetaAttributes } from '../lib'; -import { clearUnscheduledSnoozeAttributes } from '../common'; -import { RuleAttributes } from '../../data/rule/types'; +import Boom from '@hapi/boom'; +import { RawRule } from '../../../../types'; +import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization'; +import { retryIfConflicts } from '../../../../lib/retry_if_conflicts'; +import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; +import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; +import { RulesClientContext } from '../../../../rules_client/types'; +import { updateMetaAttributes } from '../../../../rules_client/lib'; +import { clearUnscheduledSnoozeAttributes } from '../../../../rules_client/common'; +import { RuleAttributes } from '../../../../data/rule/types'; +import { MuteAllRuleParams } from './types'; +import { muteAllRuleParamsSchema } from './schemas'; -export async function muteAll(context: RulesClientContext, { id }: { id: string }): Promise { +export async function muteAll( + context: RulesClientContext, + { id }: MuteAllRuleParams +): Promise { return await retryIfConflicts( context.logger, `rulesClient.muteAll('${id}')`, @@ -23,7 +29,14 @@ export async function muteAll(context: RulesClientContext, { id }: { id: string ); } -async function muteAllWithOCC(context: RulesClientContext, { id }: { id: string }) { +async function muteAllWithOCC(context: RulesClientContext, params: MuteAllRuleParams) { + try { + muteAllRuleParamsSchema.validate(params); + } catch (error) { + throw Boom.badRequest(`Error validating mute all parameters - ${error.message}`); + } + + const { id } = params; const { attributes, version } = await context.unsecuredSavedObjectsClient.get( RULE_SAVED_OBJECT_TYPE, id diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts new file mode 100644 index 0000000000000..b6c6729ac5029 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './mute_all_rule_schemas'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts new file mode 100644 index 0000000000000..0d0ae33394e72 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts @@ -0,0 +1,12 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; + +export const muteAllRuleParamsSchema = schema.object({ + id: schema.string(), +}); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts new file mode 100644 index 0000000000000..c2d2f7401b350 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts @@ -0,0 +1,8 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './mute_all_rule_types'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts new file mode 100644 index 0000000000000..4f4ad36dbc23a --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts @@ -0,0 +1,11 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { TypeOf } from '@kbn/config-schema'; +import { muteAllRuleParamsSchema } from '../schemas'; + +export type MuteAllRuleParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index 93b1800208c7d..352eb293fff77 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -31,7 +31,7 @@ import { getRuleStateRoute } from './get_rule_state'; import { healthRoute } from './health'; import { resolveRuleRoute } from './rule/apis/resolve'; import { ruleTypesRoute } from './rule_types'; -import { muteAllRuleRoute } from './mute_all_rule'; +import { muteAllRuleRoute } from './rule/apis/mute_all/mute_all_rule'; import { muteAlertRoute } from './rule/apis/mute_alert/mute_alert'; import { unmuteAllRuleRoute } from './unmute_all_rule'; import { unmuteAlertRoute } from './rule/apis/unmute_alert/unmute_alert_route'; diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts similarity index 85% rename from x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts index 131c20eb84a72..98cabee56904b 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts @@ -7,18 +7,18 @@ import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; import { muteAllRuleRoute } from './mute_all_rule'; import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { rulesClientMock } from '../rules_client.mock'; -import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled'; -import { trackDeprecatedRouteUsage } from '../lib/track_deprecated_route_usage'; +import { licenseStateMock } from '../../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../../_mock_handler_arguments'; +import { rulesClientMock } from '../../../../rules_client.mock'; +import { RuleTypeDisabledError } from '../../../../lib/errors/rule_type_disabled'; +import { trackDeprecatedRouteUsage } from '../../../../lib/track_deprecated_route_usage'; const rulesClient = rulesClientMock.create(); -jest.mock('../lib/license_api_access', () => ({ +jest.mock('../../../../lib/license_api_access', () => ({ verifyApiAccess: jest.fn(), })); -jest.mock('../lib/track_deprecated_route_usage', () => ({ +jest.mock('../../../../lib/track_deprecated_route_usage', () => ({ trackDeprecatedRouteUsage: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts similarity index 72% rename from x-pack/plugins/alerting/server/routes/mute_all_rule.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts index ab220f7444590..8ac77973575bb 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts @@ -6,20 +6,15 @@ */ import { IRouter } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { ILicenseState, RuleTypeDisabledError } from '../lib'; -import { verifyAccessAndContext } from './lib'; -import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; -import { trackDeprecatedRouteUsage } from '../lib/track_deprecated_route_usage'; - -const paramSchema = schema.object({ - id: schema.string({ - meta: { - description: 'The identifier for the rule.', - }, - }), -}); +import { ILicenseState, RuleTypeDisabledError } from '../../../../lib'; +import { verifyAccessAndContext } from '../../../lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types'; +import { trackDeprecatedRouteUsage } from '../../../../lib/track_deprecated_route_usage'; +import { + muteAllRuleRequestParamsSchemaV1, + MuteAllRuleRequestParamsV1, +} from '../../../../../common/routes/rule/apis/mute_all'; export const muteAllRuleRoute = ( router: IRouter, @@ -36,7 +31,7 @@ export const muteAllRuleRoute = ( }, validate: { request: { - params: paramSchema, + params: muteAllRuleRequestParamsSchemaV1, }, response: { 204: { @@ -48,10 +43,10 @@ export const muteAllRuleRoute = ( router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); - const { id } = req.params; + const params: MuteAllRuleRequestParamsV1 = req.params; trackDeprecatedRouteUsage('muteAll', usageCounter); try { - await rulesClient.muteAll({ id }); + await rulesClient.muteAll(params); return res.noContent(); } catch (e) { if (e instanceof RuleTypeDisabledError) { diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 80f9b82733a9d..163df75cc0e6b 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -58,7 +58,7 @@ import { enableRule } from '../application/rule/methods/enable_rule/enable_rule' import { updateRuleApiKey } from '../application/rule/methods/update_api_key/update_rule_api_key'; import { disableRule } from '../application/rule/methods/disable/disable_rule'; import { muteInstance } from '../application/rule/methods/mute_alert/mute_instance'; -import { muteAll } from './methods/mute_all'; +import { muteAll } from '../application/rule/methods/mute_all'; import { unmuteAll } from './methods/unmute_all'; import { unmuteInstance } from '../application/rule/methods/unmute_alert/unmute_instance'; import { runSoon } from './methods/run_soon';