[Alerting] Rename alert instance to alert and changing signature of alert (instance) factory alert creation (#124390)

* Rename alert instance to alert and add create fn to alert factory

* Rename alert instance to alert and add create fn to alert factory

* Fixing types

* Fixing types

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Ying Mao 2022-02-07 16:38:24 -05:00 committed by GitHub
parent aedbc9f4c9
commit 270adf4958
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
82 changed files with 997 additions and 1065 deletions

View file

@ -65,8 +65,8 @@ export const alertType: RuleType<
range(instances) range(instances)
.map(() => uuid.v4()) .map(() => uuid.v4())
.forEach((id: string) => { .forEach((id: string) => {
services services.alertFactory
.alertInstanceFactory(id) .create(id)
.replaceState({ triggerdOnCycle: count }) .replaceState({ triggerdOnCycle: count })
.scheduleActions(getTShirtSizeByIdAndThreshold(id, thresholds)); .scheduleActions(getTShirtSizeByIdAndThreshold(id, thresholds));
}); });

View file

@ -70,7 +70,7 @@ export const alertType: RuleType<
if (getOperator(op)(peopleInCraft.length, outerSpaceCapacity)) { if (getOperator(op)(peopleInCraft.length, outerSpaceCapacity)) {
peopleInCraft.forEach(({ craft, name }) => { peopleInCraft.forEach(({ craft, name }) => {
services.alertInstanceFactory(name).replaceState({ craft }).scheduleActions('default'); services.alertFactory.create(name).replaceState({ craft }).scheduleActions('default');
}); });
} }

View file

@ -40,8 +40,6 @@ Table of Contents
> References to `rule` and `rule type` entities are still named `AlertType` within the codebase. > References to `rule` and `rule type` entities are still named `AlertType` within the codebase.
> References to `alert` and `alert factory` entities are still named `AlertInstance` and `alertInstanceFactory` within the codebase.
**Rule Type**: A function that takes parameters and executes actions on alerts. **Rule Type**: A function that takes parameters and executes actions on alerts.
**Rule**: A configuration that defines a schedule, a rule type w/ parameters, state information and actions. **Rule**: A configuration that defines a schedule, a rule type w/ parameters, state information and actions.
@ -113,7 +111,7 @@ This is the primary function for a rule type. Whenever the rule needs to execute
|---|---| |---|---|
|services.scopedClusterClient|This is an instance of the Elasticsearch client. Use this to do Elasticsearch queries in the context of the user who created the alert when security is enabled.| |services.scopedClusterClient|This is an instance of the Elasticsearch client. Use this to do Elasticsearch queries in the context of the user who created the alert when security is enabled.|
|services.savedObjectsClient|This is an instance of the saved objects client. This provides the ability to perform CRUD operations on any saved object that lives in the same space as the rule.<br><br>The scope of the saved objects client is tied to the user who created the rule (only when security is enabled).| |services.savedObjectsClient|This is an instance of the saved objects client. This provides the ability to perform CRUD operations on any saved object that lives in the same space as the rule.<br><br>The scope of the saved objects client is tied to the user who created the rule (only when security is enabled).|
|services.alertInstanceFactory(id)|This [alert factory](#alert-factory) creates alerts and must be used in order to execute actions. The id you give to the alert factory is a unique identifier for the alert.| |services.alertFactory|This [alert factory](#alert-factory) creates alerts and must be used in order to schedule action execution. The id you give to the alert factory create function() is a unique identifier for the alert.|
|services.log(tags, [data], [timestamp])|Use this to create server logs. (This is the same function as server.log)| |services.log(tags, [data], [timestamp])|Use this to create server logs. (This is the same function as server.log)|
|services.shouldWriteAlerts()|This returns a boolean indicating whether the executor should write out alerts as data. This is determined by whether rule execution has been cancelled due to timeout AND whether both the Kibana `cancelAlertsOnRuleTimeout` flag and the rule type `cancelAlertsOnRuleTimeout` are set to `true`.| |services.shouldWriteAlerts()|This returns a boolean indicating whether the executor should write out alerts as data. This is determined by whether rule execution has been cancelled due to timeout AND whether both the Kibana `cancelAlertsOnRuleTimeout` flag and the rule type `cancelAlertsOnRuleTimeout` are set to `true`.|
|services.shouldStopExecution()|This returns a boolean indicating whether rule execution has been cancelled due to timeout.| |services.shouldStopExecution()|This returns a boolean indicating whether rule execution has been cancelled due to timeout.|
@ -310,7 +308,7 @@ const myRuleType: RuleType<
// scenario the provided server will be used. Also, this ID will be // scenario the provided server will be used. Also, this ID will be
// used to make `getState()` return previous state, if any, on // used to make `getState()` return previous state, if any, on
// matching identifiers. // matching identifiers.
const alert = services.alertInstanceFactory(server); const alert = services.alertFactory.create(server);
// State from the last execution. This will exist if an alert was // State from the last execution. This will exist if an alert was
// created and executed in the previous execution // created and executed in the previous execution
@ -731,13 +729,13 @@ Query:
## Alert Factory ## Alert Factory
**alertInstanceFactory(id)** **alertFactory.create(id)**
One service passed in to each rule type is the alert factory. This factory creates alerts and must be used in order to execute actions. The `id` you give to the alert factory is the unique identifier for the alert (e.g. the server identifier if the alert is about servers). The alert factory will use this identifier to retrieve the state of previous alerts with the same `id`. These alerts support persisting state between rule executions, but will clear out once the alert stops firing. One service passed in to each rule type is the alert factory. This factory creates alerts and must be used in order to schedule action execution. The `id` you give to the alert factory create fn() is the unique identifier for the alert (e.g. the server identifier if the alert is about servers). The alert factory will use this identifier to retrieve the state of previous alerts with the same `id`. These alerts support persisting state between rule executions, but will clear out once the alert stops firing.
Note that the `id` only needs to be unique **within the scope of a specific rule**, not unique across all rules or rule types. For example, Rule 1 and Rule 2 can both create an alert with an `id` of `"a"` without conflicting with one another. But if Rule 1 creates 2 alerts, then they must be differentiated with `id`s of `"a"` and `"b"`. Note that the `id` only needs to be unique **within the scope of a specific rule**, not unique across all rules or rule types. For example, Rule 1 and Rule 2 can both create an alert with an `id` of `"a"` without conflicting with one another. But if Rule 1 creates 2 alerts, then they must be differentiated with `id`s of `"a"` and `"b"`.
This factory returns an instance of `AlertInstance`. The `AlertInstance` class has the following methods. Note that we have removed the methods that you shouldn't touch. This factory returns an instance of `Alert`. The `Alert` class has the following methods. Note that we have removed the methods that you shouldn't touch.
|Method|Description| |Method|Description|
|---|---| |---|---|
@ -781,7 +779,8 @@ The templating engine is [mustache]. General definition for the [mustache variab
The following code would be within a rule type. As you can see `cpuUsage` will replace the state of the alert and `server` is the context for the alert to execute. The difference between the two is that `cpuUsage` will be accessible at the next execution. The following code would be within a rule type. As you can see `cpuUsage` will replace the state of the alert and `server` is the context for the alert to execute. The difference between the two is that `cpuUsage` will be accessible at the next execution.
``` ```
alertInstanceFactory('server_1') alertFactory
.create('server_1')
.replaceState({ .replaceState({
cpuUsage: 80, cpuUsage: 80,
}) })

View file

@ -0,0 +1,488 @@
/*
* 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 sinon from 'sinon';
import { Alert } from './alert';
import { AlertInstanceState, AlertInstanceContext, DefaultActionGroupId } from '../../common';
let clock: sinon.SinonFakeTimers;
beforeAll(() => {
clock = sinon.useFakeTimers();
});
beforeEach(() => clock.reset());
afterAll(() => clock.restore());
describe('hasScheduledActions()', () => {
test('defaults to false', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
expect(alert.hasScheduledActions()).toEqual(false);
});
test('returns true when scheduleActions is called', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
alert.scheduleActions('default');
expect(alert.hasScheduledActions()).toEqual(true);
});
});
describe('isThrottled', () => {
test(`should throttle when group didn't change and throttle period is still active`, () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(30000);
alert.scheduleActions('default');
expect(alert.isThrottled('1m')).toEqual(true);
});
test(`shouldn't throttle when group didn't change and throttle period expired`, () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(30000);
alert.scheduleActions('default');
expect(alert.isThrottled('15s')).toEqual(false);
});
test(`shouldn't throttle when group changes`, () => {
const alert = new Alert<never, never, 'default' | 'other-group'>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(5000);
alert.scheduleActions('other-group');
expect(alert.isThrottled('1m')).toEqual(false);
});
});
describe('scheduledActionGroupOrSubgroupHasChanged()', () => {
test('should be false if no last scheduled and nothing scheduled', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be false if group does not change', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alert.scheduleActions('default');
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be false if group and subgroup does not change', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alert.scheduleActionsWithSubGroup('default', 'subgroup');
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be false if group does not change and subgroup goes from undefined to defined', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alert.scheduleActionsWithSubGroup('default', 'subgroup');
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be false if group does not change and subgroup goes from defined to undefined', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alert.scheduleActions('default');
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be true if no last scheduled and has scheduled action', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
alert.scheduleActions('default');
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true);
});
test('should be true if group does change', () => {
const alert = new Alert<never, never, 'default' | 'penguin'>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alert.scheduleActions('penguin');
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true);
});
test('should be true if group does change and subgroup does change', () => {
const alert = new Alert<never, never, 'default' | 'penguin'>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alert.scheduleActionsWithSubGroup('penguin', 'fish');
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true);
});
test('should be true if group does not change and subgroup does change', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alert.scheduleActionsWithSubGroup('default', 'fish');
expect(alert.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true);
});
});
describe('getScheduledActionOptions()', () => {
test('defaults to undefined', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
expect(alert.getScheduledActionOptions()).toBeUndefined();
});
});
describe('unscheduleActions()', () => {
test('makes hasScheduledActions() return false', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
alert.scheduleActions('default');
expect(alert.hasScheduledActions()).toEqual(true);
alert.unscheduleActions();
expect(alert.hasScheduledActions()).toEqual(false);
});
test('makes getScheduledActionOptions() return undefined', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
alert.scheduleActions('default');
expect(alert.getScheduledActionOptions()).toEqual({
actionGroup: 'default',
context: {},
state: {},
});
alert.unscheduleActions();
expect(alert.getScheduledActionOptions()).toBeUndefined();
});
});
describe('getState()', () => {
test('returns state passed to constructor', () => {
const state = { foo: true };
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state,
});
expect(alert.getState()).toEqual(state);
});
});
describe('scheduleActions()', () => {
test('makes hasScheduledActions() return true', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alert.replaceState({ otherField: true }).scheduleActions('default', { field: true });
expect(alert.hasScheduledActions()).toEqual(true);
});
test('makes isThrottled() return true when throttled', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alert.replaceState({ otherField: true }).scheduleActions('default', { field: true });
expect(alert.isThrottled('1m')).toEqual(true);
});
test('make isThrottled() return false when throttled expired', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(120000);
alert.replaceState({ otherField: true }).scheduleActions('default', { field: true });
expect(alert.isThrottled('1m')).toEqual(false);
});
test('makes getScheduledActionOptions() return given options', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {},
});
alert.replaceState({ otherField: true }).scheduleActions('default', { field: true });
expect(alert.getScheduledActionOptions()).toEqual({
actionGroup: 'default',
context: { field: true },
state: { otherField: true },
});
});
test('cannot schdule for execution twice', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
alert.scheduleActions('default', { field: true });
expect(() =>
alert.scheduleActions('default', { field: false })
).toThrowErrorMatchingInlineSnapshot(
`"Alert instance execution has already been scheduled, cannot schedule twice"`
);
});
});
describe('scheduleActionsWithSubGroup()', () => {
test('makes hasScheduledActions() return true', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alert
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alert.hasScheduledActions()).toEqual(true);
});
test('makes isThrottled() return true when throttled and subgroup is the same', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alert
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alert.isThrottled('1m')).toEqual(true);
});
test('makes isThrottled() return true when throttled and last schedule had no subgroup', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alert
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alert.isThrottled('1m')).toEqual(true);
});
test('makes isThrottled() return false when throttled and subgroup is the different', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'prev-subgroup',
},
},
});
alert
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alert.isThrottled('1m')).toEqual(false);
});
test('make isThrottled() return false when throttled expired', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(120000);
alert
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alert.isThrottled('1m')).toEqual(false);
});
test('makes getScheduledActionOptions() return given options', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
meta: {},
});
alert
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alert.getScheduledActionOptions()).toEqual({
actionGroup: 'default',
subgroup: 'subgroup',
context: { field: true },
state: { otherField: true },
});
});
test('cannot schdule for execution twice', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(() =>
alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: false })
).toThrowErrorMatchingInlineSnapshot(
`"Alert instance execution has already been scheduled, cannot schedule twice"`
);
});
test('cannot schdule for execution twice with different subgroups', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(() =>
alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: false })
).toThrowErrorMatchingInlineSnapshot(
`"Alert instance execution has already been scheduled, cannot schedule twice"`
);
});
test('cannot schdule for execution twice whether there are subgroups', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>();
alert.scheduleActions('default', { field: true });
expect(() =>
alert.scheduleActionsWithSubGroup('default', 'subgroup', { field: false })
).toThrowErrorMatchingInlineSnapshot(
`"Alert instance execution has already been scheduled, cannot schedule twice"`
);
});
});
describe('replaceState()', () => {
test('replaces previous state', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
state: { foo: true },
});
alert.replaceState({ bar: true });
expect(alert.getState()).toEqual({ bar: true });
alert.replaceState({ baz: true });
expect(alert.getState()).toEqual({ baz: true });
});
});
describe('updateLastScheduledActions()', () => {
test('replaces previous lastScheduledActions', () => {
const alert = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>({
meta: {},
});
alert.updateLastScheduledActions('default');
expect(alert.toJSON()).toEqual({
state: {},
meta: {
lastScheduledActions: {
date: new Date().toISOString(),
group: 'default',
},
},
});
});
});
describe('toJSON', () => {
test('only serializes state and meta', () => {
const alertInstance = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>(
{
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
}
);
expect(JSON.stringify(alertInstance)).toEqual(
'{"state":{"foo":true},"meta":{"lastScheduledActions":{"date":"1970-01-01T00:00:00.000Z","group":"default"}}}'
);
});
});
describe('toRaw', () => {
test('returns unserialised underlying state and meta', () => {
const raw = {
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
};
const alertInstance = new Alert<AlertInstanceState, AlertInstanceContext, DefaultActionGroupId>(
raw
);
expect(alertInstance.toRaw()).toEqual(raw);
});
});

View file

@ -27,16 +27,16 @@ interface ScheduledExecutionOptions<
state: State; state: State;
} }
export type PublicAlertInstance< export type PublicAlert<
State extends AlertInstanceState = AlertInstanceState, State extends AlertInstanceState = AlertInstanceState,
Context extends AlertInstanceContext = AlertInstanceContext, Context extends AlertInstanceContext = AlertInstanceContext,
ActionGroupIds extends string = DefaultActionGroupId ActionGroupIds extends string = DefaultActionGroupId
> = Pick< > = Pick<
AlertInstance<State, Context, ActionGroupIds>, Alert<State, Context, ActionGroupIds>,
'getState' | 'replaceState' | 'scheduleActions' | 'scheduleActionsWithSubGroup' 'getState' | 'replaceState' | 'scheduleActions' | 'scheduleActionsWithSubGroup'
>; >;
export class AlertInstance< export class Alert<
State extends AlertInstanceState = AlertInstanceState, State extends AlertInstanceState = AlertInstanceState,
Context extends AlertInstanceContext = AlertInstanceContext, Context extends AlertInstanceContext = AlertInstanceContext,
ActionGroupIds extends string = never ActionGroupIds extends string = never

View file

@ -6,8 +6,8 @@
*/ */
import sinon from 'sinon'; import sinon from 'sinon';
import { AlertInstance } from './alert_instance'; import { Alert } from './alert';
import { createAlertInstanceFactory } from './create_alert_instance_factory'; import { createAlertFactory } from './create_alert_factory';
let clock: sinon.SinonFakeTimers; let clock: sinon.SinonFakeTimers;
@ -17,9 +17,9 @@ beforeAll(() => {
beforeEach(() => clock.reset()); beforeEach(() => clock.reset());
afterAll(() => clock.restore()); afterAll(() => clock.restore());
test('creates new instances for ones not passed in', () => { test('creates new alerts for ones not passed in', () => {
const alertInstanceFactory = createAlertInstanceFactory({}); const alertFactory = createAlertFactory({ alerts: {} });
const result = alertInstanceFactory('1'); const result = alertFactory.create('1');
expect(result).toMatchInlineSnapshot(` expect(result).toMatchInlineSnapshot(`
Object { Object {
"meta": Object {}, "meta": Object {},
@ -28,15 +28,17 @@ test('creates new instances for ones not passed in', () => {
`); `);
}); });
test('reuses existing instances', () => { test('reuses existing alerts', () => {
const alertInstance = new AlertInstance({ const alert = new Alert({
state: { foo: true }, state: { foo: true },
meta: { lastScheduledActions: { group: 'default', date: new Date() } }, meta: { lastScheduledActions: { group: 'default', date: new Date() } },
}); });
const alertInstanceFactory = createAlertInstanceFactory({ const alertFactory = createAlertFactory({
'1': alertInstance, alerts: {
'1': alert,
},
}); });
const result = alertInstanceFactory('1'); const result = alertFactory.create('1');
expect(result).toMatchInlineSnapshot(` expect(result).toMatchInlineSnapshot(`
Object { Object {
"meta": Object { "meta": Object {
@ -52,11 +54,11 @@ test('reuses existing instances', () => {
`); `);
}); });
test('mutates given instances', () => { test('mutates given alerts', () => {
const alertInstances = {}; const alerts = {};
const alertInstanceFactory = createAlertInstanceFactory(alertInstances); const alertFactory = createAlertFactory({ alerts });
alertInstanceFactory('1'); alertFactory.create('1');
expect(alertInstances).toMatchInlineSnapshot(` expect(alerts).toMatchInlineSnapshot(`
Object { Object {
"1": Object { "1": Object {
"meta": Object {}, "meta": Object {},

View file

@ -0,0 +1,33 @@
/*
* 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 { AlertInstanceContext, AlertInstanceState } from '../types';
import { Alert } from './alert';
export interface CreateAlertFactoryOpts<
InstanceState extends AlertInstanceState,
InstanceContext extends AlertInstanceContext,
ActionGroupIds extends string
> {
alerts: Record<string, Alert<InstanceState, InstanceContext, ActionGroupIds>>;
}
export function createAlertFactory<
InstanceState extends AlertInstanceState,
InstanceContext extends AlertInstanceContext,
ActionGroupIds extends string
>({ alerts }: CreateAlertFactoryOpts<InstanceState, InstanceContext, ActionGroupIds>) {
return {
create: (id: string): Alert<InstanceState, InstanceContext, ActionGroupIds> => {
if (!alerts[id]) {
alerts[id] = new Alert<InstanceState, InstanceContext, ActionGroupIds>();
}
return alerts[id];
},
};
}

View file

@ -5,6 +5,6 @@
* 2.0. * 2.0.
*/ */
export type { PublicAlertInstance } from './alert_instance'; export type { PublicAlert } from './alert';
export { AlertInstance } from './alert_instance'; export { Alert } from './alert';
export { createAlertInstanceFactory } from './create_alert_instance_factory'; export { createAlertFactory } from './create_alert_factory';

View file

@ -1,604 +0,0 @@
/*
* 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 sinon from 'sinon';
import { AlertInstance } from './alert_instance';
import { AlertInstanceState, AlertInstanceContext, DefaultActionGroupId } from '../../common';
let clock: sinon.SinonFakeTimers;
beforeAll(() => {
clock = sinon.useFakeTimers();
});
beforeEach(() => clock.reset());
afterAll(() => clock.restore());
describe('hasScheduledActions()', () => {
test('defaults to false', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
expect(alertInstance.hasScheduledActions()).toEqual(false);
});
test('returns true when scheduleActions is called', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
alertInstance.scheduleActions('default');
expect(alertInstance.hasScheduledActions()).toEqual(true);
});
});
describe('isThrottled', () => {
test(`should throttle when group didn't change and throttle period is still active`, () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(30000);
alertInstance.scheduleActions('default');
expect(alertInstance.isThrottled('1m')).toEqual(true);
});
test(`shouldn't throttle when group didn't change and throttle period expired`, () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(30000);
alertInstance.scheduleActions('default');
expect(alertInstance.isThrottled('15s')).toEqual(false);
});
test(`shouldn't throttle when group changes`, () => {
const alertInstance = new AlertInstance<never, never, 'default' | 'other-group'>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(5000);
alertInstance.scheduleActions('other-group');
expect(alertInstance.isThrottled('1m')).toEqual(false);
});
});
describe('scheduledActionGroupOrSubgroupHasChanged()', () => {
test('should be false if no last scheduled and nothing scheduled', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be false if group does not change', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alertInstance.scheduleActions('default');
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be false if group and subgroup does not change', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alertInstance.scheduleActionsWithSubGroup('default', 'subgroup');
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be false if group does not change and subgroup goes from undefined to defined', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alertInstance.scheduleActionsWithSubGroup('default', 'subgroup');
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be false if group does not change and subgroup goes from defined to undefined', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alertInstance.scheduleActions('default');
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(false);
});
test('should be true if no last scheduled and has scheduled action', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
alertInstance.scheduleActions('default');
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true);
});
test('should be true if group does change', () => {
const alertInstance = new AlertInstance<never, never, 'default' | 'penguin'>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alertInstance.scheduleActions('penguin');
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true);
});
test('should be true if group does change and subgroup does change', () => {
const alertInstance = new AlertInstance<never, never, 'default' | 'penguin'>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alertInstance.scheduleActionsWithSubGroup('penguin', 'fish');
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true);
});
test('should be true if group does not change and subgroup does change', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alertInstance.scheduleActionsWithSubGroup('default', 'fish');
expect(alertInstance.scheduledActionGroupOrSubgroupHasChanged()).toEqual(true);
});
});
describe('getScheduledActionOptions()', () => {
test('defaults to undefined', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
expect(alertInstance.getScheduledActionOptions()).toBeUndefined();
});
});
describe('unscheduleActions()', () => {
test('makes hasScheduledActions() return false', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
alertInstance.scheduleActions('default');
expect(alertInstance.hasScheduledActions()).toEqual(true);
alertInstance.unscheduleActions();
expect(alertInstance.hasScheduledActions()).toEqual(false);
});
test('makes getScheduledActionOptions() return undefined', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
alertInstance.scheduleActions('default');
expect(alertInstance.getScheduledActionOptions()).toEqual({
actionGroup: 'default',
context: {},
state: {},
});
alertInstance.unscheduleActions();
expect(alertInstance.getScheduledActionOptions()).toBeUndefined();
});
});
describe('getState()', () => {
test('returns state passed to constructor', () => {
const state = { foo: true };
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({ state });
expect(alertInstance.getState()).toEqual(state);
});
});
describe('scheduleActions()', () => {
test('makes hasScheduledActions() return true', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alertInstance.replaceState({ otherField: true }).scheduleActions('default', { field: true });
expect(alertInstance.hasScheduledActions()).toEqual(true);
});
test('makes isThrottled() return true when throttled', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alertInstance.replaceState({ otherField: true }).scheduleActions('default', { field: true });
expect(alertInstance.isThrottled('1m')).toEqual(true);
});
test('make isThrottled() return false when throttled expired', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(120000);
alertInstance.replaceState({ otherField: true }).scheduleActions('default', { field: true });
expect(alertInstance.isThrottled('1m')).toEqual(false);
});
test('makes getScheduledActionOptions() return given options', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({ state: { foo: true }, meta: {} });
alertInstance.replaceState({ otherField: true }).scheduleActions('default', { field: true });
expect(alertInstance.getScheduledActionOptions()).toEqual({
actionGroup: 'default',
context: { field: true },
state: { otherField: true },
});
});
test('cannot schdule for execution twice', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
alertInstance.scheduleActions('default', { field: true });
expect(() =>
alertInstance.scheduleActions('default', { field: false })
).toThrowErrorMatchingInlineSnapshot(
`"Alert instance execution has already been scheduled, cannot schedule twice"`
);
});
});
describe('scheduleActionsWithSubGroup()', () => {
test('makes hasScheduledActions() return true', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alertInstance
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alertInstance.hasScheduledActions()).toEqual(true);
});
test('makes isThrottled() return true when throttled and subgroup is the same', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'subgroup',
},
},
});
alertInstance
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alertInstance.isThrottled('1m')).toEqual(true);
});
test('makes isThrottled() return true when throttled and last schedule had no subgroup', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
alertInstance
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alertInstance.isThrottled('1m')).toEqual(true);
});
test('makes isThrottled() return false when throttled and subgroup is the different', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
subgroup: 'prev-subgroup',
},
},
});
alertInstance
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alertInstance.isThrottled('1m')).toEqual(false);
});
test('make isThrottled() return false when throttled expired', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
clock.tick(120000);
alertInstance
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alertInstance.isThrottled('1m')).toEqual(false);
});
test('makes getScheduledActionOptions() return given options', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({ state: { foo: true }, meta: {} });
alertInstance
.replaceState({ otherField: true })
.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(alertInstance.getScheduledActionOptions()).toEqual({
actionGroup: 'default',
subgroup: 'subgroup',
context: { field: true },
state: { otherField: true },
});
});
test('cannot schdule for execution twice', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(() =>
alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: false })
).toThrowErrorMatchingInlineSnapshot(
`"Alert instance execution has already been scheduled, cannot schedule twice"`
);
});
test('cannot schdule for execution twice with different subgroups', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: true });
expect(() =>
alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: false })
).toThrowErrorMatchingInlineSnapshot(
`"Alert instance execution has already been scheduled, cannot schedule twice"`
);
});
test('cannot schdule for execution twice whether there are subgroups', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>();
alertInstance.scheduleActions('default', { field: true });
expect(() =>
alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: false })
).toThrowErrorMatchingInlineSnapshot(
`"Alert instance execution has already been scheduled, cannot schedule twice"`
);
});
});
describe('replaceState()', () => {
test('replaces previous state', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({ state: { foo: true } });
alertInstance.replaceState({ bar: true });
expect(alertInstance.getState()).toEqual({ bar: true });
alertInstance.replaceState({ baz: true });
expect(alertInstance.getState()).toEqual({ baz: true });
});
});
describe('updateLastScheduledActions()', () => {
test('replaces previous lastScheduledActions', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({ meta: {} });
alertInstance.updateLastScheduledActions('default');
expect(alertInstance.toJSON()).toEqual({
state: {},
meta: {
lastScheduledActions: {
date: new Date().toISOString(),
group: 'default',
},
},
});
});
});
describe('toJSON', () => {
test('only serializes state and meta', () => {
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>({
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
});
expect(JSON.stringify(alertInstance)).toEqual(
'{"state":{"foo":true},"meta":{"lastScheduledActions":{"date":"1970-01-01T00:00:00.000Z","group":"default"}}}'
);
});
});
describe('toRaw', () => {
test('returns unserialised underlying state and meta', () => {
const raw = {
state: { foo: true },
meta: {
lastScheduledActions: {
date: new Date(),
group: 'default',
},
},
};
const alertInstance = new AlertInstance<
AlertInstanceState,
AlertInstanceContext,
DefaultActionGroupId
>(raw);
expect(alertInstance.toRaw()).toEqual(raw);
});
});

View file

@ -1,23 +0,0 @@
/*
* 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 { AlertInstanceContext, AlertInstanceState } from '../types';
import { AlertInstance } from './alert_instance';
export function createAlertInstanceFactory<
InstanceState extends AlertInstanceState,
InstanceContext extends AlertInstanceContext,
ActionGroupIds extends string
>(alertInstances: Record<string, AlertInstance<InstanceState, InstanceContext, ActionGroupIds>>) {
return (id: string): AlertInstance<InstanceState, InstanceContext, ActionGroupIds> => {
if (!alertInstances[id]) {
alertInstances[id] = new AlertInstance<InstanceState, InstanceContext, ActionGroupIds>();
}
return alertInstances[id];
};
}

View file

@ -32,7 +32,7 @@ export type {
export { DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT } from './config'; export { DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT } from './config';
export type { PluginSetupContract, PluginStartContract } from './plugin'; export type { PluginSetupContract, PluginStartContract } from './plugin';
export type { FindResult } from './rules_client'; export type { FindResult } from './rules_client';
export type { PublicAlertInstance as AlertInstance } from './alert_instance'; export type { PublicAlert as Alert } from './alert';
export { parseDuration } from './lib'; export { parseDuration } from './lib';
export { getEsErrorMessage } from './lib/errors'; export { getEsErrorMessage } from './lib/errors';
export type { export type {

View file

@ -7,7 +7,7 @@
import { rulesClientMock } from './rules_client.mock'; import { rulesClientMock } from './rules_client.mock';
import { PluginSetupContract, PluginStartContract } from './plugin'; import { PluginSetupContract, PluginStartContract } from './plugin';
import { AlertInstance } from './alert_instance'; import { Alert } from './alert';
import { import {
elasticsearchServiceMock, elasticsearchServiceMock,
savedObjectsClientMock, savedObjectsClientMock,
@ -37,11 +37,13 @@ const createStartMock = () => {
export type AlertInstanceMock< export type AlertInstanceMock<
State extends AlertInstanceState = AlertInstanceState, State extends AlertInstanceState = AlertInstanceState,
Context extends AlertInstanceContext = AlertInstanceContext Context extends AlertInstanceContext = AlertInstanceContext
> = jest.Mocked<AlertInstance<State, Context>>; > = jest.Mocked<Alert<State, Context>>;
const createAlertInstanceFactoryMock = <
const createAlertFactoryMock = {
create: <
InstanceState extends AlertInstanceState = AlertInstanceState, InstanceState extends AlertInstanceState = AlertInstanceState,
InstanceContext extends AlertInstanceContext = AlertInstanceContext InstanceContext extends AlertInstanceContext = AlertInstanceContext
>() => { >() => {
const mock = { const mock = {
hasScheduledActions: jest.fn(), hasScheduledActions: jest.fn(),
isThrottled: jest.fn(), isThrottled: jest.fn(),
@ -61,6 +63,7 @@ const createAlertInstanceFactoryMock = <
mock.scheduleActions.mockReturnValue(mock); mock.scheduleActions.mockReturnValue(mock);
return mock as unknown as AlertInstanceMock<InstanceState, InstanceContext>; return mock as unknown as AlertInstanceMock<InstanceState, InstanceContext>;
},
}; };
const createAbortableSearchClientMock = () => { const createAbortableSearchClientMock = () => {
@ -82,11 +85,11 @@ const createAlertServicesMock = <
InstanceState extends AlertInstanceState = AlertInstanceState, InstanceState extends AlertInstanceState = AlertInstanceState,
InstanceContext extends AlertInstanceContext = AlertInstanceContext InstanceContext extends AlertInstanceContext = AlertInstanceContext
>() => { >() => {
const alertInstanceFactoryMock = createAlertInstanceFactoryMock<InstanceState, InstanceContext>(); const alertFactoryMockCreate = createAlertFactoryMock.create<InstanceState, InstanceContext>();
return { return {
alertInstanceFactory: jest alertFactory: {
.fn<jest.Mocked<AlertInstance<InstanceState, InstanceContext>>, [string]>() create: jest.fn().mockReturnValue(alertFactoryMockCreate),
.mockReturnValue(alertInstanceFactoryMock), },
savedObjectsClient: savedObjectsClientMock.create(), savedObjectsClient: savedObjectsClientMock.create(),
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
shouldWriteAlerts: () => true, shouldWriteAlerts: () => true,
@ -97,7 +100,7 @@ const createAlertServicesMock = <
export type AlertServicesMock = ReturnType<typeof createAlertServicesMock>; export type AlertServicesMock = ReturnType<typeof createAlertServicesMock>;
export const alertsMock = { export const alertsMock = {
createAlertInstanceFactory: createAlertInstanceFactoryMock, createAlertFactory: createAlertFactoryMock,
createSetup: createSetupMock, createSetup: createSetupMock,
createStart: createStartMock, createStart: createStartMock,
createAlertServices: createAlertServicesMock, createAlertServices: createAlertServicesMock,

View file

@ -22,23 +22,23 @@ import {
import { esKuery } from '../../../../../src/plugins/data/server'; import { esKuery } from '../../../../../src/plugins/data/server';
import { ActionsClient, ActionsAuthorization } from '../../../actions/server'; import { ActionsClient, ActionsAuthorization } from '../../../actions/server';
import { import {
Alert, Alert as Rule,
PartialAlert, PartialAlert as PartialRule,
RawRule, RawRule,
RuleTypeRegistry, RuleTypeRegistry,
AlertAction, AlertAction as RuleAction,
IntervalSchedule, IntervalSchedule,
SanitizedAlert, SanitizedAlert as SanitizedRule,
RuleTaskState, RuleTaskState,
AlertSummary, AlertSummary,
AlertExecutionStatusValues, AlertExecutionStatusValues as RuleExecutionStatusValues,
AlertNotifyWhenType, AlertNotifyWhenType as RuleNotifyWhenType,
AlertTypeParams, AlertTypeParams as RuleTypeParams,
ResolvedSanitizedRule, ResolvedSanitizedRule,
AlertWithLegacyId, AlertWithLegacyId as RuleWithLegacyId,
SanitizedRuleWithLegacyId, SanitizedRuleWithLegacyId,
PartialAlertWithLegacyId, PartialAlertWithLegacyId as PartialRuleWithLegacyId,
RawAlertInstance, RawAlertInstance as RawAlert,
} from '../types'; } from '../types';
import { validateRuleTypeParams, ruleExecutionStatusFromRaw, getAlertNotifyWhenType } from '../lib'; import { validateRuleTypeParams, ruleExecutionStatusFromRaw, getAlertNotifyWhenType } from '../lib';
import { import {
@ -74,7 +74,7 @@ import { ruleAuditEvent, RuleAuditAction } from './audit_events';
import { KueryNode, nodeBuilder } from '../../../../../src/plugins/data/common'; import { KueryNode, nodeBuilder } from '../../../../../src/plugins/data/common';
import { mapSortField, validateOperationOnAttributes } from './lib'; import { mapSortField, validateOperationOnAttributes } from './lib';
import { getRuleExecutionStatusPending } from '../lib/rule_execution_status'; import { getRuleExecutionStatusPending } from '../lib/rule_execution_status';
import { AlertInstance } from '../alert_instance'; import { Alert } from '../alert';
import { EVENT_LOG_ACTIONS } from '../plugin'; import { EVENT_LOG_ACTIONS } from '../plugin';
import { createAlertEventLogRecordObject } from '../lib/create_alert_event_log_record_object'; import { createAlertEventLogRecordObject } from '../lib/create_alert_event_log_record_object';
import { getDefaultRuleMonitoring } from '../task_runner/task_runner'; import { getDefaultRuleMonitoring } from '../task_runner/task_runner';
@ -82,7 +82,7 @@ import { getDefaultRuleMonitoring } from '../task_runner/task_runner';
export interface RegistryAlertTypeWithAuth extends RegistryRuleType { export interface RegistryAlertTypeWithAuth extends RegistryRuleType {
authorizedConsumers: string[]; authorizedConsumers: string[];
} }
type NormalizedAlertAction = Omit<AlertAction, 'actionTypeId'>; type NormalizedAlertAction = Omit<RuleAction, 'actionTypeId'>;
export type CreateAPIKeyResult = export type CreateAPIKeyResult =
| { apiKeysEnabled: false } | { apiKeysEnabled: false }
| { apiKeysEnabled: true; result: SecurityPluginGrantAPIKeyResult }; | { apiKeysEnabled: true; result: SecurityPluginGrantAPIKeyResult };
@ -174,16 +174,16 @@ export interface AggregateResult {
ruleMutedStatus?: { muted: number; unmuted: number }; ruleMutedStatus?: { muted: number; unmuted: number };
} }
export interface FindResult<Params extends AlertTypeParams> { export interface FindResult<Params extends RuleTypeParams> {
page: number; page: number;
perPage: number; perPage: number;
total: number; total: number;
data: Array<SanitizedAlert<Params>>; data: Array<SanitizedRule<Params>>;
} }
export interface CreateOptions<Params extends AlertTypeParams> { export interface CreateOptions<Params extends RuleTypeParams> {
data: Omit< data: Omit<
Alert<Params>, Rule<Params>,
| 'id' | 'id'
| 'createdBy' | 'createdBy'
| 'updatedBy' | 'updatedBy'
@ -202,7 +202,7 @@ export interface CreateOptions<Params extends AlertTypeParams> {
}; };
} }
export interface UpdateOptions<Params extends AlertTypeParams> { export interface UpdateOptions<Params extends RuleTypeParams> {
id: string; id: string;
data: { data: {
name: string; name: string;
@ -211,7 +211,7 @@ export interface UpdateOptions<Params extends AlertTypeParams> {
actions: NormalizedAlertAction[]; actions: NormalizedAlertAction[];
params: Params; params: Params;
throttle: string | null; throttle: string | null;
notifyWhen: AlertNotifyWhenType | null; notifyWhen: RuleNotifyWhenType | null;
}; };
} }
@ -248,7 +248,7 @@ export class RulesClient {
private readonly kibanaVersion!: PluginInitializerContext['env']['packageInfo']['version']; private readonly kibanaVersion!: PluginInitializerContext['env']['packageInfo']['version'];
private readonly auditLogger?: AuditLogger; private readonly auditLogger?: AuditLogger;
private readonly eventLogger?: IEventLogger; private readonly eventLogger?: IEventLogger;
private readonly fieldsToExcludeFromPublicApi: Array<keyof SanitizedAlert> = ['monitoring']; private readonly fieldsToExcludeFromPublicApi: Array<keyof SanitizedRule> = ['monitoring'];
constructor({ constructor({
ruleTypeRegistry, ruleTypeRegistry,
@ -286,10 +286,10 @@ export class RulesClient {
this.eventLogger = eventLogger; this.eventLogger = eventLogger;
} }
public async create<Params extends AlertTypeParams = never>({ public async create<Params extends RuleTypeParams = never>({
data, data,
options, options,
}: CreateOptions<Params>): Promise<SanitizedAlert<Params>> { }: CreateOptions<Params>): Promise<SanitizedRule<Params>> {
const id = options?.id || SavedObjectsUtils.generateId(); const id = options?.id || SavedObjectsUtils.generateId();
try { try {
@ -432,7 +432,7 @@ export class RulesClient {
); );
} }
public async get<Params extends AlertTypeParams = never>({ public async get<Params extends RuleTypeParams = never>({
id, id,
includeLegacyId = false, includeLegacyId = false,
excludeFromPublicApi = false, excludeFromPublicApi = false,
@ -440,7 +440,7 @@ export class RulesClient {
id: string; id: string;
includeLegacyId?: boolean; includeLegacyId?: boolean;
excludeFromPublicApi?: boolean; excludeFromPublicApi?: boolean;
}): Promise<SanitizedAlert<Params> | SanitizedRuleWithLegacyId<Params>> { }): Promise<SanitizedRule<Params> | SanitizedRuleWithLegacyId<Params>> {
const result = await this.unsecuredSavedObjectsClient.get<RawRule>('alert', id); const result = await this.unsecuredSavedObjectsClient.get<RawRule>('alert', id);
try { try {
await this.authorization.ensureAuthorized({ await this.authorization.ensureAuthorized({
@ -475,7 +475,7 @@ export class RulesClient {
); );
} }
public async resolve<Params extends AlertTypeParams = never>({ public async resolve<Params extends RuleTypeParams = never>({
id, id,
includeLegacyId, includeLegacyId,
}: { }: {
@ -612,7 +612,7 @@ export class RulesClient {
}); });
} }
public async find<Params extends AlertTypeParams = never>({ public async find<Params extends RuleTypeParams = never>({
options: { fields, ...options } = {}, options: { fields, ...options } = {},
excludeFromPublicApi = false, excludeFromPublicApi = false,
}: { options?: FindOptions; excludeFromPublicApi?: boolean } = {}): Promise<FindResult<Params>> { }: { options?: FindOptions; excludeFromPublicApi?: boolean } = {}): Promise<FindResult<Params>> {
@ -762,7 +762,7 @@ export class RulesClient {
}, },
}; };
for (const key of AlertExecutionStatusValues) { for (const key of RuleExecutionStatusValues) {
placeholder.alertExecutionStatus[key] = 0; placeholder.alertExecutionStatus[key] = 0;
} }
@ -783,7 +783,7 @@ export class RulesClient {
}; };
// Fill missing keys with zeroes // Fill missing keys with zeroes
for (const key of AlertExecutionStatusValues) { for (const key of RuleExecutionStatusValues) {
if (!ret.alertExecutionStatus.hasOwnProperty(key)) { if (!ret.alertExecutionStatus.hasOwnProperty(key)) {
ret.alertExecutionStatus[key] = 0; ret.alertExecutionStatus[key] = 0;
} }
@ -878,10 +878,10 @@ export class RulesClient {
return removeResult; return removeResult;
} }
public async update<Params extends AlertTypeParams = never>({ public async update<Params extends RuleTypeParams = never>({
id, id,
data, data,
}: UpdateOptions<Params>): Promise<PartialAlert<Params>> { }: UpdateOptions<Params>): Promise<PartialRule<Params>> {
return await retryIfConflicts( return await retryIfConflicts(
this.logger, this.logger,
`rulesClient.update('${id}')`, `rulesClient.update('${id}')`,
@ -889,10 +889,10 @@ export class RulesClient {
); );
} }
private async updateWithOCC<Params extends AlertTypeParams>({ private async updateWithOCC<Params extends RuleTypeParams>({
id, id,
data, data,
}: UpdateOptions<Params>): Promise<PartialAlert<Params>> { }: UpdateOptions<Params>): Promise<PartialRule<Params>> {
let alertSavedObject: SavedObject<RawRule>; let alertSavedObject: SavedObject<RawRule>;
try { try {
@ -974,10 +974,10 @@ export class RulesClient {
return updateResult; return updateResult;
} }
private async updateAlert<Params extends AlertTypeParams>( private async updateAlert<Params extends RuleTypeParams>(
{ id, data }: UpdateOptions<Params>, { id, data }: UpdateOptions<Params>,
{ attributes, version }: SavedObject<RawRule> { attributes, version }: SavedObject<RawRule>
): Promise<PartialAlert<Params>> { ): Promise<PartialRule<Params>> {
const ruleType = this.ruleTypeRegistry.get(attributes.alertTypeId); const ruleType = this.ruleTypeRegistry.get(attributes.alertTypeId);
// Validate // Validate
@ -1048,7 +1048,7 @@ export class RulesClient {
throw e; throw e;
} }
return this.getPartialAlertFromRaw( return this.getPartialRuleFromRaw(
id, id,
ruleType, ruleType,
updatedObject.attributes, updatedObject.attributes,
@ -1332,12 +1332,12 @@ export class RulesClient {
try { try {
const { state } = taskInstanceToAlertTaskInstance( const { state } = taskInstanceToAlertTaskInstance(
await this.taskManager.get(attributes.scheduledTaskId), await this.taskManager.get(attributes.scheduledTaskId),
attributes as unknown as SanitizedAlert attributes as unknown as SanitizedRule
); );
const recoveredAlertInstances = mapValues<Record<string, RawAlertInstance>, AlertInstance>( const recoveredAlertInstances = mapValues<Record<string, RawAlert>, Alert>(
state.alertInstances ?? {}, state.alertInstances ?? {},
(rawAlertInstance) => new AlertInstance(rawAlertInstance) (rawAlertInstance) => new Alert(rawAlertInstance)
); );
const recoveredAlertInstanceIds = Object.keys(recoveredAlertInstances); const recoveredAlertInstanceIds = Object.keys(recoveredAlertInstances);
@ -1568,7 +1568,7 @@ export class RulesClient {
} }
private async muteInstanceWithOCC({ alertId, alertInstanceId }: MuteOptions) { private async muteInstanceWithOCC({ alertId, alertInstanceId }: MuteOptions) {
const { attributes, version } = await this.unsecuredSavedObjectsClient.get<Alert>( const { attributes, version } = await this.unsecuredSavedObjectsClient.get<Rule>(
'alert', 'alert',
alertId alertId
); );
@ -1636,7 +1636,7 @@ export class RulesClient {
alertId: string; alertId: string;
alertInstanceId: string; alertInstanceId: string;
}) { }) {
const { attributes, version } = await this.unsecuredSavedObjectsClient.get<Alert>( const { attributes, version } = await this.unsecuredSavedObjectsClient.get<Rule>(
'alert', 'alert',
alertId alertId
); );
@ -1751,22 +1751,22 @@ export class RulesClient {
...omit(action, 'actionRef'), ...omit(action, 'actionRef'),
id: reference.id, id: reference.id,
}; };
}) as Alert['actions']; }) as Rule['actions'];
} }
private getAlertFromRaw<Params extends AlertTypeParams>( private getAlertFromRaw<Params extends RuleTypeParams>(
id: string, id: string,
ruleTypeId: string, ruleTypeId: string,
rawRule: RawRule, rawRule: RawRule,
references: SavedObjectReference[] | undefined, references: SavedObjectReference[] | undefined,
includeLegacyId: boolean = false, includeLegacyId: boolean = false,
excludeFromPublicApi: boolean = false excludeFromPublicApi: boolean = false
): Alert | AlertWithLegacyId { ): Rule | RuleWithLegacyId {
const ruleType = this.ruleTypeRegistry.get(ruleTypeId); const ruleType = this.ruleTypeRegistry.get(ruleTypeId);
// In order to support the partial update API of Saved Objects we have to support // In order to support the partial update API of Saved Objects we have to support
// partial updates of an Alert, but when we receive an actual RawRule, it is safe // partial updates of an Alert, but when we receive an actual RawRule, it is safe
// to cast the result to an Alert // to cast the result to an Alert
const res = this.getPartialAlertFromRaw<Params>( const res = this.getPartialRuleFromRaw<Params>(
id, id,
ruleType, ruleType,
rawRule, rawRule,
@ -1776,13 +1776,13 @@ export class RulesClient {
); );
// include to result because it is for internal rules client usage // include to result because it is for internal rules client usage
if (includeLegacyId) { if (includeLegacyId) {
return res as AlertWithLegacyId; return res as RuleWithLegacyId;
} }
// exclude from result because it is an internal variable // exclude from result because it is an internal variable
return omit(res, ['legacyId']) as Alert; return omit(res, ['legacyId']) as Rule;
} }
private getPartialAlertFromRaw<Params extends AlertTypeParams>( private getPartialRuleFromRaw<Params extends RuleTypeParams>(
id: string, id: string,
ruleType: UntypedNormalizedRuleType, ruleType: UntypedNormalizedRuleType,
{ {
@ -1801,7 +1801,7 @@ export class RulesClient {
references: SavedObjectReference[] | undefined, references: SavedObjectReference[] | undefined,
includeLegacyId: boolean = false, includeLegacyId: boolean = false,
excludeFromPublicApi: boolean = false excludeFromPublicApi: boolean = false
): PartialAlert<Params> | PartialAlertWithLegacyId<Params> { ): PartialRule<Params> | PartialRuleWithLegacyId<Params> {
const rule = { const rule = {
id, id,
notifyWhen, notifyWhen,
@ -1820,8 +1820,8 @@ export class RulesClient {
}; };
return includeLegacyId return includeLegacyId
? ({ ...rule, legacyId } as PartialAlertWithLegacyId<Params>) ? ({ ...rule, legacyId } as PartialRuleWithLegacyId<Params>)
: (rule as PartialAlert<Params>); : (rule as PartialRule<Params>);
} }
private async validateActions( private async validateActions(
@ -1873,8 +1873,8 @@ export class RulesClient {
} }
private async extractReferences< private async extractReferences<
Params extends AlertTypeParams, Params extends RuleTypeParams,
ExtractedParams extends AlertTypeParams ExtractedParams extends RuleTypeParams
>( >(
ruleType: UntypedNormalizedRuleType, ruleType: UntypedNormalizedRuleType,
ruleActions: NormalizedAlertAction[], ruleActions: NormalizedAlertAction[],
@ -1909,8 +1909,8 @@ export class RulesClient {
} }
private injectReferencesIntoParams< private injectReferencesIntoParams<
Params extends AlertTypeParams, Params extends RuleTypeParams,
ExtractedParams extends AlertTypeParams ExtractedParams extends RuleTypeParams
>( >(
ruleId: string, ruleId: string,
ruleType: UntypedNormalizedRuleType, ruleType: UntypedNormalizedRuleType,

View file

@ -309,7 +309,7 @@ describe('Task Runner', () => {
}, },
] ]
`); `);
expect(call.services.alertInstanceFactory).toBeTruthy(); expect(call.services.alertFactory.create).toBeTruthy();
expect(call.services.scopedClusterClient).toBeTruthy(); expect(call.services.scopedClusterClient).toBeTruthy();
expect(call.services).toBeTruthy(); expect(call.services).toBeTruthy();
@ -427,8 +427,8 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices executorServices.alertFactory
.alertInstanceFactory('1') .create('1')
.scheduleActionsWithSubGroup('default', 'subDefault'); .scheduleActionsWithSubGroup('default', 'subDefault');
} }
); );
@ -708,7 +708,7 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -934,8 +934,8 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
executorServices.alertInstanceFactory('2').scheduleActions('default'); executorServices.alertFactory.create('2').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -991,7 +991,7 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -1192,7 +1192,7 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -1268,8 +1268,8 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices executorServices.alertFactory
.alertInstanceFactory('1') .create('1')
.scheduleActionsWithSubGroup('default', 'subgroup1'); .scheduleActionsWithSubGroup('default', 'subgroup1');
} }
); );
@ -1350,7 +1350,7 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -1672,7 +1672,7 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -2080,10 +2080,10 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
// create an instance, but don't schedule any actions, so it doesn't go active // create an instance, but don't schedule any actions, so it doesn't go active
executorServices.alertInstanceFactory('3'); executorServices.alertFactory.create('3');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -2186,7 +2186,7 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -2297,7 +2297,7 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
const date = new Date().toISOString(); const date = new Date().toISOString();
@ -3692,8 +3692,8 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
executorServices.alertInstanceFactory('2').scheduleActions('default'); executorServices.alertFactory.create('2').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -4006,8 +4006,8 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
executorServices.alertInstanceFactory('2').scheduleActions('default'); executorServices.alertFactory.create('2').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -4251,8 +4251,8 @@ describe('Task Runner', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
executorServices.alertInstanceFactory('2').scheduleActions('default'); executorServices.alertFactory.create('2').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(
@ -5035,7 +5035,7 @@ describe('Task Runner', () => {
}, },
] ]
`); `);
expect(call.services.alertInstanceFactory).toBeTruthy(); expect(call.services.alertFactory.create).toBeTruthy();
expect(call.services.scopedClusterClient).toBeTruthy(); expect(call.services.scopedClusterClient).toBeTruthy();
expect(call.services).toBeTruthy(); expect(call.services).toBeTruthy();

View file

@ -15,7 +15,7 @@ import { Logger, KibanaRequest } from '../../../../../src/core/server';
import { TaskRunnerContext } from './task_runner_factory'; import { TaskRunnerContext } from './task_runner_factory';
import { ConcreteTaskInstance, throwUnrecoverableError } from '../../../task_manager/server'; import { ConcreteTaskInstance, throwUnrecoverableError } from '../../../task_manager/server';
import { createExecutionHandler, ExecutionHandler } from './create_execution_handler'; import { createExecutionHandler, ExecutionHandler } from './create_execution_handler';
import { AlertInstance, createAlertInstanceFactory } from '../alert_instance'; import { Alert as CreatedAlert, createAlertFactory } from '../alert';
import { import {
validateRuleTypeParams, validateRuleTypeParams,
executionStatusFromState, executionStatusFromState,
@ -285,7 +285,7 @@ export class TaskRunner<
async executeAlert( async executeAlert(
alertId: string, alertId: string,
alert: AlertInstance<InstanceState, InstanceContext>, alert: CreatedAlert<InstanceState, InstanceContext>,
executionHandler: ExecutionHandler<ActionGroupIds | RecoveryActionGroupId> executionHandler: ExecutionHandler<ActionGroupIds | RecoveryActionGroupId>
) { ) {
const { const {
@ -333,8 +333,8 @@ export class TaskRunner<
const alerts = mapValues< const alerts = mapValues<
Record<string, RawAlertInstance>, Record<string, RawAlertInstance>,
AlertInstance<InstanceState, InstanceContext> CreatedAlert<InstanceState, InstanceContext>
>(alertRawInstances, (rawAlert) => new AlertInstance<InstanceState, InstanceContext>(rawAlert)); >(alertRawInstances, (rawAlert) => new CreatedAlert<InstanceState, InstanceContext>(rawAlert));
const originalAlerts = cloneDeep(alerts); const originalAlerts = cloneDeep(alerts);
const originalAlertIds = new Set(Object.keys(originalAlerts)); const originalAlertIds = new Set(Object.keys(originalAlerts));
@ -358,11 +358,13 @@ export class TaskRunner<
executionId: this.executionId, executionId: this.executionId,
services: { services: {
...services, ...services,
alertInstanceFactory: createAlertInstanceFactory< alertFactory: createAlertFactory<
InstanceState, InstanceState,
InstanceContext, InstanceContext,
WithoutReservedActionGroups<ActionGroupIds, RecoveryActionGroupId> WithoutReservedActionGroups<ActionGroupIds, RecoveryActionGroupId>
>(alerts), >({
alerts,
}),
shouldWriteAlerts: () => this.shouldLogAndScheduleActionsForAlerts(), shouldWriteAlerts: () => this.shouldLogAndScheduleActionsForAlerts(),
shouldStopExecution: () => this.cancelled, shouldStopExecution: () => this.cancelled,
search: createAbortableEsClientFactory({ search: createAbortableEsClientFactory({
@ -420,11 +422,11 @@ export class TaskRunner<
// Cleanup alerts that are no longer scheduling actions to avoid over populating the alertInstances object // Cleanup alerts that are no longer scheduling actions to avoid over populating the alertInstances object
const alertsWithScheduledActions = pickBy( const alertsWithScheduledActions = pickBy(
alerts, alerts,
(alert: AlertInstance<InstanceState, InstanceContext>) => alert.hasScheduledActions() (alert: CreatedAlert<InstanceState, InstanceContext>) => alert.hasScheduledActions()
); );
const recoveredAlerts = pickBy( const recoveredAlerts = pickBy(
alerts, alerts,
(alert: AlertInstance<InstanceState, InstanceContext>, id) => (alert: CreatedAlert<InstanceState, InstanceContext>, id) =>
!alert.hasScheduledActions() && originalAlertIds.has(id) !alert.hasScheduledActions() && originalAlertIds.has(id)
); );
@ -478,7 +480,7 @@ export class TaskRunner<
const alertsToExecute = const alertsToExecute =
notifyWhen === 'onActionGroupChange' notifyWhen === 'onActionGroupChange'
? Object.entries(alertsWithScheduledActions).filter( ? Object.entries(alertsWithScheduledActions).filter(
([alertName, alert]: [string, AlertInstance<InstanceState, InstanceContext>]) => { ([alertName, alert]: [string, CreatedAlert<InstanceState, InstanceContext>]) => {
const shouldExecuteAction = alert.scheduledActionGroupOrSubgroupHasChanged(); const shouldExecuteAction = alert.scheduledActionGroupOrSubgroupHasChanged();
if (!shouldExecuteAction) { if (!shouldExecuteAction) {
this.logger.debug( this.logger.debug(
@ -489,7 +491,7 @@ export class TaskRunner<
} }
) )
: Object.entries(alertsWithScheduledActions).filter( : Object.entries(alertsWithScheduledActions).filter(
([alertName, alert]: [string, AlertInstance<InstanceState, InstanceContext>]) => { ([alertName, alert]: [string, CreatedAlert<InstanceState, InstanceContext>]) => {
const throttled = alert.isThrottled(throttle); const throttled = alert.isThrottled(throttle);
const muted = mutedAlertIdsSet.has(alertName); const muted = mutedAlertIdsSet.has(alertName);
const shouldExecuteAction = !throttled && !muted; const shouldExecuteAction = !throttled && !muted;
@ -506,7 +508,7 @@ export class TaskRunner<
const allTriggeredActions = await Promise.all( const allTriggeredActions = await Promise.all(
alertsToExecute.map( alertsToExecute.map(
([alertId, alert]: [string, AlertInstance<InstanceState, InstanceContext>]) => ([alertId, alert]: [string, CreatedAlert<InstanceState, InstanceContext>]) =>
this.executeAlert(alertId, alert, executionHandler) this.executeAlert(alertId, alert, executionHandler)
) )
); );
@ -533,7 +535,7 @@ export class TaskRunner<
triggeredActions, triggeredActions,
alertTypeState: updatedRuleTypeState || undefined, alertTypeState: updatedRuleTypeState || undefined,
alertInstances: mapValues< alertInstances: mapValues<
Record<string, AlertInstance<InstanceState, InstanceContext>>, Record<string, CreatedAlert<InstanceState, InstanceContext>>,
RawAlertInstance RawAlertInstance
>(alertsWithScheduledActions, (alert) => alert.toRaw()), >(alertsWithScheduledActions, (alert) => alert.toRaw()),
}; };
@ -910,9 +912,9 @@ interface TrackAlertDurationsParams<
InstanceState extends AlertInstanceState, InstanceState extends AlertInstanceState,
InstanceContext extends AlertInstanceContext InstanceContext extends AlertInstanceContext
> { > {
originalAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext>>; originalAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext>>;
currentAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext>>; currentAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext>>;
recoveredAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext>>; recoveredAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext>>;
} }
function trackAlertDurations< function trackAlertDurations<
@ -967,9 +969,9 @@ interface GenerateNewAndRecoveredAlertEventsParams<
> { > {
eventLogger: IEventLogger; eventLogger: IEventLogger;
executionId: string; executionId: string;
originalAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext>>; originalAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext>>;
currentAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext>>; currentAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext>>;
recoveredAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext>>; recoveredAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext>>;
ruleId: string; ruleId: string;
ruleLabel: string; ruleLabel: string;
namespace: string | undefined; namespace: string | undefined;
@ -1117,7 +1119,7 @@ interface ScheduleActionsForRecoveredAlertsParams<
> { > {
logger: Logger; logger: Logger;
recoveryActionGroup: ActionGroup<RecoveryActionGroupId>; recoveryActionGroup: ActionGroup<RecoveryActionGroupId>;
recoveredAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext, RecoveryActionGroupId>>; recoveredAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext, RecoveryActionGroupId>>;
executionHandler: ExecutionHandler<RecoveryActionGroupId | RecoveryActionGroupId>; executionHandler: ExecutionHandler<RecoveryActionGroupId | RecoveryActionGroupId>;
mutedAlertIdsSet: Set<string>; mutedAlertIdsSet: Set<string>;
ruleLabel: string; ruleLabel: string;
@ -1173,8 +1175,8 @@ interface LogActiveAndRecoveredAlertsParams<
RecoveryActionGroupId extends string RecoveryActionGroupId extends string
> { > {
logger: Logger; logger: Logger;
activeAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext, ActionGroupIds>>; activeAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext, ActionGroupIds>>;
recoveredAlerts: Dictionary<AlertInstance<InstanceState, InstanceContext, RecoveryActionGroupId>>; recoveredAlerts: Dictionary<CreatedAlert<InstanceState, InstanceContext, RecoveryActionGroupId>>;
ruleLabel: string; ruleLabel: string;
} }

View file

@ -359,7 +359,7 @@ describe('Task Runner Cancel', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
// setting cancelAlertsOnRuleTimeout to false here // setting cancelAlertsOnRuleTimeout to false here
@ -393,7 +393,7 @@ describe('Task Runner Cancel', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
// setting cancelAlertsOnRuleTimeout for ruleType to false here // setting cancelAlertsOnRuleTimeout for ruleType to false here
@ -427,7 +427,7 @@ describe('Task Runner Cancel', () => {
AlertInstanceContext, AlertInstanceContext,
string string
>) => { >) => {
executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertFactory.create('1').scheduleActions('default');
} }
); );
const taskRunner = new TaskRunner( const taskRunner = new TaskRunner(

View file

@ -7,7 +7,7 @@
import type { IRouter, RequestHandlerContext, SavedObjectReference } from 'src/core/server'; import type { IRouter, RequestHandlerContext, SavedObjectReference } from 'src/core/server';
import type { PublicMethodsOf } from '@kbn/utility-types'; import type { PublicMethodsOf } from '@kbn/utility-types';
import { PublicAlertInstance } from './alert_instance'; import { PublicAlert } from './alert';
import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry';
import { PluginSetupContract, PluginStartContract } from './plugin'; import { PluginSetupContract, PluginStartContract } from './plugin';
import { RulesClient } from './rules_client'; import { RulesClient } from './rules_client';
@ -74,9 +74,9 @@ export interface AlertServices<
InstanceContext extends AlertInstanceContext = AlertInstanceContext, InstanceContext extends AlertInstanceContext = AlertInstanceContext,
ActionGroupIds extends string = never ActionGroupIds extends string = never
> extends Services { > extends Services {
alertInstanceFactory: ( alertFactory: {
id: string create: (id: string) => PublicAlert<InstanceState, InstanceContext, ActionGroupIds>;
) => PublicAlertInstance<InstanceState, InstanceContext, ActionGroupIds>; };
shouldWriteAlerts: () => boolean; shouldWriteAlerts: () => boolean;
shouldStopExecution: () => boolean; shouldStopExecution: () => boolean;
search: IAbortableClusterClient; search: IAbortableClusterClient;

View file

@ -39,7 +39,7 @@ describe('Error count alert', () => {
); );
await executor({ params }); await executor({ params });
expect(services.alertInstanceFactory).not.toBeCalled(); expect(services.alertFactory.create).not.toBeCalled();
}); });
it('sends alerts with service name and environment for those that exceeded the threshold', async () => { it('sends alerts with service name and environment for those that exceeded the threshold', async () => {
@ -138,7 +138,7 @@ describe('Error count alert', () => {
'apm.error_rate_foo_env-foo-2', 'apm.error_rate_foo_env-foo-2',
'apm.error_rate_bar_env-bar', 'apm.error_rate_bar_env-bar',
].forEach((instanceName) => ].forEach((instanceName) =>
expect(services.alertInstanceFactory).toHaveBeenCalledWith(instanceName) expect(services.alertFactory.create).toHaveBeenCalledWith(instanceName)
); );
expect(scheduleActions).toHaveBeenCalledTimes(3); expect(scheduleActions).toHaveBeenCalledTimes(3);

View file

@ -32,7 +32,7 @@ describe('Transaction duration anomaly alert', () => {
services.scopedClusterClient.asCurrentUser.search services.scopedClusterClient.asCurrentUser.search
).not.toHaveBeenCalled(); ).not.toHaveBeenCalled();
expect(services.alertInstanceFactory).not.toHaveBeenCalled(); expect(services.alertFactory.create).not.toHaveBeenCalled();
}); });
it('ml jobs are not available', async () => { it('ml jobs are not available', async () => {
@ -59,7 +59,7 @@ describe('Transaction duration anomaly alert', () => {
services.scopedClusterClient.asCurrentUser.search services.scopedClusterClient.asCurrentUser.search
).not.toHaveBeenCalled(); ).not.toHaveBeenCalled();
expect(services.alertInstanceFactory).not.toHaveBeenCalled(); expect(services.alertFactory.create).not.toHaveBeenCalled();
}); });
it('anomaly is less than threshold', async () => { it('anomaly is less than threshold', async () => {
@ -110,7 +110,7 @@ describe('Transaction duration anomaly alert', () => {
expect( expect(
services.scopedClusterClient.asCurrentUser.search services.scopedClusterClient.asCurrentUser.search
).not.toHaveBeenCalled(); ).not.toHaveBeenCalled();
expect(services.alertInstanceFactory).not.toHaveBeenCalled(); expect(services.alertFactory.create).not.toHaveBeenCalled();
}); });
}); });
@ -183,9 +183,9 @@ describe('Transaction duration anomaly alert', () => {
await executor({ params }); await executor({ params });
expect(services.alertInstanceFactory).toHaveBeenCalledTimes(1); expect(services.alertFactory.create).toHaveBeenCalledTimes(1);
expect(services.alertInstanceFactory).toHaveBeenCalledWith( expect(services.alertFactory.create).toHaveBeenCalledWith(
'apm.transaction_duration_anomaly_foo_development_type-foo' 'apm.transaction_duration_anomaly_foo_development_type-foo'
); );

View file

@ -46,7 +46,7 @@ describe('Transaction error rate alert', () => {
); );
await executor({ params }); await executor({ params });
expect(services.alertInstanceFactory).not.toBeCalled(); expect(services.alertFactory.create).not.toBeCalled();
}); });
it('sends alerts for services that exceeded the threshold', async () => { it('sends alerts for services that exceeded the threshold', async () => {
@ -117,12 +117,12 @@ describe('Transaction error rate alert', () => {
await executor({ params }); await executor({ params });
expect(services.alertInstanceFactory).toHaveBeenCalledTimes(1); expect(services.alertFactory.create).toHaveBeenCalledTimes(1);
expect(services.alertInstanceFactory).toHaveBeenCalledWith( expect(services.alertFactory.create).toHaveBeenCalledWith(
'apm.transaction_error_rate_foo_type-foo_env-foo' 'apm.transaction_error_rate_foo_type-foo_env-foo'
); );
expect(services.alertInstanceFactory).not.toHaveBeenCalledWith( expect(services.alertFactory.create).not.toHaveBeenCalledWith(
'apm.transaction_error_rate_bar_type-bar_env-bar' 'apm.transaction_error_rate_bar_type-bar_env-bar'
); );

View file

@ -42,7 +42,7 @@ export const createRuleTypeMocks = () => {
savedObjectsClient: { savedObjectsClient: {
get: () => ({ attributes: { consumer: APM_SERVER_FEATURE_ID } }), get: () => ({ attributes: { consumer: APM_SERVER_FEATURE_ID } }),
}, },
alertInstanceFactory: jest.fn(() => ({ scheduleActions })), alertFactory: { create: jest.fn(() => ({ scheduleActions })) },
alertWithLifecycle: jest.fn(), alertWithLifecycle: jest.fn(),
logger: loggerMock, logger: loggerMock,
shouldWriteAlerts: () => true, shouldWriteAlerts: () => true,

View file

@ -16,10 +16,7 @@ import {
AlertInstanceState as AlertState, AlertInstanceState as AlertState,
RecoveredActionGroup, RecoveredActionGroup,
} from '../../../../../alerting/common'; } from '../../../../../alerting/common';
import { import { Alert, AlertTypeState as RuleTypeState } from '../../../../../alerting/server';
AlertInstance as Alert,
AlertTypeState as RuleTypeState,
} from '../../../../../alerting/server';
import { AlertStates, InventoryMetricThresholdParams } from '../../../../common/alerting/metrics'; import { AlertStates, InventoryMetricThresholdParams } from '../../../../common/alerting/metrics';
import { createFormatter } from '../../../../common/formatters'; import { createFormatter } from '../../../../common/formatters';
import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label'; import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label';

View file

@ -422,7 +422,7 @@ describe('Log threshold executor', () => {
processUngroupedResults( processUngroupedResults(
results, results,
ruleParams, ruleParams,
alertsMock.createAlertInstanceFactory, alertsMock.createAlertFactory.create,
alertUpdaterMock alertUpdaterMock
); );
// First call, second argument // First call, second argument
@ -486,7 +486,7 @@ describe('Log threshold executor', () => {
processGroupByResults( processGroupByResults(
results, results,
ruleParams, ruleParams,
alertsMock.createAlertInstanceFactory, alertsMock.createAlertFactory.create,
alertUpdaterMock alertUpdaterMock
); );
expect(alertUpdaterMock.mock.calls.length).toBe(2); expect(alertUpdaterMock.mock.calls.length).toBe(2);

View file

@ -16,7 +16,7 @@ import { ElasticsearchClient } from 'kibana/server';
import { import {
ActionGroup, ActionGroup,
ActionGroupIdsOf, ActionGroupIdsOf,
AlertInstance as Alert, Alert,
AlertInstanceContext as AlertContext, AlertInstanceContext as AlertContext,
AlertInstanceState as AlertState, AlertInstanceState as AlertState,
AlertTypeState as RuleTypeState, AlertTypeState as RuleTypeState,

View file

@ -83,7 +83,7 @@ export const createMetricAnomalyExecutor =
typical, typical,
influencers, influencers,
} = first(data as MappedAnomalyHit[])!; } = first(data as MappedAnomalyHit[])!;
const alert = services.alertInstanceFactory(`${nodeType}-${metric}`); const alert = services.alertFactory.create(`${nodeType}-${metric}`);
alert.scheduleActions(FIRED_ACTIONS_ID, { alert.scheduleActions(FIRED_ACTIONS_ID, {
alertState: stateToAlertMessage[AlertStates.ALERT], alertState: stateToAlertMessage[AlertStates.ALERT],

View file

@ -840,9 +840,9 @@ services.savedObjectsClient.get.mockImplementation(async (type: string, sourceId
}); });
const alertInstances = new Map<string, AlertTestInstance>(); const alertInstances = new Map<string, AlertTestInstance>();
services.alertInstanceFactory.mockImplementation((instanceID: string) => { services.alertFactory.create.mockImplementation((instanceID: string) => {
const newAlertInstance: AlertTestInstance = { const newAlertInstance: AlertTestInstance = {
instance: alertsMock.createAlertInstanceFactory(), instance: alertsMock.createAlertFactory.create(),
actionQueue: [], actionQueue: [],
state: {}, state: {},
}; };

View file

@ -15,10 +15,7 @@ import {
AlertInstanceState as AlertState, AlertInstanceState as AlertState,
RecoveredActionGroup, RecoveredActionGroup,
} from '../../../../../alerting/common'; } from '../../../../../alerting/common';
import { import { Alert, AlertTypeState as RuleTypeState } from '../../../../../alerting/server';
AlertInstance as Alert,
AlertTypeState as RuleTypeState,
} from '../../../../../alerting/server';
import { AlertStates, Comparator } from '../../../../common/alerting/metrics'; import { AlertStates, Comparator } from '../../../../common/alerting/metrics';
import { createFormatter } from '../../../../common/formatters'; import { createFormatter } from '../../../../common/formatters';
import { InfraBackendLibs } from '../../infra_types'; import { InfraBackendLibs } from '../../infra_types';

View file

@ -139,7 +139,7 @@ export function registerAnomalyDetectionAlertType({
if (executionResult) { if (executionResult) {
const alertInstanceName = executionResult.name; const alertInstanceName = executionResult.name;
const alertInstance = services.alertInstanceFactory(alertInstanceName); const alertInstance = services.alertFactory.create(alertInstanceName);
alertInstance.scheduleActions(ANOMALY_SCORE_MATCH_GROUP_ID, executionResult); alertInstance.scheduleActions(ANOMALY_SCORE_MATCH_GROUP_ID, executionResult);
} }
}, },

View file

@ -159,7 +159,7 @@ export function registerJobsMonitoringRuleType({
); );
executionResult.forEach(({ name: alertInstanceName, context }) => { executionResult.forEach(({ name: alertInstanceName, context }) => {
const alertInstance = services.alertInstanceFactory(alertInstanceName); const alertInstance = services.alertFactory.create(alertInstanceName);
alertInstance.scheduleActions(ANOMALY_DETECTION_JOB_REALTIME_ISSUE, context); alertInstance.scheduleActions(ANOMALY_DETECTION_JOB_REALTIME_ISSUE, context);
}); });
} }

View file

@ -10,11 +10,16 @@ import { i18n } from '@kbn/i18n';
import { import {
RuleType, RuleType,
AlertExecutorOptions, AlertExecutorOptions,
AlertInstance, Alert,
RulesClient, RulesClient,
AlertServices, AlertServices,
} from '../../../alerting/server'; } from '../../../alerting/server';
import { Alert, AlertTypeParams, RawAlertInstance, SanitizedAlert } from '../../../alerting/common'; import {
Alert as Rule,
AlertTypeParams,
RawAlertInstance,
SanitizedAlert,
} from '../../../alerting/common';
import { ActionsClient } from '../../../actions/server'; import { ActionsClient } from '../../../actions/server';
import { import {
AlertState, AlertState,
@ -121,7 +126,7 @@ export class BaseRule {
}); });
if (existingRuleData.total > 0) { if (existingRuleData.total > 0) {
return existingRuleData.data[0] as Alert; return existingRuleData.data[0] as Rule;
} }
const ruleActions = []; const ruleActions = [];
@ -272,7 +277,7 @@ export class BaseRule {
for (const node of nodes) { for (const node of nodes) {
const newAlertStates: AlertNodeState[] = []; const newAlertStates: AlertNodeState[] = [];
// quick fix for now so that non node level alerts will use the cluster id // quick fix for now so that non node level alerts will use the cluster id
const instance = services.alertInstanceFactory( const instance = services.alertFactory.create(
node.meta.nodeId || node.meta.instanceId || cluster.clusterUuid node.meta.nodeId || node.meta.instanceId || cluster.clusterUuid
); );
@ -331,7 +336,7 @@ export class BaseRule {
} }
protected executeActions( protected executeActions(
instance: AlertInstance, instance: Alert,
instanceState: AlertInstanceState | AlertState | unknown, instanceState: AlertInstanceState | AlertState | unknown,
item: AlertData | unknown, item: AlertData | unknown,
cluster?: AlertCluster | unknown cluster?: AlertCluster | unknown

View file

@ -116,7 +116,8 @@ describe('CCRReadExceptionsRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -124,6 +125,7 @@ describe('CCRReadExceptionsRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -21,7 +21,7 @@ import {
CommonAlertFilter, CommonAlertFilter,
CCRReadExceptionsStats, CCRReadExceptionsStats,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_CCR_READ_EXCEPTIONS, RULE_DETAILS } from '../../common/constants'; import { RULE_CCR_READ_EXCEPTIONS, RULE_DETAILS } from '../../common/constants';
import { fetchCCRReadExceptions } from '../lib/alerts/fetch_ccr_read_exceptions'; import { fetchCCRReadExceptions } from '../lib/alerts/fetch_ccr_read_exceptions';
import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { AlertMessageTokenType, AlertSeverity } from '../../common/enums';
@ -209,7 +209,7 @@ export class CCRReadExceptionsRule extends BaseRule {
} }
protected executeActions( protected executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -81,7 +81,8 @@ describe('ClusterHealthRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -89,6 +90,7 @@ describe('ClusterHealthRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -18,7 +18,7 @@ import {
AlertClusterHealth, AlertClusterHealth,
AlertInstanceState, AlertInstanceState,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_CLUSTER_HEALTH, LEGACY_RULE_DETAILS } from '../../common/constants'; import { RULE_CLUSTER_HEALTH, LEGACY_RULE_DETAILS } from '../../common/constants';
import { AlertMessageTokenType, AlertClusterHealthType, AlertSeverity } from '../../common/enums'; import { AlertMessageTokenType, AlertClusterHealthType, AlertSeverity } from '../../common/enums';
import { AlertingDefaults } from './alert_helpers'; import { AlertingDefaults } from './alert_helpers';
@ -111,7 +111,7 @@ export class ClusterHealthRule extends BaseRule {
} }
protected async executeActions( protected async executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -83,7 +83,8 @@ describe('CpuUsageRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -91,6 +92,7 @@ describe('CpuUsageRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -22,7 +22,7 @@ import {
CommonAlertParams, CommonAlertParams,
CommonAlertFilter, CommonAlertFilter,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_CPU_USAGE, RULE_DETAILS } from '../../common/constants'; import { RULE_CPU_USAGE, RULE_DETAILS } from '../../common/constants';
// @ts-ignore // @ts-ignore
import { ROUNDED_FLOAT } from '../../common/formatting'; import { ROUNDED_FLOAT } from '../../common/formatting';
@ -145,7 +145,7 @@ export class CpuUsageRule extends BaseRule {
} }
protected executeActions( protected executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -96,7 +96,8 @@ describe('DiskUsageRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -104,6 +105,7 @@ describe('DiskUsageRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -22,7 +22,7 @@ import {
AlertDiskUsageNodeStats, AlertDiskUsageNodeStats,
CommonAlertFilter, CommonAlertFilter,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_DISK_USAGE, RULE_DETAILS } from '../../common/constants'; import { RULE_DISK_USAGE, RULE_DETAILS } from '../../common/constants';
// @ts-ignore // @ts-ignore
import { ROUNDED_FLOAT } from '../../common/formatting'; import { ROUNDED_FLOAT } from '../../common/formatting';
@ -152,7 +152,7 @@ export class DiskUsageRule extends BaseRule {
} }
protected executeActions( protected executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -85,7 +85,8 @@ describe('ElasticsearchVersionMismatchAlert', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -93,6 +94,7 @@ describe('ElasticsearchVersionMismatchAlert', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -17,7 +17,7 @@ import {
CommonAlertParams, CommonAlertParams,
AlertVersions, AlertVersions,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_ELASTICSEARCH_VERSION_MISMATCH, LEGACY_RULE_DETAILS } from '../../common/constants'; import { RULE_ELASTICSEARCH_VERSION_MISMATCH, LEGACY_RULE_DETAILS } from '../../common/constants';
import { AlertSeverity } from '../../common/enums'; import { AlertSeverity } from '../../common/enums';
import { AlertingDefaults } from './alert_helpers'; import { AlertingDefaults } from './alert_helpers';
@ -87,7 +87,7 @@ export class ElasticsearchVersionMismatchRule extends BaseRule {
} }
protected async executeActions( protected async executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -88,7 +88,8 @@ describe('KibanaVersionMismatchRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -96,6 +97,7 @@ describe('KibanaVersionMismatchRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -17,7 +17,7 @@ import {
CommonAlertParams, CommonAlertParams,
AlertVersions, AlertVersions,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_KIBANA_VERSION_MISMATCH, LEGACY_RULE_DETAILS } from '../../common/constants'; import { RULE_KIBANA_VERSION_MISMATCH, LEGACY_RULE_DETAILS } from '../../common/constants';
import { AlertSeverity } from '../../common/enums'; import { AlertSeverity } from '../../common/enums';
import { AlertingDefaults } from './alert_helpers'; import { AlertingDefaults } from './alert_helpers';
@ -97,7 +97,7 @@ export class KibanaVersionMismatchRule extends BaseRule {
} }
protected async executeActions( protected async executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -96,7 +96,8 @@ describe('LargeShardSizeRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -104,6 +105,7 @@ describe('LargeShardSizeRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -21,7 +21,7 @@ import {
CommonAlertFilter, CommonAlertFilter,
IndexShardSizeStats, IndexShardSizeStats,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_LARGE_SHARD_SIZE, RULE_DETAILS } from '../../common/constants'; import { RULE_LARGE_SHARD_SIZE, RULE_DETAILS } from '../../common/constants';
import { fetchIndexShardSize } from '../lib/alerts/fetch_index_shard_size'; import { fetchIndexShardSize } from '../lib/alerts/fetch_index_shard_size';
import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { AlertMessageTokenType, AlertSeverity } from '../../common/enums';
@ -149,7 +149,7 @@ export class LargeShardSizeRule extends BaseRule {
} }
protected executeActions( protected executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -86,7 +86,8 @@ describe('LicenseExpirationRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -94,6 +95,7 @@ describe('LicenseExpirationRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -20,7 +20,7 @@ import {
AlertLicense, AlertLicense,
AlertLicenseState, AlertLicenseState,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertExecutorOptions, AlertInstance } from '../../../alerting/server'; import { AlertExecutorOptions, Alert } from '../../../alerting/server';
import { RULE_LICENSE_EXPIRATION, LEGACY_RULE_DETAILS } from '../../common/constants'; import { RULE_LICENSE_EXPIRATION, LEGACY_RULE_DETAILS } from '../../common/constants';
import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { AlertMessageTokenType, AlertSeverity } from '../../common/enums';
import { AlertingDefaults } from './alert_helpers'; import { AlertingDefaults } from './alert_helpers';
@ -143,7 +143,7 @@ export class LicenseExpirationRule extends BaseRule {
} }
protected async executeActions( protected async executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -86,7 +86,8 @@ describe('LogstashVersionMismatchRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -94,6 +95,7 @@ describe('LogstashVersionMismatchRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -17,7 +17,7 @@ import {
CommonAlertParams, CommonAlertParams,
AlertVersions, AlertVersions,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_LOGSTASH_VERSION_MISMATCH, LEGACY_RULE_DETAILS } from '../../common/constants'; import { RULE_LOGSTASH_VERSION_MISMATCH, LEGACY_RULE_DETAILS } from '../../common/constants';
import { AlertSeverity } from '../../common/enums'; import { AlertSeverity } from '../../common/enums';
import { AlertingDefaults } from './alert_helpers'; import { AlertingDefaults } from './alert_helpers';
@ -87,7 +87,7 @@ export class LogstashVersionMismatchRule extends BaseRule {
} }
protected async executeActions( protected async executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -83,7 +83,8 @@ describe('MemoryUsageRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -91,6 +92,7 @@ describe('MemoryUsageRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -22,7 +22,7 @@ import {
AlertMemoryUsageNodeStats, AlertMemoryUsageNodeStats,
CommonAlertFilter, CommonAlertFilter,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_MEMORY_USAGE, RULE_DETAILS } from '../../common/constants'; import { RULE_MEMORY_USAGE, RULE_DETAILS } from '../../common/constants';
// @ts-ignore // @ts-ignore
import { ROUNDED_FLOAT } from '../../common/formatting'; import { ROUNDED_FLOAT } from '../../common/formatting';
@ -158,7 +158,7 @@ export class MemoryUsageRule extends BaseRule {
} }
protected executeActions( protected executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -87,7 +87,8 @@ describe('MissingMonitoringDataRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -95,6 +96,7 @@ describe('MissingMonitoringDataRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -19,7 +19,7 @@ import {
CommonAlertFilter, CommonAlertFilter,
AlertNodeState, AlertNodeState,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_MISSING_MONITORING_DATA, RULE_DETAILS } from '../../common/constants'; import { RULE_MISSING_MONITORING_DATA, RULE_DETAILS } from '../../common/constants';
import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { AlertMessageTokenType, AlertSeverity } from '../../common/enums';
import { RawAlertInstance, SanitizedAlert } from '../../../alerting/common'; import { RawAlertInstance, SanitizedAlert } from '../../../alerting/common';
@ -137,7 +137,7 @@ export class MissingMonitoringDataRule extends BaseRule {
} }
protected executeActions( protected executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: { alertStates: AlertState[] }, { alertStates }: { alertStates: AlertState[] },
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -137,7 +137,8 @@ describe('NodesChangedAlert', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -145,6 +146,7 @@ describe('NodesChangedAlert', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -19,7 +19,7 @@ import {
AlertInstanceState, AlertInstanceState,
AlertNodesChangedState, AlertNodesChangedState,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { RULE_NODES_CHANGED, LEGACY_RULE_DETAILS } from '../../common/constants'; import { RULE_NODES_CHANGED, LEGACY_RULE_DETAILS } from '../../common/constants';
import { AlertingDefaults } from './alert_helpers'; import { AlertingDefaults } from './alert_helpers';
import { SanitizedAlert } from '../../../alerting/common'; import { SanitizedAlert } from '../../../alerting/common';
@ -174,7 +174,7 @@ export class NodesChangedRule extends BaseRule {
} }
protected async executeActions( protected async executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: AlertInstanceState, { alertStates }: AlertInstanceState,
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -20,10 +20,10 @@ import {
AlertState, AlertState,
AlertThreadPoolRejectionsStats, AlertThreadPoolRejectionsStats,
} from '../../common/types/alerts'; } from '../../common/types/alerts';
import { AlertInstance } from '../../../alerting/server'; import { Alert } from '../../../alerting/server';
import { fetchThreadPoolRejectionStats } from '../lib/alerts/fetch_thread_pool_rejections_stats'; import { fetchThreadPoolRejectionStats } from '../lib/alerts/fetch_thread_pool_rejections_stats';
import { AlertMessageTokenType, AlertSeverity } from '../../common/enums'; import { AlertMessageTokenType, AlertSeverity } from '../../common/enums';
import { Alert, RawAlertInstance } from '../../../alerting/common'; import { Alert as Rule, RawAlertInstance } from '../../../alerting/common';
import { AlertingDefaults, createLink } from './alert_helpers'; import { AlertingDefaults, createLink } from './alert_helpers';
import { Globals } from '../static_globals'; import { Globals } from '../static_globals';
@ -47,7 +47,7 @@ export class ThreadPoolRejectionsRuleBase extends BaseRule {
} }
constructor( constructor(
sanitizedRule: Alert | undefined = undefined, sanitizedRule: Rule | undefined = undefined,
public readonly id: string, public readonly id: string,
public readonly threadPoolType: string, public readonly threadPoolType: string,
public readonly name: string, public readonly name: string,
@ -176,7 +176,7 @@ export class ThreadPoolRejectionsRuleBase extends BaseRule {
}; };
} }
protected executeActions( protected executeActions(
instance: AlertInstance, instance: Alert,
{ alertStates }: { alertStates: AlertState[] }, { alertStates }: { alertStates: AlertState[] },
item: AlertData | null, item: AlertData | null,
cluster: AlertCluster cluster: AlertCluster

View file

@ -89,7 +89,8 @@ describe('ThreadpoolSearchRejectionsRule', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -97,6 +98,7 @@ describe('ThreadpoolSearchRejectionsRule', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -89,7 +89,8 @@ describe('ThreadpoolWriteRejectionsAlert', () => {
const executorOptions = { const executorOptions = {
services: { services: {
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn().mockImplementation(() => { alertFactory: {
create: jest.fn().mockImplementation(() => {
return { return {
replaceState, replaceState,
scheduleActions, scheduleActions,
@ -97,6 +98,7 @@ describe('ThreadpoolWriteRejectionsAlert', () => {
}; };
}), }),
}, },
},
state: {}, state: {},
}; };

View file

@ -13,7 +13,7 @@ import { v4 } from 'uuid';
import { difference } from 'lodash'; import { difference } from 'lodash';
import { import {
AlertExecutorOptions, AlertExecutorOptions,
AlertInstance, Alert,
AlertInstanceContext, AlertInstanceContext,
AlertInstanceState, AlertInstanceState,
AlertTypeParams, AlertTypeParams,
@ -62,7 +62,7 @@ export type LifecycleAlertService<
> = (alert: { > = (alert: {
id: string; id: string;
fields: ExplicitAlertFields; fields: ExplicitAlertFields;
}) => AlertInstance<InstanceState, InstanceContext, ActionGroupIds>; }) => Alert<InstanceState, InstanceContext, ActionGroupIds>;
export interface LifecycleAlertServices< export interface LifecycleAlertServices<
InstanceState extends AlertInstanceState = never, InstanceState extends AlertInstanceState = never,
@ -143,7 +143,7 @@ export const createLifecycleExecutor =
> >
): Promise<WrappedLifecycleRuleState<State>> => { ): Promise<WrappedLifecycleRuleState<State>> => {
const { const {
services: { alertInstanceFactory, shouldWriteAlerts }, services: { alertFactory, shouldWriteAlerts },
state: previousState, state: previousState,
} = options; } = options;
@ -165,7 +165,7 @@ export const createLifecycleExecutor =
> = { > = {
alertWithLifecycle: ({ id, fields }) => { alertWithLifecycle: ({ id, fields }) => {
currentAlerts[id] = fields; currentAlerts[id] = fields;
return alertInstanceFactory(id); return alertFactory.create(id);
}, },
}; };

View file

@ -66,10 +66,12 @@ function createRule(shouldWriteAlerts: boolean = true) {
const scheduleActions = jest.fn(); const scheduleActions = jest.fn();
const alertInstanceFactory = () => { const alertFactory = {
create: () => {
return { return {
scheduleActions, scheduleActions,
} as any; } as any;
},
}; };
return { return {
@ -107,7 +109,7 @@ function createRule(shouldWriteAlerts: boolean = true) {
updatedBy: 'updatedBy', updatedBy: 'updatedBy',
}, },
services: { services: {
alertInstanceFactory, alertFactory,
savedObjectsClient: {} as any, savedObjectsClient: {} as any,
scopedClusterClient: {} as any, scopedClusterClient: {} as any,
shouldWriteAlerts: () => shouldWriteAlerts, shouldWriteAlerts: () => shouldWriteAlerts,

View file

@ -34,5 +34,5 @@ export const createLifecycleAlertServicesMock = <
>( >(
alertServices: AlertServices<InstanceState, InstanceContext> alertServices: AlertServices<InstanceState, InstanceContext>
): LifecycleAlertServices<InstanceState, InstanceContext, ActionGroupIds> => ({ ): LifecycleAlertServices<InstanceState, InstanceContext, ActionGroupIds> => ({
alertWithLifecycle: ({ id }) => alertServices.alertInstanceFactory(id), alertWithLifecycle: ({ id }) => alertServices.alertFactory.create(id),
}); });

View file

@ -67,8 +67,7 @@ export const createDefaultAlertExecutorOptions = <
params, params,
spaceId: 'SPACE_ID', spaceId: 'SPACE_ID',
services: { services: {
alertInstanceFactory: alertsMock.createAlertServices<InstanceState, InstanceContext>() alertFactory: alertsMock.createAlertServices<InstanceState, InstanceContext>().alertFactory,
.alertInstanceFactory,
savedObjectsClient: savedObjectsClientMock.create(), savedObjectsClient: savedObjectsClientMock.create(),
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
shouldWriteAlerts: () => shouldWriteAlerts, shouldWriteAlerts: () => shouldWriteAlerts,

View file

@ -136,9 +136,9 @@ describe('legacyRules_notification_alert_type', () => {
); );
await alert.executor(payload); await alert.executor(payload);
expect(alertServices.alertInstanceFactory).toHaveBeenCalled(); expect(alertServices.alertFactory.create).toHaveBeenCalled();
const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results; const [{ value: alertInstanceMock }] = alertServices.alertFactory.create.mock.results;
expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith( expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith(
'default', 'default',
expect.objectContaining({ expect.objectContaining({
@ -163,9 +163,9 @@ describe('legacyRules_notification_alert_type', () => {
) )
); );
await alert.executor(payload); await alert.executor(payload);
expect(alertServices.alertInstanceFactory).toHaveBeenCalled(); expect(alertServices.alertFactory.create).toHaveBeenCalled();
const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results; const [{ value: alertInstanceMock }] = alertServices.alertFactory.create.mock.results;
expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith( expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith(
'default', 'default',
expect.objectContaining({ expect.objectContaining({
@ -192,9 +192,9 @@ describe('legacyRules_notification_alert_type', () => {
) )
); );
await alert.executor(payload); await alert.executor(payload);
expect(alertServices.alertInstanceFactory).toHaveBeenCalled(); expect(alertServices.alertFactory.create).toHaveBeenCalled();
const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results; const [{ value: alertInstanceMock }] = alertServices.alertFactory.create.mock.results;
expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith( expect(alertInstanceMock.scheduleActions).toHaveBeenCalledWith(
'default', 'default',
expect.objectContaining({ expect.objectContaining({
@ -204,7 +204,7 @@ describe('legacyRules_notification_alert_type', () => {
); );
}); });
it('should not call alertInstanceFactory if signalsCount was 0', async () => { it('should not call alertFactory.create if signalsCount was 0', async () => {
const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); const ruleAlert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams());
alertServices.savedObjectsClient.get.mockResolvedValue({ alertServices.savedObjectsClient.get.mockResolvedValue({
id: 'id', id: 'id',
@ -218,7 +218,7 @@ describe('legacyRules_notification_alert_type', () => {
await alert.executor(payload); await alert.executor(payload);
expect(alertServices.alertInstanceFactory).not.toHaveBeenCalled(); expect(alertServices.alertFactory.create).not.toHaveBeenCalled();
}); });
it('should call scheduleActions if signalsCount was greater than 0', async () => { it('should call scheduleActions if signalsCount was greater than 0', async () => {
@ -237,9 +237,9 @@ describe('legacyRules_notification_alert_type', () => {
await alert.executor(payload); await alert.executor(payload);
expect(alertServices.alertInstanceFactory).toHaveBeenCalled(); expect(alertServices.alertFactory.create).toHaveBeenCalled();
const [{ value: alertInstanceMock }] = alertServices.alertInstanceFactory.mock.results; const [{ value: alertInstanceMock }] = alertServices.alertFactory.create.mock.results;
expect(alertInstanceMock.replaceState).toHaveBeenCalledWith( expect(alertInstanceMock.replaceState).toHaveBeenCalledWith(
expect.objectContaining({ signals_count: 100 }) expect.objectContaining({ signals_count: 100 })
); );

View file

@ -119,7 +119,7 @@ export const legacyRulesNotificationAlertType = ({
); );
if (signalsCount !== 0) { if (signalsCount !== 0) {
const alertInstance = services.alertInstanceFactory(alertId); const alertInstance = services.alertFactory.create(alertId);
scheduleNotificationActions({ scheduleNotificationActions({
alertInstance, alertInstance,
signalsCount, signalsCount,

View file

@ -54,7 +54,7 @@ describe('schedule_notification_actions', () => {
}; };
it('Should schedule actions with unflatted and legacy context', () => { it('Should schedule actions with unflatted and legacy context', () => {
const alertInstance = alertServices.alertInstanceFactory(alertId); const alertInstance = alertServices.alertFactory.create(alertId);
const signals = [sampleThresholdAlert._source, sampleThresholdAlert._source]; const signals = [sampleThresholdAlert._source, sampleThresholdAlert._source];
scheduleNotificationActions({ scheduleNotificationActions({
alertInstance, alertInstance,

View file

@ -6,7 +6,7 @@
*/ */
import { mapKeys, snakeCase } from 'lodash/fp'; import { mapKeys, snakeCase } from 'lodash/fp';
import { AlertInstance } from '../../../../../alerting/server'; import { Alert } from '../../../../../alerting/server';
import { expandDottedObject } from '../../../../common/utils/expand_dotted'; import { expandDottedObject } from '../../../../common/utils/expand_dotted';
import { RuleParams } from '../schemas/rule_schemas'; import { RuleParams } from '../schemas/rule_schemas';
import aadFieldConversion from '../routes/index/signal_aad_mapping.json'; import aadFieldConversion from '../routes/index/signal_aad_mapping.json';
@ -46,7 +46,7 @@ const formatAlertsForNotificationActions = (alerts: unknown[]): unknown[] => {
}; };
interface ScheduleNotificationActions { interface ScheduleNotificationActions {
alertInstance: AlertInstance; alertInstance: Alert;
signalsCount: number; signalsCount: number;
resultsLink: string; resultsLink: string;
ruleParams: NotificationRuleTypeParams; ruleParams: NotificationRuleTypeParams;
@ -59,7 +59,7 @@ export const scheduleNotificationActions = ({
resultsLink = '', resultsLink = '',
ruleParams, ruleParams,
signals, signals,
}: ScheduleNotificationActions): AlertInstance => }: ScheduleNotificationActions): Alert =>
alertInstance alertInstance
.replaceState({ .replaceState({
signals_count: signalsCount, signals_count: signalsCount,

View file

@ -82,7 +82,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [], signals: [],
@ -107,7 +107,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [ signals: [
@ -137,7 +137,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [], signals: [],
@ -166,7 +166,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [], signals: [],
@ -197,7 +197,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [], signals: [],
@ -235,7 +235,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [], signals: [],
@ -271,7 +271,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [], signals: [],
@ -313,7 +313,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [ signals: [
@ -375,7 +375,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [ signals: [
@ -435,7 +435,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [ signals: [
@ -497,7 +497,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [ signals: [
@ -559,7 +559,7 @@ describe('schedule_throttle_notification_actions', () => {
}, },
}) })
), ),
alertInstance: alertsMock.createAlertInstanceFactory(), alertInstance: alertsMock.createAlertFactory.create(),
notificationRuleParams, notificationRuleParams,
logger, logger,
signals: [ signals: [

View file

@ -7,7 +7,7 @@
import { ElasticsearchClient, SavedObject, Logger } from 'src/core/server'; import { ElasticsearchClient, SavedObject, Logger } from 'src/core/server';
import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils';
import { AlertInstance } from '../../../../../alerting/server'; import { Alert } from '../../../../../alerting/server';
import { RuleParams } from '../schemas/rule_schemas'; import { RuleParams } from '../schemas/rule_schemas';
import { deconflictSignalsAndResults, getNotificationResultsLink } from '../notifications/utils'; import { deconflictSignalsAndResults, getNotificationResultsLink } from '../notifications/utils';
import { DEFAULT_RULE_NOTIFICATION_QUERY_SIZE } from '../../../../common/constants'; import { DEFAULT_RULE_NOTIFICATION_QUERY_SIZE } from '../../../../common/constants';
@ -26,7 +26,7 @@ interface ScheduleThrottledNotificationActionsOptions {
outputIndex: RuleParams['outputIndex']; outputIndex: RuleParams['outputIndex'];
ruleId: RuleParams['ruleId']; ruleId: RuleParams['ruleId'];
esClient: ElasticsearchClient; esClient: ElasticsearchClient;
alertInstance: AlertInstance; alertInstance: Alert;
notificationRuleParams: NotificationRuleTypeParams; notificationRuleParams: NotificationRuleTypeParams;
signals: unknown[]; signals: unknown[];
logger: Logger; logger: Logger;

View file

@ -34,7 +34,7 @@ import {
} from '../../../../../../alerting/common'; } from '../../../../../../alerting/common';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths // eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { ExecutorType } from '../../../../../../alerting/server/types'; import { ExecutorType } from '../../../../../../alerting/server/types';
import { AlertInstance } from '../../../../../../alerting/server'; import { Alert } from '../../../../../../alerting/server';
import { ConfigType } from '../../../../config'; import { ConfigType } from '../../../../config';
import { alertInstanceFactoryStub } from '../../signals/preview/alert_instance_factory_stub'; import { alertInstanceFactoryStub } from '../../signals/preview/alert_instance_factory_stub';
import { CreateRuleOptions, CreateSecurityRuleTypeWrapperProps } from '../../rule_types/types'; import { CreateRuleOptions, CreateSecurityRuleTypeWrapperProps } from '../../rule_types/types';
@ -140,12 +140,14 @@ export const previewRulesRoute = async (
ruleTypeName: string, ruleTypeName: string,
params: TParams, params: TParams,
shouldWriteAlerts: () => boolean, shouldWriteAlerts: () => boolean,
alertInstanceFactory: ( alertFactory: {
create: (
id: string id: string
) => Pick< ) => Pick<
AlertInstance<TInstanceState, TInstanceContext, TActionGroupIds>, Alert<TInstanceState, TInstanceContext, TActionGroupIds>,
'getState' | 'replaceState' | 'scheduleActions' | 'scheduleActionsWithSubGroup' 'getState' | 'replaceState' | 'scheduleActions' | 'scheduleActionsWithSubGroup'
> >;
}
) => { ) => {
let statePreview = runState as TState; let statePreview = runState as TState;
@ -178,7 +180,7 @@ export const previewRulesRoute = async (
services: { services: {
shouldWriteAlerts, shouldWriteAlerts,
shouldStopExecution: () => false, shouldStopExecution: () => false,
alertInstanceFactory, alertFactory,
// Just use es client always for preview // Just use es client always for preview
search: context.core.elasticsearch.client, search: context.core.elasticsearch.client,
savedObjectsClient: context.core.savedObjects.client, savedObjectsClient: context.core.savedObjects.client,
@ -223,7 +225,7 @@ export const previewRulesRoute = async (
queryAlertType.name, queryAlertType.name,
previewRuleParams, previewRuleParams,
() => true, () => true,
alertInstanceFactoryStub { create: alertInstanceFactoryStub }
); );
break; break;
case 'threshold': case 'threshold':
@ -236,7 +238,7 @@ export const previewRulesRoute = async (
thresholdAlertType.name, thresholdAlertType.name,
previewRuleParams, previewRuleParams,
() => true, () => true,
alertInstanceFactoryStub { create: alertInstanceFactoryStub }
); );
break; break;
case 'threat_match': case 'threat_match':
@ -249,7 +251,7 @@ export const previewRulesRoute = async (
threatMatchAlertType.name, threatMatchAlertType.name,
previewRuleParams, previewRuleParams,
() => true, () => true,
alertInstanceFactoryStub { create: alertInstanceFactoryStub }
); );
break; break;
case 'eql': case 'eql':
@ -260,7 +262,7 @@ export const previewRulesRoute = async (
eqlAlertType.name, eqlAlertType.name,
previewRuleParams, previewRuleParams,
() => true, () => true,
alertInstanceFactoryStub { create: alertInstanceFactoryStub }
); );
break; break;
case 'machine_learning': case 'machine_learning':
@ -271,7 +273,7 @@ export const previewRulesRoute = async (
mlAlertType.name, mlAlertType.name,
previewRuleParams, previewRuleParams,
() => true, () => true,
alertInstanceFactoryStub { create: alertInstanceFactoryStub }
); );
break; break;
} }

View file

@ -76,7 +76,7 @@ export const createRuleTypeMocks = (
search: elasticsearchServiceMock.createScopedClusterClient(), search: elasticsearchServiceMock.createScopedClusterClient(),
savedObjectsClient: mockSavedObjectsClient, savedObjectsClient: mockSavedObjectsClient,
scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(), scopedClusterClient: elasticsearchServiceMock.createScopedClusterClient(),
alertInstanceFactory: jest.fn(() => ({ scheduleActions })), alertFactory: { create: jest.fn(() => ({ scheduleActions })) },
findAlerts: jest.fn(), // TODO: does this stay? findAlerts: jest.fn(), // TODO: does this stay?
alertWithPersistence: jest.fn(), alertWithPersistence: jest.fn(),
logger: loggerMock, logger: loggerMock,

View file

@ -315,7 +315,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
if (completeRule.ruleConfig.throttle != null) { if (completeRule.ruleConfig.throttle != null) {
await scheduleThrottledNotificationActions({ await scheduleThrottledNotificationActions({
alertInstance: services.alertInstanceFactory(alertId), alertInstance: services.alertFactory.create(alertId),
throttle: completeRule.ruleConfig.throttle ?? '', throttle: completeRule.ruleConfig.throttle ?? '',
startedAt, startedAt,
id: alertId, id: alertId,
@ -329,7 +329,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
logger, logger,
}); });
} else if (createdSignalsCount) { } else if (createdSignalsCount) {
const alertInstance = services.alertInstanceFactory(alertId); const alertInstance = services.alertFactory.create(alertId);
scheduleNotificationActions({ scheduleNotificationActions({
alertInstance, alertInstance,
signalsCount: createdSignalsCount, signalsCount: createdSignalsCount,
@ -371,7 +371,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
// NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early
if (completeRule.ruleConfig.throttle != null) { if (completeRule.ruleConfig.throttle != null) {
await scheduleThrottledNotificationActions({ await scheduleThrottledNotificationActions({
alertInstance: services.alertInstanceFactory(alertId), alertInstance: services.alertFactory.create(alertId),
throttle: completeRule.ruleConfig.throttle ?? '', throttle: completeRule.ruleConfig.throttle ?? '',
startedAt, startedAt,
id: completeRule.alertId, id: completeRule.alertId,
@ -403,7 +403,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper =
// NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early
if (completeRule.ruleConfig.throttle != null) { if (completeRule.ruleConfig.throttle != null) {
await scheduleThrottledNotificationActions({ await scheduleThrottledNotificationActions({
alertInstance: services.alertInstanceFactory(alertId), alertInstance: services.alertFactory.create(alertId),
throttle: completeRule.ruleConfig.throttle ?? '', throttle: completeRule.ruleConfig.throttle ?? '',
startedAt, startedAt,
id: completeRule.alertId, id: completeRule.alertId,

View file

@ -12,7 +12,7 @@ import {
AlertTypeState, AlertTypeState,
} from '../../../../../../alerting/common'; } from '../../../../../../alerting/common';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths // eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { AlertInstance } from '../../../../../../alerting/server/alert_instance'; import { Alert } from '../../../../../../alerting/server/alert';
export const alertInstanceFactoryStub = < export const alertInstanceFactoryStub = <
TParams extends RuleParams, TParams extends RuleParams,
@ -27,13 +27,13 @@ export const alertInstanceFactoryStub = <
return {} as unknown as TInstanceState; return {} as unknown as TInstanceState;
}, },
replaceState(state: TInstanceState) { replaceState(state: TInstanceState) {
return new AlertInstance<TInstanceState, TInstanceContext, TActionGroupIds>({ return new Alert<TInstanceState, TInstanceContext, TActionGroupIds>({
state: {} as TInstanceState, state: {} as TInstanceState,
meta: { lastScheduledActions: { group: 'default', date: new Date() } }, meta: { lastScheduledActions: { group: 'default', date: new Date() } },
}); });
}, },
scheduleActions(actionGroup: TActionGroupIds, alertcontext: TInstanceContext) { scheduleActions(actionGroup: TActionGroupIds, alertcontext: TInstanceContext) {
return new AlertInstance<TInstanceState, TInstanceContext, TActionGroupIds>({ return new Alert<TInstanceState, TInstanceContext, TActionGroupIds>({
state: {} as TInstanceState, state: {} as TInstanceState,
meta: { lastScheduledActions: { group: 'default', date: new Date() } }, meta: { lastScheduledActions: { group: 'default', date: new Date() } },
}); });
@ -43,7 +43,7 @@ export const alertInstanceFactoryStub = <
subgroup: string, subgroup: string,
alertcontext: TInstanceContext alertcontext: TInstanceContext
) { ) {
return new AlertInstance<TInstanceState, TInstanceContext, TActionGroupIds>({ return new Alert<TInstanceState, TInstanceContext, TActionGroupIds>({
state: {} as TInstanceState, state: {} as TInstanceState,
meta: { lastScheduledActions: { group: 'default', date: new Date() } }, meta: { lastScheduledActions: { group: 'default', date: new Date() } },
}); });

View file

@ -178,7 +178,7 @@ describe('alertType', () => {
}, },
}); });
expect(alertServices.alertInstanceFactory).not.toHaveBeenCalled(); expect(alertServices.alertFactory.create).not.toHaveBeenCalled();
expect(result).toMatchInlineSnapshot(` expect(result).toMatchInlineSnapshot(`
Object { Object {
@ -257,8 +257,8 @@ describe('alertType', () => {
}, },
}); });
expect(alertServices.alertInstanceFactory).toHaveBeenCalledWith(ConditionMetAlertInstanceId); expect(alertServices.alertFactory.create).toHaveBeenCalledWith(ConditionMetAlertInstanceId);
const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; const instance: AlertInstanceMock = alertServices.alertFactory.create.mock.results[0].value;
expect(instance.replaceState).toHaveBeenCalledWith({ expect(instance.replaceState).toHaveBeenCalledWith({
latestTimestamp: undefined, latestTimestamp: undefined,
dateStart: expect.any(String), dateStart: expect.any(String),
@ -328,7 +328,7 @@ describe('alertType', () => {
}, },
}); });
const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; const instance: AlertInstanceMock = alertServices.alertFactory.create.mock.results[0].value;
expect(instance.replaceState).toHaveBeenCalledWith({ expect(instance.replaceState).toHaveBeenCalledWith({
// ensure the invalid "latestTimestamp" in the state is stored as an ISO string going forward // ensure the invalid "latestTimestamp" in the state is stored as an ISO string going forward
latestTimestamp: new Date(previousTimestamp).toISOString(), latestTimestamp: new Date(previousTimestamp).toISOString(),
@ -410,7 +410,7 @@ describe('alertType', () => {
}, },
}); });
const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; const instance: AlertInstanceMock = alertServices.alertFactory.create.mock.results[0].value;
expect(instance.replaceState).toHaveBeenCalledWith({ expect(instance.replaceState).toHaveBeenCalledWith({
latestTimestamp: undefined, latestTimestamp: undefined,
dateStart: expect.any(String), dateStart: expect.any(String),
@ -488,7 +488,7 @@ describe('alertType', () => {
}; };
const result = await alertType.executor(executorOptions); const result = await alertType.executor(executorOptions);
const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; const instance: AlertInstanceMock = alertServices.alertFactory.create.mock.results[0].value;
expect(instance.replaceState).toHaveBeenCalledWith({ expect(instance.replaceState).toHaveBeenCalledWith({
latestTimestamp: undefined, latestTimestamp: undefined,
dateStart: expect.any(String), dateStart: expect.any(String),
@ -518,7 +518,7 @@ describe('alertType', () => {
state: result as EsQueryAlertState, state: result as EsQueryAlertState,
}); });
const existingInstance: AlertInstanceMock = const existingInstance: AlertInstanceMock =
alertServices.alertInstanceFactory.mock.results[1].value; alertServices.alertFactory.create.mock.results[1].value;
expect(existingInstance.replaceState).toHaveBeenCalledWith({ expect(existingInstance.replaceState).toHaveBeenCalledWith({
latestTimestamp: new Date(oldestDocumentTimestamp).toISOString(), latestTimestamp: new Date(oldestDocumentTimestamp).toISOString(),
dateStart: expect.any(String), dateStart: expect.any(String),
@ -601,7 +601,7 @@ describe('alertType', () => {
}, },
}); });
const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; const instance: AlertInstanceMock = alertServices.alertFactory.create.mock.results[0].value;
expect(instance.replaceState).toHaveBeenCalledWith({ expect(instance.replaceState).toHaveBeenCalledWith({
latestTimestamp: undefined, latestTimestamp: undefined,
dateStart: expect.any(String), dateStart: expect.any(String),
@ -685,7 +685,7 @@ describe('alertType', () => {
}, },
}); });
const instance: AlertInstanceMock = alertServices.alertInstanceFactory.mock.results[0].value; const instance: AlertInstanceMock = alertServices.alertFactory.create.mock.results[0].value;
expect(instance.replaceState).toHaveBeenCalledWith({ expect(instance.replaceState).toHaveBeenCalledWith({
latestTimestamp: undefined, latestTimestamp: undefined,
dateStart: expect.any(String), dateStart: expect.any(String),

View file

@ -160,7 +160,7 @@ export function getAlertType(logger: Logger): RuleType<
> >
) { ) {
const { alertId, name, services, params, state } = options; const { alertId, name, services, params, state } = options;
const { alertInstanceFactory, search } = services; const { alertFactory, search } = services;
const previousTimestamp = state.latestTimestamp; const previousTimestamp = state.latestTimestamp;
const abortableEsClient = search.asCurrentUser; const abortableEsClient = search.asCurrentUser;
@ -255,7 +255,7 @@ export function getAlertType(logger: Logger): RuleType<
}; };
const actionContext = addMessages(options, baseContext, params); const actionContext = addMessages(options, baseContext, params);
const alertInstance = alertInstanceFactory(ConditionMetAlertInstanceId); const alertInstance = alertFactory.create(ConditionMetAlertInstanceId);
alertInstance alertInstance
// store the params we would need to recreate the query that led to this alert instance // store the params we would need to recreate the query that led to this alert instance
.replaceState({ latestTimestamp: timestamp, dateStart, dateEnd }) .replaceState({ latestTimestamp: timestamp, dateStart, dateEnd })

View file

@ -87,11 +87,11 @@ export function transformResults(
export function getActiveEntriesAndGenerateAlerts( export function getActiveEntriesAndGenerateAlerts(
prevLocationMap: Map<string, LatestEntityLocation[]>, prevLocationMap: Map<string, LatestEntityLocation[]>,
currLocationMap: Map<string, LatestEntityLocation[]>, currLocationMap: Map<string, LatestEntityLocation[]>,
alertInstanceFactory: AlertServices< alertFactory: AlertServices<
GeoContainmentInstanceState, GeoContainmentInstanceState,
GeoContainmentInstanceContext, GeoContainmentInstanceContext,
typeof ActionGroupId typeof ActionGroupId
>['alertInstanceFactory'], >['alertFactory'],
shapesIdsNamesMap: Record<string, unknown>, shapesIdsNamesMap: Record<string, unknown>,
currIntervalEndTime: Date currIntervalEndTime: Date
) { ) {
@ -113,7 +113,7 @@ export function getActiveEntriesAndGenerateAlerts(
}; };
const alertInstanceId = `${entityName}-${context.containingBoundaryName}`; const alertInstanceId = `${entityName}-${context.containingBoundaryName}`;
if (shapeLocationId !== OTHER_CATEGORY) { if (shapeLocationId !== OTHER_CATEGORY) {
alertInstanceFactory(alertInstanceId).scheduleActions(ActionGroupId, context); alertFactory.create(alertInstanceId).scheduleActions(ActionGroupId, context);
} }
}); });
@ -189,7 +189,7 @@ export const getGeoContainmentExecutor = (log: Logger): GeoContainmentAlertType[
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
prevLocationMap, prevLocationMap,
currLocationMap, currLocationMap,
services.alertInstanceFactory, services.alertFactory,
shapesIdsNamesMap, shapesIdsNamesMap,
currIntervalEndTime currIntervalEndTime
); );

View file

@ -20,9 +20,9 @@ import { OTHER_CATEGORY } from '../es_query_builder';
import { GeoContainmentInstanceContext, GeoContainmentInstanceState } from '../alert_type'; import { GeoContainmentInstanceContext, GeoContainmentInstanceState } from '../alert_type';
import type { GeoContainmentParams } from '../alert_type'; import type { GeoContainmentParams } from '../alert_type';
const alertInstanceFactory = const alertFactory = (contextKeys: unknown[], testAlertActionArr: unknown[]) => ({
(contextKeys: unknown[], testAlertActionArr: unknown[]) => (instanceId: string) => { create: (instanceId: string) => {
const alertInstance = alertsMock.createAlertInstanceFactory< const alertInstance = alertsMock.createAlertFactory.create<
GeoContainmentInstanceState, GeoContainmentInstanceState,
GeoContainmentInstanceContext GeoContainmentInstanceContext
>(); >();
@ -39,7 +39,8 @@ const alertInstanceFactory =
} }
); );
return alertInstance; return alertInstance;
}; },
});
describe('geo_containment', () => { describe('geo_containment', () => {
describe('transformResults', () => { describe('transformResults', () => {
@ -253,7 +254,7 @@ describe('geo_containment', () => {
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
emptyPrevLocationMap, emptyPrevLocationMap,
currLocationMap, currLocationMap,
alertInstanceFactory(contextKeys, testAlertActionArr), alertFactory(contextKeys, testAlertActionArr),
emptyShapesIdsNamesMap, emptyShapesIdsNamesMap,
currentDateTime currentDateTime
); );
@ -278,7 +279,7 @@ describe('geo_containment', () => {
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
prevLocationMapWithIdenticalEntityEntry, prevLocationMapWithIdenticalEntityEntry,
currLocationMap, currLocationMap,
alertInstanceFactory(contextKeys, testAlertActionArr), alertFactory(contextKeys, testAlertActionArr),
emptyShapesIdsNamesMap, emptyShapesIdsNamesMap,
currentDateTime currentDateTime
); );
@ -317,7 +318,7 @@ describe('geo_containment', () => {
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
prevLocationMapWithNonIdenticalEntityEntry, prevLocationMapWithNonIdenticalEntityEntry,
currLocationMap, currLocationMap,
alertInstanceFactory(contextKeys, testAlertActionArr), alertFactory(contextKeys, testAlertActionArr),
emptyShapesIdsNamesMap, emptyShapesIdsNamesMap,
currentDateTime currentDateTime
); );
@ -340,7 +341,7 @@ describe('geo_containment', () => {
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
emptyPrevLocationMap, emptyPrevLocationMap,
currLocationMapWithOther, currLocationMapWithOther,
alertInstanceFactory(contextKeys, testAlertActionArr), alertFactory(contextKeys, testAlertActionArr),
emptyShapesIdsNamesMap, emptyShapesIdsNamesMap,
currentDateTime currentDateTime
); );
@ -373,7 +374,7 @@ describe('geo_containment', () => {
getActiveEntriesAndGenerateAlerts( getActiveEntriesAndGenerateAlerts(
emptyPrevLocationMap, emptyPrevLocationMap,
currLocationMapWithThreeMore, currLocationMapWithThreeMore,
alertInstanceFactory(contextKeys, testAlertActionArr), alertFactory(contextKeys, testAlertActionArr),
emptyShapesIdsNamesMap, emptyShapesIdsNamesMap,
currentDateTime currentDateTime
); );
@ -410,7 +411,7 @@ describe('geo_containment', () => {
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
emptyPrevLocationMap, emptyPrevLocationMap,
currLocationMapWithOther, currLocationMapWithOther,
alertInstanceFactory(contextKeys, testAlertActionArr), alertFactory(contextKeys, testAlertActionArr),
emptyShapesIdsNamesMap, emptyShapesIdsNamesMap,
currentDateTime currentDateTime
); );
@ -442,7 +443,7 @@ describe('geo_containment', () => {
const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts( const allActiveEntriesMap = getActiveEntriesAndGenerateAlerts(
emptyPrevLocationMap, emptyPrevLocationMap,
currLocationMapWithOther, currLocationMapWithOther,
alertInstanceFactory(contextKeys, testAlertActionArr), alertFactory(contextKeys, testAlertActionArr),
emptyShapesIdsNamesMap, emptyShapesIdsNamesMap,
currentDateTime currentDateTime
); );
@ -514,7 +515,7 @@ describe('geo_containment', () => {
const alertServicesWithSearchMock: AlertServicesMock = { const alertServicesWithSearchMock: AlertServicesMock = {
...alertsMock.createAlertServices(), ...alertsMock.createAlertServices(),
// @ts-ignore // @ts-ignore
alertInstanceFactory: alertInstanceFactory(contextKeys, testAlertActionArr), alertFactory: alertFactory(contextKeys, testAlertActionArr),
scopedClusterClient: { scopedClusterClient: {
asCurrentUser: { asCurrentUser: {
// @ts-ignore // @ts-ignore
@ -538,6 +539,7 @@ describe('geo_containment', () => {
it('should query for shapes if state does not contain shapes', async () => { it('should query for shapes if state does not contain shapes', async () => {
const executor = await getGeoContainmentExecutor(mockLogger); const executor = await getGeoContainmentExecutor(mockLogger);
// @ts-ignore
const executionResult = await executor({ const executionResult = await executor({
previousStartedAt, previousStartedAt,
startedAt, startedAt,
@ -557,6 +559,7 @@ describe('geo_containment', () => {
it('should not query for shapes if state contains shapes', async () => { it('should not query for shapes if state contains shapes', async () => {
const executor = await getGeoContainmentExecutor(mockLogger); const executor = await getGeoContainmentExecutor(mockLogger);
// @ts-ignore
const executionResult = await executor({ const executionResult = await executor({
previousStartedAt, previousStartedAt,
startedAt, startedAt,
@ -575,6 +578,7 @@ describe('geo_containment', () => {
it('should carry through shapes filters in state to next call unmodified', async () => { it('should carry through shapes filters in state to next call unmodified', async () => {
const executor = await getGeoContainmentExecutor(mockLogger); const executor = await getGeoContainmentExecutor(mockLogger);
// @ts-ignore
const executionResult = await executor({ const executionResult = await executor({
previousStartedAt, previousStartedAt,
startedAt, startedAt,
@ -610,6 +614,7 @@ describe('geo_containment', () => {
], ],
}; };
const executor = await getGeoContainmentExecutor(mockLogger); const executor = await getGeoContainmentExecutor(mockLogger);
// @ts-ignore
const executionResult = await executor({ const executionResult = await executor({
previousStartedAt, previousStartedAt,
startedAt, startedAt,

View file

@ -203,7 +203,7 @@ describe('alertType', () => {
}, },
}); });
expect(alertServices.alertInstanceFactory).toHaveBeenCalledWith('all documents'); expect(alertServices.alertFactory.create).toHaveBeenCalledWith('all documents');
}); });
it('should ensure a null result does not fire actions', async () => { it('should ensure a null result does not fire actions', async () => {
@ -269,7 +269,7 @@ describe('alertType', () => {
}, },
}); });
expect(customAlertServices.alertInstanceFactory).not.toHaveBeenCalled(); expect(customAlertServices.alertFactory.create).not.toHaveBeenCalled();
}); });
it('should ensure an undefined result does not fire actions', async () => { it('should ensure an undefined result does not fire actions', async () => {
@ -335,6 +335,6 @@ describe('alertType', () => {
}, },
}); });
expect(customAlertServices.alertInstanceFactory).not.toHaveBeenCalled(); expect(customAlertServices.alertFactory.create).not.toHaveBeenCalled();
}); });
}); });

View file

@ -134,7 +134,7 @@ export function getAlertType(
options: AlertExecutorOptions<Params, {}, {}, ActionContext, typeof ActionGroupId> options: AlertExecutorOptions<Params, {}, {}, ActionContext, typeof ActionGroupId>
) { ) {
const { alertId, name, services, params } = options; const { alertId, name, services, params } = options;
const { alertInstanceFactory, search } = services; const { alertFactory, search } = services;
const compareFn = ComparatorFns.get(params.thresholdComparator); const compareFn = ComparatorFns.get(params.thresholdComparator);
if (compareFn == null) { if (compareFn == null) {
@ -208,7 +208,7 @@ export function getAlertType(
conditions: humanFn, conditions: humanFn,
}; };
const actionContext = addMessages(options, baseContext, params); const actionContext = addMessages(options, baseContext, params);
const alertInstance = alertInstanceFactory(instanceId); const alertInstance = alertFactory.create(instanceId);
alertInstance.scheduleActions(ActionGroupId, actionContext); alertInstance.scheduleActions(ActionGroupId, actionContext);
logger.debug(`scheduled actionGroup: ${JSON.stringify(actionContext)}`); logger.debug(`scheduled actionGroup: ${JSON.stringify(actionContext)}`);
} }

View file

@ -100,7 +100,7 @@ export function getTransformHealthRuleType(): RuleType<
isExportable: true, isExportable: true,
async executor(options) { async executor(options) {
const { const {
services: { scopedClusterClient, alertInstanceFactory }, services: { scopedClusterClient, alertFactory },
params, params,
} = options; } = options;
@ -112,7 +112,7 @@ export function getTransformHealthRuleType(): RuleType<
if (executionResult.length > 0) { if (executionResult.length > 0) {
executionResult.forEach(({ name: alertInstanceName, context }) => { executionResult.forEach(({ name: alertInstanceName, context }) => {
const alertInstance = alertInstanceFactory(alertInstanceName); const alertInstance = alertFactory.create(alertInstanceName);
alertInstance.scheduleActions(TRANSFORM_ISSUE, context); alertInstance.scheduleActions(TRANSFORM_ISSUE, context);
}); });
} }

View file

@ -16,7 +16,7 @@ import { commonStateTranslations, tlsTranslations } from './translations';
import { ActionGroupIdsOf } from '../../../../alerting/common'; import { ActionGroupIdsOf } from '../../../../alerting/common';
import { AlertInstanceContext } from '../../../../alerting/common'; import { AlertInstanceContext } from '../../../../alerting/common';
import { AlertInstance } from '../../../../alerting/server'; import { Alert } from '../../../../alerting/server';
import { savedObjectsAdapter } from '../saved_objects/saved_objects'; import { savedObjectsAdapter } from '../saved_objects/saved_objects';
import { createUptimeESClient } from '../lib'; import { createUptimeESClient } from '../lib';
@ -28,7 +28,7 @@ import {
export type ActionGroupIds = ActionGroupIdsOf<typeof TLS_LEGACY>; export type ActionGroupIds = ActionGroupIdsOf<typeof TLS_LEGACY>;
type TLSAlertInstance = AlertInstance<Record<string, any>, AlertInstanceContext, ActionGroupIds>; type TLSAlertInstance = Alert<Record<string, any>, AlertInstanceContext, ActionGroupIds>;
interface TlsAlertState { interface TlsAlertState {
count: number; count: number;
@ -113,10 +113,7 @@ export const tlsLegacyAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (_s
}, },
isExportable: true, isExportable: true,
minimumLicenseRequired: 'basic', minimumLicenseRequired: 'basic',
async executor({ async executor({ services: { alertFactory, scopedClusterClient, savedObjectsClient }, state }) {
services: { alertInstanceFactory, scopedClusterClient, savedObjectsClient },
state,
}) {
const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient);
const uptimeEsClient = createUptimeESClient({ const uptimeEsClient = createUptimeESClient({
@ -156,7 +153,7 @@ export const tlsLegacyAlertFactory: UptimeAlertTypeFactory<ActionGroupIds> = (_s
'd' 'd'
) )
.valueOf(); .valueOf();
const alertInstance: TLSAlertInstance = alertInstanceFactory(TLS_LEGACY.id); const alertInstance: TLSAlertInstance = alertFactory.create(TLS_LEGACY.id);
const summary = getCertSummary(certs, absoluteExpirationThreshold, absoluteAgeThreshold); const summary = getCertSummary(certs, absoluteExpirationThreshold, absoluteAgeThreshold);
alertInstance.replaceState({ alertInstance.replaceState({
...updateState(state, foundCerts), ...updateState(state, foundCerts),

View file

@ -122,7 +122,7 @@ async function alwaysFiringExecutor(alertExecutorOptions: any) {
} }
if (group) { if (group) {
const instance = services.alertInstanceFactory('1').replaceState({ instanceStateValue: true }); const instance = services.alertFactory.create('1').replaceState({ instanceStateValue: true });
if (subgroup) { if (subgroup) {
instance.scheduleActionsWithSubGroup(group, subgroup, { instance.scheduleActionsWithSubGroup(group, subgroup, {
@ -177,8 +177,8 @@ function getCumulativeFiringAlertType() {
const runCount = (state.runCount || 0) + 1; const runCount = (state.runCount || 0) + 1;
times(runCount, (index) => { times(runCount, (index) => {
services services.alertFactory
.alertInstanceFactory(`instance-${index}`) .create(`instance-${index}`)
.replaceState({ instanceStateValue: true }) .replaceState({ instanceStateValue: true })
.scheduleActions(group); .scheduleActions(group);
}); });
@ -446,13 +446,13 @@ function getPatternFiringAlertType() {
for (const [instanceId, instancePattern] of Object.entries(pattern)) { for (const [instanceId, instancePattern] of Object.entries(pattern)) {
const scheduleByPattern = instancePattern[patternIndex]; const scheduleByPattern = instancePattern[patternIndex];
if (scheduleByPattern === true) { if (scheduleByPattern === true) {
services.alertInstanceFactory(instanceId).scheduleActions('default', { services.alertFactory.create(instanceId).scheduleActions('default', {
...EscapableStrings, ...EscapableStrings,
deep: DeepContextVariables, deep: DeepContextVariables,
}); });
} else if (typeof scheduleByPattern === 'string') { } else if (typeof scheduleByPattern === 'string') {
services services.alertFactory
.alertInstanceFactory(instanceId) .create(instanceId)
.scheduleActionsWithSubGroup('default', scheduleByPattern); .scheduleActionsWithSubGroup('default', scheduleByPattern);
} }
} }
@ -538,7 +538,7 @@ function getLongRunningPatternRuleType(cancelAlertsOnRuleTimeout: boolean = true
return {}; return {};
} }
services.alertInstanceFactory('alert').scheduleActions('default', {}); services.alertFactory.create('alert').scheduleActions('default', {});
// run long if pattern says to // run long if pattern says to
if (pattern[globalPatternIndex++] === true) { if (pattern[globalPatternIndex++] === true) {

View file

@ -54,8 +54,8 @@ export const alwaysFiringAlertType: RuleType<
const { services, state, params } = alertExecutorOptions; const { services, state, params } = alertExecutorOptions;
(params.instances || []).forEach((instance: { id: string; state: any }) => { (params.instances || []).forEach((instance: { id: string; state: any }) => {
services services.alertFactory
.alertInstanceFactory(instance.id) .create(instance.id)
.replaceState({ instanceStateValue: true, ...(instance.state || {}) }) .replaceState({ instanceStateValue: true, ...(instance.state || {}) })
.scheduleActions('default'); .scheduleActions('default');
}); });

View file

@ -177,7 +177,7 @@ export default function createLifecycleExecutorApiTest({ getService }: FtrProvid
producer: 'observability.test', producer: 'observability.test',
}, },
services: { services: {
alertInstanceFactory: sinon.stub(), alertFactory: { create: sinon.stub() },
shouldWriteAlerts: sinon.stub().returns(true), shouldWriteAlerts: sinon.stub().returns(true),
}, },
} as unknown as RuleExecutorOptions< } as unknown as RuleExecutorOptions<