mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Alerting] add more alert properties to action parameter templating (#59718)
This is a pre-cursor to https://github.com/elastic/kibana/issues/58529 I realized a bit ago that we weren't making quite enough info available in the action parameter templating that happens when alerts schedule actions to execute. Missing were alert name, tags, and spaceId. For the index threshold alert, I had added them to it's context, but then every other action would have to do the same if they also wanted those values. So I added these as additional top-level variables that can be used in templates, along with the alert id, alert instance id, context, and state. The other bits in RawAlert didn't seem that interesting, to be used as an action parameter.
This commit is contained in:
parent
8c0da24e6c
commit
7db4196164
12 changed files with 266 additions and 76 deletions
|
@ -388,7 +388,15 @@ This factory returns an instance of `AlertInstance`. The alert instance class ha
|
|||
|
||||
There needs to be a way to map alert context into action parameters. For this, we started off by adding template support. Any string within the `params` of an alert saved object's `actions` will be processed as a template and can inject context or state values.
|
||||
|
||||
When an alert instance executes, the first argument is the `group` of actions to execute and the second is the context the alert exposes to templates. We iterate through each action params attributes recursively and render templates if they are a string. Templates have access to the `context` (provided by second argument of `.scheduleActions(...)` on an alert instance) and the alert instance's `state` (provided by the most recent `replaceState` call on an alert instance) as well as `alertId` and `alertInstanceId`.
|
||||
When an alert instance executes, the first argument is the `group` of actions to execute and the second is the context the alert exposes to templates. We iterate through each action params attributes recursively and render templates if they are a string. Templates have access to the following "variables":
|
||||
|
||||
- `context` - provided by second argument of `.scheduleActions(...)` on an alert instance
|
||||
- `state` - the alert instance's `state` provided by the most recent `replaceState` call on an alert instance
|
||||
- `alertId` - the id of the alert
|
||||
- `alertInstanceId` - the alert instance id
|
||||
- `alertName` - the name of the alert
|
||||
- `spaceId` - the id of the space the alert exists in
|
||||
- `tags` - the tags set in the alert
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -410,6 +418,7 @@ Below is an example of an alert that takes advantage of templating:
|
|||
{
|
||||
...
|
||||
id: "123",
|
||||
name: "cpu alert",
|
||||
actions: [
|
||||
{
|
||||
"group": "default",
|
||||
|
@ -418,7 +427,7 @@ Below is an example of an alert that takes advantage of templating:
|
|||
"from": "example@elastic.co",
|
||||
"to": ["destination@elastic.co"],
|
||||
"subject": "A notification about {{context.server}}"
|
||||
"body": "The server {{context.server}} has a CPU usage of {{state.cpuUsage}}%. This message for {{alertInstanceId}} was created by the alert {{alertId}}."
|
||||
"body": "The server {{context.server}} has a CPU usage of {{state.cpuUsage}}%. This message for {{alertInstanceId}} was created by the alert {{alertId}} {{alertName}}."
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -432,7 +441,7 @@ The templating system will take the alert and alert type as described above and
|
|||
"from": "example@elastic.co",
|
||||
"to": ["destination@elastic.co"],
|
||||
"subject": "A notification about server_1"
|
||||
"body": "The server server_1 has a CPU usage of 80%. This message for server_1 was created by the alert 123"
|
||||
"body": "The server server_1 has a CPU usage of 80%. This message for server_1 was created by the alert 123 cpu alert"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ const createExecutionHandlerParams = {
|
|||
executeAction: jest.fn(),
|
||||
spaceId: 'default',
|
||||
alertId: '1',
|
||||
alertName: 'name-of-alert',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
apiKey: 'MTIzOmFiYw==',
|
||||
spaceIdToNamespace: jest.fn().mockReturnValue(undefined),
|
||||
getBasePath: jest.fn().mockReturnValue(undefined),
|
||||
|
@ -37,6 +39,7 @@ const createExecutionHandlerParams = {
|
|||
foo: true,
|
||||
contextVal: 'My {{context.value}} goes here',
|
||||
stateVal: 'My {{state.value}} goes here',
|
||||
alertVal: 'My {{alertId}} {{alertName}} {{spaceId}} {{tags}} {{alertInstanceId}} goes here',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -59,6 +62,7 @@ test('calls executeAction per selected action', async () => {
|
|||
"apiKey": "MTIzOmFiYw==",
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here",
|
||||
"contextVal": "My goes here",
|
||||
"foo": true,
|
||||
"stateVal": "My goes here",
|
||||
|
@ -95,6 +99,7 @@ test('context attribute gets parameterized', async () => {
|
|||
"apiKey": "MTIzOmFiYw==",
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here",
|
||||
"contextVal": "My context-val goes here",
|
||||
"foo": true,
|
||||
"stateVal": "My goes here",
|
||||
|
@ -120,6 +125,7 @@ test('state attribute gets parameterized', async () => {
|
|||
"apiKey": "MTIzOmFiYw==",
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"alertVal": "My 1 name-of-alert default tag-A,tag-B 2 goes here",
|
||||
"contextVal": "My goes here",
|
||||
"foo": true,
|
||||
"stateVal": "My state-val goes here",
|
||||
|
|
|
@ -12,6 +12,8 @@ import { PluginStartContract as ActionsPluginStartContract } from '../../../../p
|
|||
|
||||
interface CreateExecutionHandlerOptions {
|
||||
alertId: string;
|
||||
alertName: string;
|
||||
tags?: string[];
|
||||
executeAction: ActionsPluginStartContract['execute'];
|
||||
actions: AlertAction[];
|
||||
spaceId: string;
|
||||
|
@ -30,6 +32,8 @@ interface ExecutionHandlerOptions {
|
|||
export function createExecutionHandler({
|
||||
logger,
|
||||
alertId,
|
||||
alertName,
|
||||
tags,
|
||||
executeAction,
|
||||
actions: alertActions,
|
||||
spaceId,
|
||||
|
@ -49,9 +53,12 @@ export function createExecutionHandler({
|
|||
...action,
|
||||
params: transformActionParams({
|
||||
alertId,
|
||||
alertName,
|
||||
spaceId,
|
||||
tags,
|
||||
alertInstanceId,
|
||||
context,
|
||||
params: action.params,
|
||||
actionParams: action.params,
|
||||
state,
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -93,8 +93,10 @@ export class TaskRunner {
|
|||
return this.context.getServices(fakeRequest);
|
||||
}
|
||||
|
||||
getExecutionHandler(
|
||||
private getExecutionHandler(
|
||||
alertId: string,
|
||||
alertName: string,
|
||||
tags: string[] | undefined,
|
||||
spaceId: string,
|
||||
apiKey: string | null,
|
||||
actions: RawAlert['actions'],
|
||||
|
@ -114,6 +116,8 @@ export class TaskRunner {
|
|||
|
||||
return createExecutionHandler({
|
||||
alertId,
|
||||
alertName,
|
||||
tags,
|
||||
logger: this.logger,
|
||||
executeAction: this.context.executeAction,
|
||||
apiKey,
|
||||
|
@ -225,6 +229,8 @@ export class TaskRunner {
|
|||
const params = validateAlertTypeParams(this.alertType, attributes.params);
|
||||
const executionHandler = this.getExecutionHandler(
|
||||
alertId,
|
||||
attributes.name,
|
||||
attributes.tags,
|
||||
spaceId,
|
||||
apiKey,
|
||||
attributes.actions,
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { transformActionParams } from './transform_action_params';
|
||||
|
||||
test('skips non string parameters', () => {
|
||||
const params = {
|
||||
const actionParams = {
|
||||
boolean: true,
|
||||
number: 1,
|
||||
empty1: null,
|
||||
|
@ -15,10 +15,13 @@ test('skips non string parameters', () => {
|
|||
date: '2019-02-12T21:01:22.479Z',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
params,
|
||||
actionParams,
|
||||
context: {},
|
||||
state: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
@ -33,15 +36,18 @@ test('skips non string parameters', () => {
|
|||
});
|
||||
|
||||
test('missing parameters get emptied out', () => {
|
||||
const params = {
|
||||
const actionParams = {
|
||||
message1: '{{context.value}}',
|
||||
message2: 'This message "{{context.value2}}" is missing',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
params,
|
||||
actionParams,
|
||||
context: {},
|
||||
state: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
@ -53,14 +59,17 @@ test('missing parameters get emptied out', () => {
|
|||
});
|
||||
|
||||
test('context parameters are passed to templates', () => {
|
||||
const params = {
|
||||
const actionParams = {
|
||||
message: 'Value "{{context.foo}}" exists',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
params,
|
||||
actionParams,
|
||||
state: {},
|
||||
context: { foo: 'fooVal' },
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
@ -71,14 +80,17 @@ test('context parameters are passed to templates', () => {
|
|||
});
|
||||
|
||||
test('state parameters are passed to templates', () => {
|
||||
const params = {
|
||||
const actionParams = {
|
||||
message: 'Value "{{state.bar}}" exists',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
params,
|
||||
actionParams,
|
||||
state: { bar: 'barVal' },
|
||||
context: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
@ -89,14 +101,17 @@ test('state parameters are passed to templates', () => {
|
|||
});
|
||||
|
||||
test('alertId is passed to templates', () => {
|
||||
const params = {
|
||||
const actionParams = {
|
||||
message: 'Value "{{alertId}}" exists',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
params,
|
||||
actionParams,
|
||||
state: {},
|
||||
context: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
@ -106,15 +121,122 @@ test('alertId is passed to templates', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('alertInstanceId is passed to templates', () => {
|
||||
const params = {
|
||||
message: 'Value "{{alertInstanceId}}" exists',
|
||||
test('alertName is passed to templates', () => {
|
||||
const actionParams = {
|
||||
message: 'Value "{{alertName}}" exists',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
params,
|
||||
actionParams,
|
||||
state: {},
|
||||
context: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"message": "Value \\"alert-name\\" exists",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('tags is passed to templates', () => {
|
||||
const actionParams = {
|
||||
message: 'Value "{{tags}}" exists',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
actionParams,
|
||||
state: {},
|
||||
context: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"message": "Value \\"tag-A,tag-B\\" exists",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('undefined tags is passed to templates', () => {
|
||||
const actionParams = {
|
||||
message: 'Value "{{tags}}" is undefined and renders as empty string',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
actionParams,
|
||||
state: {},
|
||||
context: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"message": "Value \\"\\" is undefined and renders as empty string",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('empty tags is passed to templates', () => {
|
||||
const actionParams = {
|
||||
message: 'Value "{{tags}}" is an empty array and renders as empty string',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
actionParams,
|
||||
state: {},
|
||||
context: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: [],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"message": "Value \\"\\" is an empty array and renders as empty string",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('spaceId is passed to templates', () => {
|
||||
const actionParams = {
|
||||
message: 'Value "{{spaceId}}" exists',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
actionParams,
|
||||
state: {},
|
||||
context: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"message": "Value \\"spaceId-A\\" exists",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('alertInstanceId is passed to templates', () => {
|
||||
const actionParams = {
|
||||
message: 'Value "{{alertInstanceId}}" exists',
|
||||
};
|
||||
const result = transformActionParams({
|
||||
actionParams,
|
||||
state: {},
|
||||
context: {},
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
@ -125,16 +247,19 @@ test('alertInstanceId is passed to templates', () => {
|
|||
});
|
||||
|
||||
test('works recursively', () => {
|
||||
const params = {
|
||||
const actionParams = {
|
||||
body: {
|
||||
message: 'State: "{{state.value}}", Context: "{{context.value}}"',
|
||||
},
|
||||
};
|
||||
const result = transformActionParams({
|
||||
params,
|
||||
actionParams,
|
||||
state: { value: 'state' },
|
||||
context: { value: 'context' },
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
@ -147,16 +272,19 @@ test('works recursively', () => {
|
|||
});
|
||||
|
||||
test('works recursively with arrays', () => {
|
||||
const params = {
|
||||
const actionParams = {
|
||||
body: {
|
||||
messages: ['State: "{{state.value}}", Context: "{{context.value}}"'],
|
||||
},
|
||||
};
|
||||
const result = transformActionParams({
|
||||
params,
|
||||
actionParams,
|
||||
state: { value: 'state' },
|
||||
context: { value: 'context' },
|
||||
alertId: '1',
|
||||
alertName: 'alert-name',
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
spaceId: 'spaceId-A',
|
||||
alertInstanceId: '2',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
|
|
@ -10,23 +10,38 @@ import { AlertActionParams, State, Context } from '../types';
|
|||
|
||||
interface TransformActionParamsOptions {
|
||||
alertId: string;
|
||||
alertName: string;
|
||||
spaceId: string;
|
||||
tags?: string[];
|
||||
alertInstanceId: string;
|
||||
params: AlertActionParams;
|
||||
actionParams: AlertActionParams;
|
||||
state: State;
|
||||
context: Context;
|
||||
}
|
||||
|
||||
export function transformActionParams({
|
||||
alertId,
|
||||
alertName,
|
||||
spaceId,
|
||||
tags,
|
||||
alertInstanceId,
|
||||
context,
|
||||
params,
|
||||
actionParams,
|
||||
state,
|
||||
}: TransformActionParamsOptions): AlertActionParams {
|
||||
const result = cloneDeep(params, (value: any) => {
|
||||
const result = cloneDeep(actionParams, (value: any) => {
|
||||
if (!isString(value)) return;
|
||||
|
||||
return Mustache.render(value, { alertId, alertInstanceId, context, state });
|
||||
const variables = {
|
||||
alertId,
|
||||
alertName,
|
||||
spaceId,
|
||||
tags,
|
||||
alertInstanceId,
|
||||
context,
|
||||
state,
|
||||
};
|
||||
return Mustache.render(value, variables);
|
||||
});
|
||||
|
||||
// The return type signature for `cloneDeep()` ends up taking the return
|
||||
|
|
|
@ -12,9 +12,6 @@ describe('ActionContext', () => {
|
|||
const base: BaseActionContext = {
|
||||
date: '2020-01-01T00:00:00.000Z',
|
||||
group: '[group]',
|
||||
name: '[name]',
|
||||
spaceId: '[spaceId]',
|
||||
namespace: '[spaceId]',
|
||||
value: 42,
|
||||
};
|
||||
const params = ParamsSchema.validate({
|
||||
|
@ -29,12 +26,15 @@ describe('ActionContext', () => {
|
|||
thresholdComparator: '>',
|
||||
threshold: [4],
|
||||
});
|
||||
const context = addMessages(base, params);
|
||||
const alertInfo = {
|
||||
name: '[alert-name]',
|
||||
};
|
||||
const context = addMessages(alertInfo, base, params);
|
||||
expect(context.subject).toMatchInlineSnapshot(
|
||||
`"alert [name] group [group] exceeded threshold"`
|
||||
`"alert [alert-name] group [group] exceeded threshold"`
|
||||
);
|
||||
expect(context.message).toMatchInlineSnapshot(
|
||||
`"alert [name] group [group] value 42 exceeded threshold count > 4 over 5m on 2020-01-01T00:00:00.000Z"`
|
||||
`"alert [alert-name] group [group] value 42 exceeded threshold count > 4 over 5m on 2020-01-01T00:00:00.000Z"`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -42,9 +42,6 @@ describe('ActionContext', () => {
|
|||
const base: BaseActionContext = {
|
||||
date: '2020-01-01T00:00:00.000Z',
|
||||
group: '[group]',
|
||||
name: '[name]',
|
||||
spaceId: '[spaceId]',
|
||||
namespace: '[spaceId]',
|
||||
value: 42,
|
||||
};
|
||||
const params = ParamsSchema.validate({
|
||||
|
@ -60,12 +57,15 @@ describe('ActionContext', () => {
|
|||
thresholdComparator: '>',
|
||||
threshold: [4.2],
|
||||
});
|
||||
const context = addMessages(base, params);
|
||||
const alertInfo = {
|
||||
name: '[alert-name]',
|
||||
};
|
||||
const context = addMessages(alertInfo, base, params);
|
||||
expect(context.subject).toMatchInlineSnapshot(
|
||||
`"alert [name] group [group] exceeded threshold"`
|
||||
`"alert [alert-name] group [group] exceeded threshold"`
|
||||
);
|
||||
expect(context.message).toMatchInlineSnapshot(
|
||||
`"alert [name] group [group] value 42 exceeded threshold avg([aggField]) > 4.2 over 5m on 2020-01-01T00:00:00.000Z"`
|
||||
`"alert [alert-name] group [group] value 42 exceeded threshold avg([aggField]) > 4.2 over 5m on 2020-01-01T00:00:00.000Z"`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -73,9 +73,6 @@ describe('ActionContext', () => {
|
|||
const base: BaseActionContext = {
|
||||
date: '2020-01-01T00:00:00.000Z',
|
||||
group: '[group]',
|
||||
name: '[name]',
|
||||
spaceId: '[spaceId]',
|
||||
namespace: '[spaceId]',
|
||||
value: 4,
|
||||
};
|
||||
const params = ParamsSchema.validate({
|
||||
|
@ -90,12 +87,15 @@ describe('ActionContext', () => {
|
|||
thresholdComparator: 'between',
|
||||
threshold: [4, 5],
|
||||
});
|
||||
const context = addMessages(base, params);
|
||||
const alertInfo = {
|
||||
name: '[alert-name]',
|
||||
};
|
||||
const context = addMessages(alertInfo, base, params);
|
||||
expect(context.subject).toMatchInlineSnapshot(
|
||||
`"alert [name] group [group] exceeded threshold"`
|
||||
`"alert [alert-name] group [group] exceeded threshold"`
|
||||
);
|
||||
expect(context.message).toMatchInlineSnapshot(
|
||||
`"alert [name] group [group] value 4 exceeded threshold count between 4,5 over 5m on 2020-01-01T00:00:00.000Z"`
|
||||
`"alert [alert-name] group [group] value 4 exceeded threshold count between 4,5 over 5m on 2020-01-01T00:00:00.000Z"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Params } from './alert_type_params';
|
||||
import { AlertExecutorOptions } from '../../../../alerting/server';
|
||||
|
||||
// alert type context provided to actions
|
||||
|
||||
type AlertInfo = Pick<AlertExecutorOptions, 'name'>;
|
||||
|
||||
export interface ActionContext extends BaseActionContext {
|
||||
// a short generic message which may be used in an action message
|
||||
subject: string;
|
||||
|
@ -17,14 +20,6 @@ export interface ActionContext extends BaseActionContext {
|
|||
}
|
||||
|
||||
export interface BaseActionContext {
|
||||
// the alert name
|
||||
name: string;
|
||||
// the spaceId of the alert
|
||||
spaceId: string;
|
||||
// the namespace of the alert (spaceId === (namespace || 'default')
|
||||
namespace?: string;
|
||||
// the alert tags
|
||||
tags?: string[];
|
||||
// the aggType used in the alert
|
||||
// the value of the aggField, if used, otherwise 'all documents'
|
||||
group: string;
|
||||
|
@ -34,37 +29,41 @@ export interface BaseActionContext {
|
|||
value: number;
|
||||
}
|
||||
|
||||
export function addMessages(c: BaseActionContext, p: Params): ActionContext {
|
||||
export function addMessages(
|
||||
alertInfo: AlertInfo,
|
||||
baseContext: BaseActionContext,
|
||||
params: Params
|
||||
): ActionContext {
|
||||
const subject = i18n.translate(
|
||||
'xpack.alertingBuiltins.indexThreshold.alertTypeContextSubjectTitle',
|
||||
{
|
||||
defaultMessage: 'alert {name} group {group} exceeded threshold',
|
||||
values: {
|
||||
name: c.name,
|
||||
group: c.group,
|
||||
name: alertInfo.name,
|
||||
group: baseContext.group,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const agg = p.aggField ? `${p.aggType}(${p.aggField})` : `${p.aggType}`;
|
||||
const humanFn = `${agg} ${p.thresholdComparator} ${p.threshold.join(',')}`;
|
||||
const agg = params.aggField ? `${params.aggType}(${params.aggField})` : `${params.aggType}`;
|
||||
const humanFn = `${agg} ${params.thresholdComparator} ${params.threshold.join(',')}`;
|
||||
|
||||
const window = `${p.timeWindowSize}${p.timeWindowUnit}`;
|
||||
const window = `${params.timeWindowSize}${params.timeWindowUnit}`;
|
||||
const message = i18n.translate(
|
||||
'xpack.alertingBuiltins.indexThreshold.alertTypeContextMessageDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'alert {name} group {group} value {value} exceeded threshold {function} over {window} on {date}',
|
||||
values: {
|
||||
name: c.name,
|
||||
group: c.group,
|
||||
value: c.value,
|
||||
name: alertInfo.name,
|
||||
group: baseContext.group,
|
||||
value: baseContext.value,
|
||||
function: humanFn,
|
||||
window,
|
||||
date: c.date,
|
||||
date: baseContext.date,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return { ...c, subject, message };
|
||||
return { ...baseContext, subject, message };
|
||||
}
|
||||
|
|
|
@ -85,15 +85,11 @@ export function getAlertType(service: Service): AlertType {
|
|||
if (!met) continue;
|
||||
|
||||
const baseContext: BaseActionContext = {
|
||||
name,
|
||||
spaceId: options.spaceId,
|
||||
namespace: options.namespace,
|
||||
tags: options.tags,
|
||||
date,
|
||||
group: instanceId,
|
||||
value,
|
||||
};
|
||||
const actionContext = addMessages(baseContext, params);
|
||||
const actionContext = addMessages(options, baseContext, params);
|
||||
const alertInstance = options.services.alertInstanceFactory(instanceId);
|
||||
alertInstance.scheduleActions(ActionGroupId, actionContext);
|
||||
logger.debug(`scheduled actionGroup: ${JSON.stringify(actionContext)}`);
|
||||
|
|
|
@ -264,12 +264,21 @@ export class AlertUtils {
|
|||
}
|
||||
|
||||
function getDefaultAlwaysFiringAlertData(reference: string, actionId: string) {
|
||||
const messageTemplate = `
|
||||
alertId: {{alertId}},
|
||||
alertName: {{alertName}},
|
||||
spaceId: {{spaceId}},
|
||||
tags: {{tags}},
|
||||
alertInstanceId: {{alertInstanceId}},
|
||||
instanceContextValue: {{context.instanceContextValue}},
|
||||
instanceStateValue: {{state.instanceStateValue}}
|
||||
`.trim();
|
||||
return {
|
||||
enabled: true,
|
||||
name: 'abc',
|
||||
schedule: { interval: '1m' },
|
||||
throttle: '1m',
|
||||
tags: [],
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
alertTypeId: 'test.always-firing',
|
||||
consumer: 'bar',
|
||||
params: {
|
||||
|
@ -283,8 +292,7 @@ function getDefaultAlwaysFiringAlertData(reference: string, actionId: string) {
|
|||
params: {
|
||||
index: ES_TEST_INDEX_NAME,
|
||||
reference,
|
||||
message:
|
||||
'instanceContextValue: {{context.instanceContextValue}}, instanceStateValue: {{state.instanceStateValue}}',
|
||||
message: messageTemplate,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -120,7 +120,7 @@ export default function alertTests({ getService }: FtrProviderContext) {
|
|||
spaceId: space.id,
|
||||
namespace: space.id,
|
||||
name: 'abc',
|
||||
tags: [],
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
createdBy: user.fullName,
|
||||
updatedBy: user.fullName,
|
||||
},
|
||||
|
@ -142,7 +142,15 @@ export default function alertTests({ getService }: FtrProviderContext) {
|
|||
params: {
|
||||
index: ES_TEST_INDEX_NAME,
|
||||
reference,
|
||||
message: 'instanceContextValue: true, instanceStateValue: true',
|
||||
message: `
|
||||
alertId: ${alertId},
|
||||
alertName: abc,
|
||||
spaceId: ${space.id},
|
||||
tags: tag-A,tag-B,
|
||||
alertInstanceId: 1,
|
||||
instanceContextValue: true,
|
||||
instanceStateValue: true
|
||||
`.trim(),
|
||||
},
|
||||
reference,
|
||||
source: 'action:test.index-record',
|
||||
|
|
|
@ -97,7 +97,7 @@ export function alertTests({ getService }: FtrProviderContext, space: Space) {
|
|||
spaceId: space.id,
|
||||
namespace: space.namespace,
|
||||
name: 'abc',
|
||||
tags: [],
|
||||
tags: ['tag-A', 'tag-B'],
|
||||
createdBy: null,
|
||||
updatedBy: null,
|
||||
},
|
||||
|
@ -119,7 +119,15 @@ export function alertTests({ getService }: FtrProviderContext, space: Space) {
|
|||
params: {
|
||||
index: ES_TEST_INDEX_NAME,
|
||||
reference,
|
||||
message: 'instanceContextValue: true, instanceStateValue: true',
|
||||
message: `
|
||||
alertId: ${alertId},
|
||||
alertName: abc,
|
||||
spaceId: ${space.id},
|
||||
tags: tag-A,tag-B,
|
||||
alertInstanceId: 1,
|
||||
instanceContextValue: true,
|
||||
instanceStateValue: true
|
||||
`.trim(),
|
||||
},
|
||||
reference,
|
||||
source: 'action:test.index-record',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue