Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function useFetchRules({
page,
searchText,
typesFilter: typesFilter.length > 0 ? typesFilter : OBSERVABILITY_RULE_TYPES,
ruleStatusesFilter: ruleLastResponseFilter,
ruleExecutionStatusesFilter: ruleLastResponseFilter,
sort,
});
setRulesState((oldState) => ({
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -29364,7 +29364,6 @@
"xpack.triggersActionsUI.sections.rulesList.ruleStatusActive": "Actif",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusDropdownMenuLabel": "Modifier le statut de la règle ou répéter",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusError": "Erreur",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusFilterLabel": "Dernière réponse",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusLicenseError": "Erreur de licence",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusOk": "Ok",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusPending": "En attente",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -29433,7 +29433,6 @@
"xpack.triggersActionsUI.sections.rulesList.ruleStatusActive": "アクティブ",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusDropdownMenuLabel": "ルールステータスまたはスヌーズを変更",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusError": "エラー",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusFilterLabel": "前回の応答",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusLicenseError": "ライセンスエラー",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusOk": "OK",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusPending": "保留中",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -29468,7 +29468,6 @@
"xpack.triggersActionsUI.sections.rulesList.ruleStatusActive": "活动",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusDropdownMenuLabel": "更改规则状态或暂停",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusError": "错误",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusFilterLabel": "上次响应",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusLicenseError": "许可证错误",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusOk": "确定",
"xpack.triggersActionsUI.sections.rulesList.ruleStatusPending": "待处理",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const allowedExperimentalValues = Object.freeze({
rulesListDatagrid: true,
internalAlertsTable: false,
internalShareableComponentsSandbox: false,
ruleStatusFilter: false,
rulesDetailLogs: true,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 React, { useState } from 'react';
import { RuleStatusFilterProps } from '../../../types';
import { getRuleStatusFilterLazy } from '../../../common/get_rule_status_filter';

export const RuleStatusFilterSandbox = () => {
const [selectedStatuses, setSelectedStatuses] = useState<
RuleStatusFilterProps['selectedStatuses']
>([]);

return (
<div style={{ flex: 1 }}>
{getRuleStatusFilterLazy({
selectedStatuses,
onChange: setSelectedStatuses,
})}
<div>Selected states: {JSON.stringify(selectedStatuses)}</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

import React from 'react';
import { RuleStatusDropdownSandbox } from './rule_status_dropdown_sandbox';
import { RuleStatusFilterSandbox } from './rule_status_filter_sandbox';
import { RuleTagBadgeSandbox } from './rule_tag_badge_sandbox';

export const InternalShareableComponentsSandbox: React.FC<{}> = () => {
return (
<>
<RuleStatusDropdownSandbox />
<RuleStatusFilterSandbox />
<RuleTagBadgeSandbox />
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,84 @@ describe('loadRuleAggregations', () => {
]
`);
});

test('should call aggregate API with ruleStatusesFilter', async () => {
const resolvedValue = {
rule_execution_status: {
ok: 4,
active: 2,
error: 1,
pending: 1,
unknown: 0,
},
};
http.get.mockResolvedValue(resolvedValue);

let result = await loadRuleAggregations({
http,
ruleStatusesFilter: ['enabled'],
});

expect(result).toEqual({
ruleExecutionStatus: {
ok: 4,
active: 2,
error: 1,
pending: 1,
unknown: 0,
},
});

expect(http.get.mock.calls[0]).toMatchInlineSnapshot(`
Array [
"/internal/alerting/rules/_aggregate",
Object {
"query": Object {
"default_search_operator": "AND",
"filter": "alert.attributes.enabled:(true) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
"search": undefined,
"search_fields": undefined,
},
},
]
`);

result = await loadRuleAggregations({
http,
ruleStatusesFilter: ['enabled', 'snoozed'],
});

expect(http.get.mock.calls[1]).toMatchInlineSnapshot(`
Array [
"/internal/alerting/rules/_aggregate",
Object {
"query": Object {
"default_search_operator": "AND",
"filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
"search": undefined,
"search_fields": undefined,
},
},
]
`);

result = await loadRuleAggregations({
http,
ruleStatusesFilter: ['enabled', 'disabled', 'snoozed'],
});

expect(http.get.mock.calls[1]).toMatchInlineSnapshot(`
Array [
"/internal/alerting/rules/_aggregate",
Object {
"query": Object {
"default_search_operator": "AND",
"filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
"search": undefined,
"search_fields": undefined,
},
},
]
`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import { HttpSetup } from '@kbn/core/public';
import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common';
import { RuleAggregations } from '../../../types';
import { RuleAggregations, RuleStatus } from '../../../types';
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
import { mapFiltersToKql } from './map_filters_to_kql';

Expand All @@ -29,15 +29,22 @@ export async function loadRuleAggregations({
searchText,
typesFilter,
actionTypesFilter,
ruleExecutionStatusesFilter,
ruleStatusesFilter,
}: {
http: HttpSetup;
searchText?: string;
typesFilter?: string[];
actionTypesFilter?: string[];
ruleStatusesFilter?: string[];
ruleExecutionStatusesFilter?: string[];
ruleStatusesFilter?: RuleStatus[];
}): Promise<RuleAggregations> {
const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, ruleStatusesFilter });
Copy link
Copy Markdown
Contributor

@mgiota mgiota Apr 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JiaweiWu Out of curiosity I was wondering why the new ruleStatusesFilter is not included here. Is it because the options of the rule status filter is a static hardcoded list (enabled, disabled, snoozed)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to filter the aggregate results as well? I suppose we do, I can add it

const filters = mapFiltersToKql({
typesFilter,
actionTypesFilter,
ruleExecutionStatusesFilter,
ruleStatusesFilter,
});
const res = await http.get<AsApiContract<RuleAggregations>>(
`${INTERNAL_BASE_ALERTING_API_PATH}/rules/_aggregate`,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,62 @@ describe('mapFiltersToKql', () => {
]);
});

test('should handle ruleStatusesFilter', () => {
test('should handle ruleExecutionStatusesFilter', () => {
expect(
mapFiltersToKql({
ruleStatusesFilter: ['alert', 'statuses', 'filter'],
ruleExecutionStatusesFilter: ['alert', 'statuses', 'filter'],
})
).toEqual(['alert.attributes.executionStatus.status:(alert or statuses or filter)']);
});

test('should handle ruleStatusesFilter', () => {
expect(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add a test for just disabled too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, good idea!

mapFiltersToKql({
ruleStatusesFilter: ['enabled'],
})
).toEqual([
'alert.attributes.enabled:(true) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
]);

expect(
mapFiltersToKql({
ruleStatusesFilter: ['disabled'],
})
).toEqual([
'alert.attributes.enabled:(false) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
]);

expect(
mapFiltersToKql({
ruleStatusesFilter: ['snoozed'],
})
).toEqual(['(alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)']);

expect(
mapFiltersToKql({
ruleStatusesFilter: ['enabled', 'snoozed'],
})
).toEqual([
'alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
]);

expect(
mapFiltersToKql({
ruleStatusesFilter: ['disabled', 'snoozed'],
})
).toEqual([
'alert.attributes.enabled:(false) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
]);

expect(
mapFiltersToKql({
ruleStatusesFilter: ['enabled', 'disabled', 'snoozed'],
})
).toEqual([
'alert.attributes.enabled:(true or false) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
]);
});

test('should handle typesFilter and actionTypesFilter', () => {
expect(
mapFiltersToKql({
Expand All @@ -52,12 +100,12 @@ describe('mapFiltersToKql', () => {
]);
});

test('should handle typesFilter, actionTypesFilter and ruleStatusesFilter', () => {
test('should handle typesFilter, actionTypesFilter and ruleExecutionStatusesFilter', () => {
expect(
mapFiltersToKql({
typesFilter: ['type', 'filter'],
actionTypesFilter: ['action', 'types', 'filter'],
ruleStatusesFilter: ['alert', 'statuses', 'filter'],
ruleExecutionStatusesFilter: ['alert', 'statuses', 'filter'],
})
).toEqual([
'alert.attributes.alertTypeId:(type or filter)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,34 @@
* 2.0.
*/

import { RuleStatus } from '../../../types';

const getEnablementFilter = (ruleStatusFilter: RuleStatus[] = []) => {
const enablementFilters = ruleStatusFilter.reduce<string[]>((result, filter) => {
if (filter === 'enabled') {
return [...result, 'true'];
}
if (filter === 'disabled') {
return [...result, 'false'];
}
return result;
}, []);
return `alert.attributes.enabled:(${enablementFilters.join(' or ')})`;
};

export const mapFiltersToKql = ({
typesFilter,
actionTypesFilter,
ruleExecutionStatusesFilter,
ruleStatusesFilter,
}: {
typesFilter?: string[];
actionTypesFilter?: string[];
ruleStatusesFilter?: string[];
ruleExecutionStatusesFilter?: string[];
ruleStatusesFilter?: RuleStatus[];
}): string[] => {
const filters = [];

if (typesFilter && typesFilter.length) {
filters.push(`alert.attributes.alertTypeId:(${typesFilter.join(' or ')})`);
}
Expand All @@ -29,8 +47,27 @@ export const mapFiltersToKql = ({
].join('')
);
}
if (ruleExecutionStatusesFilter && ruleExecutionStatusesFilter.length) {
filters.push(
`alert.attributes.executionStatus.status:(${ruleExecutionStatusesFilter.join(' or ')})`
);
}

if (ruleStatusesFilter && ruleStatusesFilter.length) {
filters.push(`alert.attributes.executionStatus.status:(${ruleStatusesFilter.join(' or ')})`);
const enablementFilter = getEnablementFilter(ruleStatusesFilter);
const snoozedFilter = `(alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)`;
const hasEnablement =
ruleStatusesFilter.includes('enabled') || ruleStatusesFilter.includes('disabled');
const hasSnoozed = ruleStatusesFilter.includes('snoozed');

if (hasEnablement && !hasSnoozed) {
filters.push(`${enablementFilter} and not ${snoozedFilter}`);
} else if (!hasEnablement && hasSnoozed) {
filters.push(snoozedFilter);
} else {
filters.push(`${enablementFilter} or ${snoozedFilter}`);
}
}

return filters;
};
Loading