Skip to content

Commit

Permalink
[Response Ops] Adds recovery context for ES query rule type (elastic#…
Browse files Browse the repository at this point in the history
…132839)

* Renaming alert to rule for es query rule type

* adding recovery context

* Updating unit tests

* Fixing i18n

* Adding functional test

* Adding functional test

* Fixing functional test

* Adding space id to link

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
2 people authored and Muhammad Ibragimov committed Jun 7, 2022
1 parent 41a1671 commit 3c2023c
Show file tree
Hide file tree
Showing 21 changed files with 475 additions and 298 deletions.
Expand Up @@ -5,13 +5,13 @@
* 2.0.
*/

import { EsQueryAlertActionContext, addMessages } from './action_context';
import { EsQueryAlertParamsSchema } from './alert_type_params';
import { OnlyEsQueryAlertParams } from './types';
import { EsQueryRuleActionContext, addMessages } from './action_context';
import { EsQueryRuleParamsSchema } from './rule_type_params';
import { OnlyEsQueryRuleParams } from './types';

describe('ActionContext', () => {
it('generates expected properties', async () => {
const params = EsQueryAlertParamsSchema.validate({
const params = EsQueryRuleParamsSchema.validate({
index: ['[index]'],
timeField: '[timeField]',
esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
Expand All @@ -21,18 +21,18 @@ describe('ActionContext', () => {
thresholdComparator: '>',
threshold: [4],
searchType: 'esQuery',
}) as OnlyEsQueryAlertParams;
const base: EsQueryAlertActionContext = {
}) as OnlyEsQueryRuleParams;
const base: EsQueryRuleActionContext = {
date: '2020-01-01T00:00:00.000Z',
value: 42,
conditions: 'count greater than 4',
hits: [],
link: 'link-mock',
};
const context = addMessages({ name: '[alert-name]' }, base, params);
expect(context.title).toMatchInlineSnapshot(`"alert '[alert-name]' matched query"`);
const context = addMessages({ name: '[rule-name]' }, base, params);
expect(context.title).toMatchInlineSnapshot(`"rule '[rule-name]' matched query"`);
expect(context.message).toEqual(
`alert '[alert-name]' is active:
`rule '[rule-name]' is active:
- Value: 42
- Conditions Met: count greater than 4 over 5m
Expand All @@ -41,8 +41,39 @@ describe('ActionContext', () => {
);
});

it('generates expected properties when isRecovered is true', async () => {
const params = EsQueryRuleParamsSchema.validate({
index: ['[index]'],
timeField: '[timeField]',
esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
size: 100,
timeWindowSize: 5,
timeWindowUnit: 'm',
thresholdComparator: '>',
threshold: [4],
searchType: 'esQuery',
}) as OnlyEsQueryRuleParams;
const base: EsQueryRuleActionContext = {
date: '2020-01-01T00:00:00.000Z',
value: 42,
conditions: 'count not greater than 4',
hits: [],
link: 'link-mock',
};
const context = addMessages({ name: '[rule-name]' }, base, params, true);
expect(context.title).toMatchInlineSnapshot(`"rule '[rule-name]' recovered"`);
expect(context.message).toEqual(
`rule '[rule-name]' is recovered:
- Value: 42
- Conditions Met: count not greater than 4 over 5m
- Timestamp: 2020-01-01T00:00:00.000Z
- Link: link-mock`
);
});

it('generates expected properties if comparator is between', async () => {
const params = EsQueryAlertParamsSchema.validate({
const params = EsQueryRuleParamsSchema.validate({
index: ['[index]'],
timeField: '[timeField]',
esQuery: `{\n \"query\":{\n \"match_all\" : {}\n }\n}`,
Expand All @@ -52,18 +83,18 @@ describe('ActionContext', () => {
thresholdComparator: 'between',
threshold: [4, 5],
searchType: 'esQuery',
}) as OnlyEsQueryAlertParams;
const base: EsQueryAlertActionContext = {
}) as OnlyEsQueryRuleParams;
const base: EsQueryRuleActionContext = {
date: '2020-01-01T00:00:00.000Z',
value: 4,
conditions: 'count between 4 and 5',
hits: [],
link: 'link-mock',
};
const context = addMessages({ name: '[alert-name]' }, base, params);
expect(context.title).toMatchInlineSnapshot(`"alert '[alert-name]' matched query"`);
const context = addMessages({ name: '[rule-name]' }, base, params);
expect(context.title).toMatchInlineSnapshot(`"rule '[rule-name]' matched query"`);
expect(context.message).toEqual(
`alert '[alert-name]' is active:
`rule '[rule-name]' is active:
- Value: 4
- Conditions Met: count between 4 and 5 over 5m
Expand Down
Expand Up @@ -8,60 +8,63 @@
import { i18n } from '@kbn/i18n';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { RuleExecutorOptions, AlertInstanceContext } from '@kbn/alerting-plugin/server';
import { OnlyEsQueryAlertParams, OnlySearchSourceAlertParams } from './types';
import { OnlyEsQueryRuleParams, OnlySearchSourceRuleParams } from './types';

// alert type context provided to actions
// rule type context provided to actions

type AlertInfo = Pick<RuleExecutorOptions, 'name'>;
type RuleInfo = Pick<RuleExecutorOptions, 'name'>;

export interface ActionContext extends EsQueryAlertActionContext {
export interface ActionContext extends EsQueryRuleActionContext {
// a short pre-constructed message which may be used in an action field
title: string;
// a longer pre-constructed message which may be used in an action field
message: string;
}

export interface EsQueryAlertActionContext extends AlertInstanceContext {
// the date the alert was run as an ISO date
export interface EsQueryRuleActionContext extends AlertInstanceContext {
// the date the rule was run as an ISO date
date: string;
// the value that met the threshold
value: number;
// threshold conditions
conditions: string;
// query matches
hits: estypes.SearchHit[];
// a link to see records that triggered the alert for Discover alert
// a link which navigates to stack management in case of Elastic query alert
// a link to see records that triggered the rule for Discover rule
// a link which navigates to stack management in case of Elastic query rule
link: string;
}

export function addMessages(
alertInfo: AlertInfo,
baseContext: EsQueryAlertActionContext,
params: OnlyEsQueryAlertParams | OnlySearchSourceAlertParams
ruleInfo: RuleInfo,
baseContext: EsQueryRuleActionContext,
params: OnlyEsQueryRuleParams | OnlySearchSourceRuleParams,
isRecovered: boolean = false
): ActionContext {
const title = i18n.translate('xpack.stackAlerts.esQuery.alertTypeContextSubjectTitle', {
defaultMessage: `alert '{name}' matched query`,
defaultMessage: `rule '{name}' {verb}`,
values: {
name: alertInfo.name,
name: ruleInfo.name,
verb: isRecovered ? 'recovered' : 'matched query',
},
});

const window = `${params.timeWindowSize}${params.timeWindowUnit}`;
const message = i18n.translate('xpack.stackAlerts.esQuery.alertTypeContextMessageDescription', {
defaultMessage: `alert '{name}' is active:
defaultMessage: `rule '{name}' is {verb}:
- Value: {value}
- Conditions Met: {conditions} over {window}
- Timestamp: {date}
- Link: {link}`,
values: {
name: alertInfo.name,
name: ruleInfo.name,
value: baseContext.value,
conditions: baseContext.conditions,
window,
date: baseContext.date,
link: baseContext.link,
verb: isRecovered ? 'recovered' : 'active',
},
});

Expand Down
Expand Up @@ -5,8 +5,14 @@
* 2.0.
*/

import { getSearchParams, getValidTimefieldSort, tryToParseAsDate } from './executor';
import { OnlyEsQueryAlertParams } from './types';
import {
getSearchParams,
getValidTimefieldSort,
tryToParseAsDate,
getContextConditionsDescription,
} from './executor';
import { OnlyEsQueryRuleParams } from './types';
import { Comparator } from '../../../common/comparator_types';

describe('es_query executor', () => {
const defaultProps = {
Expand Down Expand Up @@ -49,13 +55,13 @@ describe('es_query executor', () => {

describe('getSearchParams', () => {
it('should return search params correctly', () => {
const result = getSearchParams(defaultProps as OnlyEsQueryAlertParams);
const result = getSearchParams(defaultProps as OnlyEsQueryRuleParams);
expect(result.parsedQuery.query).toBe('test-query');
});

it('should throw invalid query error', () => {
expect(() =>
getSearchParams({ ...defaultProps, esQuery: '' } as OnlyEsQueryAlertParams)
getSearchParams({ ...defaultProps, esQuery: '' } as OnlyEsQueryRuleParams)
).toThrow('invalid query specified: "" - query must be JSON');
});

Expand All @@ -64,7 +70,7 @@ describe('es_query executor', () => {
getSearchParams({
...defaultProps,
esQuery: '{ "someProperty": "test-query" }',
} as OnlyEsQueryAlertParams)
} as OnlyEsQueryRuleParams)
).toThrow('invalid query specified: "{ "someProperty": "test-query" }" - query must be JSON');
});

Expand All @@ -74,8 +80,25 @@ describe('es_query executor', () => {
...defaultProps,
timeWindowSize: 5,
timeWindowUnit: 'r',
} as OnlyEsQueryAlertParams)
} as OnlyEsQueryRuleParams)
).toThrow('invalid format for windowSize: "5r"');
});
});

describe('getContextConditionsDescription', () => {
it('should return conditions correctly', () => {
const result = getContextConditionsDescription(Comparator.GT, [10]);
expect(result).toBe(`Number of matching documents is greater than 10`);
});

it('should return conditions correctly when isRecovered is true', () => {
const result = getContextConditionsDescription(Comparator.GT, [10], true);
expect(result).toBe(`Number of matching documents is NOT greater than 10`);
});

it('should return conditions correctly when multiple thresholds provided', () => {
const result = getContextConditionsDescription(Comparator.BETWEEN, [10, 20], true);
expect(result).toBe(`Number of matching documents is NOT between 10 and 20`);
});
});
});

0 comments on commit 3c2023c

Please sign in to comment.