Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RAM] HTTP Versioning Rule Mute Alert #165573

Merged
merged 20 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from 17 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
Original file line number Diff line number Diff line change
@@ -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 { muteAlertParamsSchema } from './schemas/latest';
export { muteAlertParamsSchema as muteAlertParamsSchemaV1 } from './schemas/v1';

export type { MuteAlertRequestParams } from './types/latest';
export type { MuteAlertRequestParams as MuteAlertRequestParamsV1 } from './types/v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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 { muteAlertParamsSchema } from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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 muteAlertParamsSchema = schema.object({
rule_id: schema.string(),
alert_id: schema.string(),
});
Original file line number Diff line number Diff line change
@@ -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 type { MuteAlertRequestParams } from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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 { muteAlertParamsSchemaV1 } from '..';

export type MuteAlertRequestParams = TypeOf<typeof muteAlertParamsSchemaV1>;
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,37 @@
* 2.0.
*/

import { Rule } from '../../types';
import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization';
import { retryIfConflicts } from '../../lib/retry_if_conflicts';
import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events';
import { MuteOptions } from '../types';
import { RulesClientContext } from '../types';
import { updateMeta } from '../lib';
import Boom from '@hapi/boom';
import { muteAlertSo } from '../../../../data/rule/methods/mute_alert_so';
import { muteAlertParamsSchema } from './schemas';
import type { MuteAlertParams } from './types';
import { Rule } from '../../../../types';
import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization';
import { retryIfConflicts } from '../../../../lib/retry_if_conflicts';
import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events';
import { RulesClientContext } from '../../../../rules_client/types';
import { updateMeta } from '../../../../rules_client/lib';

export async function muteInstance(
context: RulesClientContext,
{ alertId, alertInstanceId }: MuteOptions
params: MuteAlertParams
): Promise<void> {
try {
muteAlertParamsSchema.validate(params);
} catch (error) {
throw Boom.badRequest(`Failed to validate params: ${error.message}`);
}

return await retryIfConflicts(
context.logger,
`rulesClient.muteInstance('${alertId}')`,
async () => await muteInstanceWithOCC(context, { alertId, alertInstanceId })
`rulesClient.muteInstance('${params.alertId}')`,
async () => await muteInstanceWithOCC(context, params)
);
}

async function muteInstanceWithOCC(
context: RulesClientContext,
{ alertId, alertInstanceId }: MuteOptions
{ alertId, alertInstanceId }: MuteAlertParams
) {
const { attributes, version } = await context.unsecuredSavedObjectsClient.get<Rule>(
'alert',
Expand Down Expand Up @@ -68,15 +77,15 @@ async function muteInstanceWithOCC(
const mutedInstanceIds = attributes.mutedInstanceIds || [];
if (!attributes.muteAll && !mutedInstanceIds.includes(alertInstanceId)) {
mutedInstanceIds.push(alertInstanceId);
await context.unsecuredSavedObjectsClient.update(
'alert',
await muteAlertSo({
jcger marked this conversation as resolved.
Show resolved Hide resolved
savedObjectsClient: context.unsecuredSavedObjectsClient,
savedObjectsUpdateOptions: { version },
alertId,
updateMeta(context, {
alertAttributes: updateMeta(context, {
mutedInstanceIds,
updatedBy: await context.getUserName(),
updatedAt: new Date().toISOString(),
}),
{ version }
);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* 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 { muteAlertParamsSchema } from './mute_alert_params_schema';
Original file line number Diff line number Diff line change
@@ -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 muteAlertParamsSchema = schema.object({
alertId: schema.string(),
alertInstanceId: schema.string(),
});
Original file line number Diff line number Diff line change
@@ -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 type { MuteAlertParams } from './mute_alert_params';
Original file line number Diff line number Diff line change
@@ -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 { TypeOf } from '@kbn/config-schema';
import { muteAlertParamsSchema } from '../schemas';

export type MuteAlertParams = TypeOf<typeof muteAlertParamsSchema>;
29 changes: 29 additions & 0 deletions x-pack/plugins/alerting/server/data/rule/methods/mute_alert_so.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 {
SavedObjectsClientContract,
SavedObjectsUpdateOptions,
SavedObjectsUpdateResponse,
} from '@kbn/core/server';
import { RuleAttributes } from '../types';

export interface MuteAlertSoParams {
savedObjectsClient: SavedObjectsClientContract;
savedObjectsUpdateOptions: SavedObjectsUpdateOptions;
alertId: string;
alertAttributes: Partial<RuleAttributes>;
}

export const muteAlertSo = ({
savedObjectsClient,
savedObjectsUpdateOptions: { version },
alertId,
alertAttributes,
}: MuteAlertSoParams): Promise<SavedObjectsUpdateResponse<RuleAttributes>> => {
return savedObjectsClient.update('alert', alertId, alertAttributes, { version });
};
2 changes: 1 addition & 1 deletion x-pack/plugins/alerting/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { healthRoute } from './health';
import { resolveRuleRoute } from './resolve_rule';
import { ruleTypesRoute } from './rule_types';
import { muteAllRuleRoute } from './mute_all_rule';
import { muteAlertRoute } from './mute_alert';
import { muteAlertRoute } from './rule/apis/mute_alert/mute_alert';
import { unmuteAllRuleRoute } from './unmute_all_rule';
import { unmuteAlertRoute } from './unmute_alert';
import { updateRuleApiKeyRoute } from './update_rule_api_key';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

import { muteAlertRoute } from './mute_alert';
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 { licenseStateMock } from '../../../../lib/license_state.mock';
import { mockHandlerArguments } from '../../../_mock_handler_arguments';
import { rulesClientMock } from '../../../../rules_client.mock';
import { RuleTypeDisabledError } from '../../../../lib';

const rulesClient = rulesClientMock.create();
jest.mock('../lib/license_api_access', () => ({
jest.mock('../../../../lib/license_api_access', () => ({
verifyApiAccess: jest.fn(),
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,15 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { IRouter } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import { ILicenseState, RuleTypeDisabledError } from '../lib';
import { MuteOptions } from '../rules_client';
import { RewriteRequestCase, verifyAccessAndContext } from './lib';
import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types';

const paramSchema = schema.object({
rule_id: schema.string(),
alert_id: schema.string(),
});

const rewriteParamsReq: RewriteRequestCase<MuteOptions> = ({
rule_id: alertId,
alert_id: alertInstanceId,
}) => ({
alertId,
alertInstanceId,
});
import { transformRequestParamsToApplicationV1 } from './transforms';
import { ILicenseState, RuleTypeDisabledError } from '../../../../lib';
import { verifyAccessAndContext } from '../../../lib';
import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types';
import {
muteAlertParamsSchemaV1,
MuteAlertRequestParamsV1,
} from '../../../../../common/routes/rule/apis/mute_alert';

export const muteAlertRoute = (
router: IRouter<AlertingRequestHandlerContext>,
Expand All @@ -33,15 +22,15 @@ export const muteAlertRoute = (
{
path: `${BASE_ALERTING_API_PATH}/rule/{rule_id}/alert/{alert_id}/_mute`,
validate: {
params: paramSchema,
params: muteAlertParamsSchemaV1,
},
},
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const rulesClient = (await context.alerting).getRulesClient();
const params = rewriteParamsReq(req.params);
const params: MuteAlertRequestParamsV1 = req.params;
try {
await rulesClient.muteInstance(params);
await rulesClient.muteInstance(transformRequestParamsToApplicationV1(params));
return res.noContent();
} catch (e) {
if (e instanceof RuleTypeDisabledError) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { transformRequestParamsToApplication } from './transform_request_params_to_application/latest';
export { transformRequestParamsToApplication as transformRequestParamsToApplicationV1 } from './transform_request_params_to_application/v1';
Original file line number Diff line number Diff line change
@@ -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 { transformRequestParamsToApplication } from './v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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 { MuteAlertParams } from '../../../../../../application/rule/methods/mute_alert/types';
import { RewriteRequestCase } from '../../../../../lib';

export const transformRequestParamsToApplication: RewriteRequestCase<MuteAlertParams> = ({
rule_id: alertId,
alert_id: alertInstanceId,
}) => ({
alertId,
alertInstanceId,
});
9 changes: 5 additions & 4 deletions x-pack/plugins/alerting/server/rules_client/rules_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
* 2.0.
*/

import { MuteAlertParams } from '../application/rule/methods/mute_alert/types';
import { SanitizedRule, RuleTypeParams } from '../types';
import { parseDuration } from '../../common/parse_duration';
import { RulesClientContext, BulkOptions, MuteOptions } from './types';
import { RulesClientContext, BulkOptions } from './types';

import { clone, CloneArguments } from './methods/clone';
import { createRule, CreateRuleParams } from '../application/rule/methods/create';
Expand Down Expand Up @@ -49,9 +50,9 @@ import { disable } from './methods/disable';
import { snooze, SnoozeParams } from './methods/snooze';
import { unsnooze, UnsnoozeParams } from './methods/unsnooze';
import { clearExpiredSnoozes } from './methods/clear_expired_snoozes';
import { muteInstance } from '../application/rule/methods/mute_alert/mute_instance';
import { muteAll } from './methods/mute_all';
import { unmuteAll } from './methods/unmute_all';
import { muteInstance } from './methods/mute_instance';
import { unmuteInstance } from './methods/unmute_instance';
import { runSoon } from './methods/run_soon';
import { listRuleTypes } from './methods/list_rule_types';
Expand Down Expand Up @@ -159,8 +160,8 @@ export class RulesClient {

public muteAll = (options: { id: string }) => muteAll(this.context, options);
public unmuteAll = (options: { id: string }) => unmuteAll(this.context, options);
public muteInstance = (options: MuteOptions) => muteInstance(this.context, options);
public unmuteInstance = (options: MuteOptions) => unmuteInstance(this.context, options);
public muteInstance = (options: MuteAlertParams) => muteInstance(this.context, options);
public unmuteInstance = (options: MuteAlertParams) => unmuteInstance(this.context, options);

public runSoon = (options: { id: string }) => runSoon(this.context, options);

Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/alerting/server/rules_client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export interface IndexType {
[key: string]: unknown;
}

// TODO: remove once all mute endpoints have been migrated to RuleMuteAlertOptions
export interface MuteOptions extends IndexType {
alertId: string;
alertInstanceId: string;
Expand Down