Skip to content

Commit b5085c9

Browse files
umbopepatobenakansara
authored andcommitted
[ResponseOps][Alerts] Fix muted alerts query using wrong filter (elastic#204182)
## Summary Reverts the `getMutedAlerts` filter to use rule ids instead of rule type ids that caused the muted alerts state to be always empty. ## To verify 1. Create a rule that fire alerts 2. Navigate to `Stack management > Alerts` 3. Click on any alert actions menu (•••) 4. Toggle on and off the alert muted state and check that the action updates accordingly and the slashed bell icon appears in the status cell when muted ## Release note Fix alert mute/unmute action
1 parent 3ab9da8 commit b5085c9

File tree

4 files changed

+150
-2
lines changed

4 files changed

+150
-2
lines changed

x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_muted_alerts.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,28 @@ describe('getMutedAlerts', () => {
3737
Array [
3838
"/internal/alerting/rules/_find",
3939
Object {
40-
"body": "{\\"rule_type_ids\\":[\\"foo\\"],\\"fields\\":[\\"id\\",\\"mutedInstanceIds\\"],\\"page\\":1,\\"per_page\\":1}",
40+
"body": "{\\"filter\\":\\"{\\\\\\"type\\\\\\":\\\\\\"function\\\\\\",\\\\\\"function\\\\\\":\\\\\\"is\\\\\\",\\\\\\"arguments\\\\\\":[{\\\\\\"type\\\\\\":\\\\\\"literal\\\\\\",\\\\\\"value\\\\\\":\\\\\\"alert.id\\\\\\",\\\\\\"isQuoted\\\\\\":false},{\\\\\\"type\\\\\\":\\\\\\"literal\\\\\\",\\\\\\"value\\\\\\":\\\\\\"alert:foo\\\\\\",\\\\\\"isQuoted\\\\\\":false}]}\\",\\"fields\\":[\\"id\\",\\"mutedInstanceIds\\"],\\"page\\":1,\\"per_page\\":1}",
41+
"signal": undefined,
42+
},
43+
]
44+
`);
45+
});
46+
47+
test('should call find API with multiple ruleIds', async () => {
48+
const result = await getMutedAlerts(http, { ruleIds: ['foo', 'bar'] });
49+
50+
expect(result).toEqual({
51+
page: 1,
52+
per_page: 10,
53+
total: 0,
54+
data: [],
55+
});
56+
57+
expect(http.post.mock.calls[0]).toMatchInlineSnapshot(`
58+
Array [
59+
"/internal/alerting/rules/_find",
60+
Object {
61+
"body": "{\\"filter\\":\\"{\\\\\\"type\\\\\\":\\\\\\"function\\\\\\",\\\\\\"function\\\\\\":\\\\\\"or\\\\\\",\\\\\\"arguments\\\\\\":[{\\\\\\"type\\\\\\":\\\\\\"function\\\\\\",\\\\\\"function\\\\\\":\\\\\\"is\\\\\\",\\\\\\"arguments\\\\\\":[{\\\\\\"type\\\\\\":\\\\\\"literal\\\\\\",\\\\\\"value\\\\\\":\\\\\\"alert.id\\\\\\",\\\\\\"isQuoted\\\\\\":false},{\\\\\\"type\\\\\\":\\\\\\"literal\\\\\\",\\\\\\"value\\\\\\":\\\\\\"alert:foo\\\\\\",\\\\\\"isQuoted\\\\\\":false}]},{\\\\\\"type\\\\\\":\\\\\\"function\\\\\\",\\\\\\"function\\\\\\":\\\\\\"is\\\\\\",\\\\\\"arguments\\\\\\":[{\\\\\\"type\\\\\\":\\\\\\"literal\\\\\\",\\\\\\"value\\\\\\":\\\\\\"alert.id\\\\\\",\\\\\\"isQuoted\\\\\\":false},{\\\\\\"type\\\\\\":\\\\\\"literal\\\\\\",\\\\\\"value\\\\\\":\\\\\\"alert:bar\\\\\\",\\\\\\"isQuoted\\\\\\":false}]}]}\\",\\"fields\\":[\\"id\\",\\"mutedInstanceIds\\"],\\"page\\":1,\\"per_page\\":2}",
4162
"signal": undefined,
4263
},
4364
]

x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/apis/get_rules_muted_alerts.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import { HttpStart } from '@kbn/core-http-browser';
9+
import { nodeBuilder } from '@kbn/es-query';
910

1011
const INTERNAL_FIND_RULES_URL = '/internal/alerting/rules/_find';
1112

@@ -23,9 +24,12 @@ export const getMutedAlerts = async (
2324
params: { ruleIds: string[] },
2425
signal?: AbortSignal
2526
) => {
27+
const filterNode = nodeBuilder.or(
28+
params.ruleIds.map((id) => nodeBuilder.is('alert.id', `alert:${id}`))
29+
);
2630
return http.post<FindRulesResponse>(INTERNAL_FIND_RULES_URL, {
2731
body: JSON.stringify({
28-
rule_type_ids: params.ruleIds,
32+
filter: JSON.stringify(filterNode),
2933
fields: ['id', 'mutedInstanceIds'],
3034
page: 1,
3135
per_page: params.ruleIds.length,

x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC
1818
loadTestFile(require.resolve('./builtin_alert_types'));
1919
loadTestFile(require.resolve('./mustache_templates.ts'));
2020
loadTestFile(require.resolve('./notify_when'));
21+
loadTestFile(require.resolve('./muted_alerts'));
2122
loadTestFile(require.resolve('./event_log_alerts'));
2223
loadTestFile(require.resolve('./snooze'));
2324
loadTestFile(require.resolve('./unsnooze'));
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import expect from '@kbn/expect';
9+
import { ES_TEST_INDEX_NAME } from '@kbn/alerting-api-integration-helpers';
10+
import { ALERT_INSTANCE_ID, ALERT_RULE_UUID, ALERT_STATUS } from '@kbn/rule-data-utils';
11+
import { nodeBuilder } from '@kbn/es-query';
12+
import { Spaces } from '../../../scenarios';
13+
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
14+
import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib';
15+
16+
const alertAsDataIndex = '.internal.alerts-observability.test.alerts.alerts-default-000001';
17+
18+
// eslint-disable-next-line import/no-default-export
19+
export default function createDisableRuleTests({ getService }: FtrProviderContext) {
20+
const es = getService('es');
21+
const retry = getService('retry');
22+
const supertest = getService('supertest');
23+
24+
describe('mutedAlerts', () => {
25+
const objectRemover = new ObjectRemover(supertest);
26+
27+
const createRule = async () => {
28+
const { body: createdRule } = await supertest
29+
.post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`)
30+
.set('kbn-xsrf', 'foo')
31+
.send(
32+
getTestRuleData({
33+
rule_type_id: 'test.always-firing-alert-as-data',
34+
schedule: { interval: '24h' },
35+
throttle: undefined,
36+
notify_when: undefined,
37+
params: {
38+
index: ES_TEST_INDEX_NAME,
39+
reference: 'test',
40+
},
41+
})
42+
)
43+
.expect(200);
44+
45+
objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting');
46+
return createdRule.id;
47+
};
48+
49+
const getAlerts = async () => {
50+
const {
51+
hits: { hits: alerts },
52+
} = await es.search({
53+
index: alertAsDataIndex,
54+
body: { query: { match_all: {} } },
55+
});
56+
57+
return alerts;
58+
};
59+
60+
afterEach(async () => {
61+
await es.deleteByQuery({
62+
index: alertAsDataIndex,
63+
query: {
64+
match_all: {},
65+
},
66+
conflicts: 'proceed',
67+
ignore_unavailable: true,
68+
});
69+
await objectRemover.removeAll();
70+
});
71+
72+
it('should reflect muted alert instance ids in rule', async () => {
73+
const createdRule1 = await createRule();
74+
const createdRule2 = await createRule();
75+
76+
let alerts: any[] = [];
77+
78+
await retry.try(async () => {
79+
alerts = await getAlerts();
80+
81+
expect(alerts.length).greaterThan(2);
82+
alerts.forEach((activeAlert: any) => {
83+
expect(activeAlert._source[ALERT_STATUS]).eql('active');
84+
});
85+
});
86+
87+
const alertFromRule1 = alerts.find(
88+
(alert: any) =>
89+
alert._source[ALERT_STATUS] === 'active' &&
90+
alert._source[ALERT_RULE_UUID] === createdRule1
91+
);
92+
93+
await supertest
94+
.post(
95+
`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${encodeURIComponent(
96+
createdRule1
97+
)}/alert/${encodeURIComponent(alertFromRule1._source['kibana.alert.instance.id'])}/_mute`
98+
)
99+
.set('kbn-xsrf', 'foo')
100+
.expect(204);
101+
102+
const ruleUuids = [createdRule1, createdRule2];
103+
104+
const filterNode = nodeBuilder.or(
105+
ruleUuids.map((id) => nodeBuilder.is('alert.id', `alert:${id}`))
106+
);
107+
const { body: rules } = await supertest
108+
.post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_find`)
109+
.set('kbn-xsrf', 'foo')
110+
.send({
111+
filter: JSON.stringify(filterNode),
112+
fields: ['id', 'mutedInstanceIds'],
113+
page: 1,
114+
per_page: ruleUuids.length,
115+
});
116+
117+
expect(rules.data.length).to.be(2);
118+
const mutedRule = rules.data.find((rule: { id: string }) => rule.id === createdRule1);
119+
expect(mutedRule.muted_alert_ids).to.contain(alertFromRule1._source[ALERT_INSTANCE_ID]);
120+
});
121+
});
122+
}

0 commit comments

Comments
 (0)