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

[ResponseOps][Rules] Allow users to delete snooze schedule from a rule #213247

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
51 changes: 51 additions & 0 deletions oas_docs/bundle.json
Original file line number Diff line number Diff line change
Expand Up @@ -4365,6 +4365,57 @@
]
}
},
"/api/alerting/rule/{ruleId}/snooze_schedule/{scheduleId}": {
"delete": {
"operationId": "delete-alerting-rule-ruleid-snooze-schedule-scheduleid",
"parameters": [
{
"description": "A required header to protect against CSRF attacks",
"in": "header",
"name": "kbn-xsrf",
"required": true,
"schema": {
"example": "true",
"type": "string"
}
},
{
"in": "path",
"name": "ruleId",
"required": true,
"schema": {
"type": "string"
}
},
{
"in": "path",
"name": "scheduleId",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"204": {
"description": "Indicates a successful call."
},
"400": {
"description": "Indicates an invalid schema."
},
"403": {
"description": "Indicates that this call is forbidden."
},
"404": {
"description": "Indicates a rule with the given id does not exist."
}
},
"summary": "Delete a snooze schedule for a rule",
"tags": [
"alerting"
]
}
},
"/api/alerting/rule/{rule_id}/alert/{alert_id}/_mute": {
"post": {
"operationId": "post-alerting-rule-rule-id-alert-alert-id-mute",
Expand Down
51 changes: 51 additions & 0 deletions oas_docs/bundle.serverless.json
Original file line number Diff line number Diff line change
Expand Up @@ -4365,6 +4365,57 @@
]
}
},
"/api/alerting/rule/{ruleId}/snooze_schedule/{scheduleId}": {
"delete": {
"operationId": "delete-alerting-rule-ruleid-snooze-schedule-scheduleid",
"parameters": [
{
"description": "A required header to protect against CSRF attacks",
"in": "header",
"name": "kbn-xsrf",
"required": true,
"schema": {
"example": "true",
"type": "string"
}
},
{
"in": "path",
"name": "ruleId",
"required": true,
"schema": {
"type": "string"
}
},
{
"in": "path",
"name": "scheduleId",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"204": {
"description": "Indicates a successful call."
},
"400": {
"description": "Indicates an invalid schema."
},
"403": {
"description": "Indicates that this call is forbidden."
},
"404": {
"description": "Indicates a rule with the given id does not exist."
}
},
"summary": "Delete a snooze schedule for a rule",
"tags": [
"alerting"
]
}
},
"/api/alerting/rule/{rule_id}/alert/{alert_id}/_mute": {
"post": {
"operationId": "post-alerting-rule-rule-id-alert-alert-id-mute",
Expand Down
34 changes: 34 additions & 0 deletions oas_docs/output/kibana.serverless.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3475,6 +3475,40 @@ paths:
tags:
- alerting
x-beta: true
/api/alerting/rule/{ruleId}/snooze_schedule/{scheduleId}:
delete:
operationId: delete-alerting-rule-ruleid-snooze-schedule-scheduleid
parameters:
- description: A required header to protect against CSRF attacks
in: header
name: kbn-xsrf
required: true
schema:
example: 'true'
type: string
- in: path
name: ruleId
required: true
schema:
type: string
- in: path
name: scheduleId
required: true
schema:
type: string
responses:
'204':
description: Indicates a successful call.
'400':
description: Indicates an invalid schema.
'403':
description: Indicates that this call is forbidden.
'404':
description: Indicates a rule with the given id does not exist.
summary: Delete a snooze schedule for a rule
tags:
- alerting
x-beta: true
/api/alerting/rules/_find:
get:
operationId: get-alerting-rules-find
Expand Down
33 changes: 33 additions & 0 deletions oas_docs/output/kibana.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3818,6 +3818,39 @@ paths:
summary: Unmute an alert
tags:
- alerting
/api/alerting/rule/{ruleId}/snooze_schedule/{scheduleId}:
delete:
operationId: delete-alerting-rule-ruleid-snooze-schedule-scheduleid
parameters:
- description: A required header to protect against CSRF attacks
in: header
name: kbn-xsrf
required: true
schema:
example: 'true'
type: string
- in: path
name: ruleId
required: true
schema:
type: string
- in: path
name: scheduleId
required: true
schema:
type: string
responses:
'204':
description: Indicates a successful call.
'400':
description: Indicates an invalid schema.
'403':
description: Indicates that this call is forbidden.
'404':
description: Indicates a rule with the given id does not exist.
summary: Delete a snooze schedule for a rule
tags:
- alerting
/api/alerting/rules/_find:
get:
operationId: get-alerting-rules-find
Expand Down
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 unsnoozeParamsSchema = schema.object({
ruleId: schema.string(),
scheduleId: schema.string(),
});
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 { TypeOf } from '@kbn/config-schema';
import { unsnoozeParamsSchemaV1 } from '../..';

export type UnsnoozeParams = TypeOf<typeof unsnoozeParamsSchemaV1>;
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@
* 2.0.
*/

export { unsnoozeParamsSchema, unsnoozeBodySchema } from './schemas/latest';
export {
unsnoozeParamsInternalSchema,
unsnoozeBodyInternalSchema,
} from './internal/schemas/latest';
export { unsnoozeParamsSchema } from './external/schemas/latest';
export type { UnsnoozeParams } from './external/types/latest';

export {
unsnoozeParamsSchema as unsnoozeParamsSchemaV1,
unsnoozeBodySchema as unsnoozeBodySchemaV1,
} from './schemas/v1';
unsnoozeParamsInternalSchema as unsnoozeParamsInternalSchemaV1,
unsnoozeBodyInternalSchema as unsnoozeBodyInternalSchemaV1,
} from './internal/schemas/v1';
export { unsnoozeParamsSchema as unsnoozeParamsSchemaV1 } from './external/schemas/v1';
export type { UnsnoozeParams as UnsnoozeParamsV1 } from './external/types/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 * from './v1';
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

import { schema } from '@kbn/config-schema';

export const unsnoozeParamsSchema = schema.object({
export const unsnoozeParamsInternalSchema = schema.object({
id: schema.string(),
});

const scheduleIdsSchema = schema.maybe(schema.arrayOf(schema.string()));

export const unsnoozeBodySchema = schema.object({
export const unsnoozeBodyInternalSchema = schema.object({
schedule_ids: scheduleIdsSchema,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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 { unsnoozeRule } from './unsnooze_rule';
import { savedObjectsRepositoryMock } from '@kbn/core-saved-objects-api-server-mocks';

const loggerErrorMock = jest.fn();
const getBulkMock = jest.fn();

const savedObjectsMock = savedObjectsRepositoryMock.create();
savedObjectsMock.get = jest.fn().mockReturnValue({
attributes: {
actions: [],
snoozeSchedule: [
{
duration: 600000,
rRule: {
interval: 1,
freq: 3,
dtstart: '2025-03-01T06:30:37.011Z',
tzid: 'UTC',
},
id: 'snooze_schedule_1',
},
],
},
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('validate unsnooze params', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should validate params correctly', async () => {
await expect(
unsnoozeRule(context, { id: '123', scheduleIds: ['snooze_schedule_1'] })
).resolves.toBeUndefined();
});

it('should validate params with empty schedule ids correctly', async () => {
await expect(unsnoozeRule(context, { id: '123', scheduleIds: [] })).resolves.toBeUndefined();
});

it('should throw bad request for invalid params', async () => {
// @ts-expect-error: testing invalid params
await expect(unsnoozeRule(context, {})).rejects.toThrowErrorMatchingInlineSnapshot(
`"Error validating unsnooze params - [id]: expected value of type [string] but got [undefined]"`
);
});

it('should throw bad request for when snooze schedule is empty for public route', async () => {
savedObjectsMock.get = jest.fn().mockReturnValue({
attributes: {
actions: [],
snoozeSchedule: [],
},
version: '9.0.0',
});
await expect(
unsnoozeRule(context, { id: '123', scheduleIds: ['snooze_schedule_1'], isPublic: true })
).rejects.toThrowErrorMatchingInlineSnapshot(`"Rule has no snooze schedules."`);
});

it('should throw bad request for invalid snooze schedule id for public route', async () => {
savedObjectsMock.get = jest.fn().mockReturnValue({
attributes: {
actions: [],
snoozeSchedule: [
{
duration: 600000,
rRule: {
interval: 1,
freq: 3,
dtstart: '2025-03-01T06:30:37.011Z',
tzid: 'UTC',
},
id: 'snooze_schedule_1',
},
],
},
version: '9.0.0',
});

const invalidParams = {
id: '123',
scheduleIds: ['random_schedule_id'],
isPublic: true,
};

await expect(unsnoozeRule(context, invalidParams)).rejects.toThrowErrorMatchingInlineSnapshot(
`"Rule has no snooze schedule with id random_schedule_id."`
);
});
});
Loading