From b99ca969e02dc0f67835cee4dfcabc505e7c7395 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 6 Jan 2021 10:38:33 +0000 Subject: [PATCH] [Alerting] revert the revert of `Enforces typing of Alert's ActionGroups` (#87382) The https://github.com/elastic/kibana/pull/86761 PR was reverted due to a small typing issue. This PR reverts that revert and adds a commit to address the issue: https://github.com/elastic/kibana/pull/87382/commits/9e4ab2002c640e7aefbd00ce9e79d3edd48796b2. --- .../alerting_example/common/constants.ts | 13 +- .../public/alert_types/always_firing.tsx | 6 +- .../server/alert_types/always_firing.ts | 4 +- .../server/alert_types/astros.ts | 5 +- x-pack/plugins/alerts/README.md | 41 +++- x-pack/plugins/alerts/common/alert_type.ts | 22 +- .../alerts/common/builtin_action_groups.ts | 22 +- .../alert_instance/alert_instance.test.ts | 205 +++++++++++++++--- .../server/alert_instance/alert_instance.ts | 26 ++- .../create_alert_instance_factory.ts | 9 +- .../alerts/server/alert_type_registry.test.ts | 39 +++- .../alerts/server/alert_type_registry.ts | 119 ++++++++-- x-pack/plugins/alerts/server/index.ts | 1 + .../alerts/server/lib/license_state.test.ts | 4 +- .../alerts/server/lib/license_state.ts | 26 ++- x-pack/plugins/alerts/server/plugin.test.ts | 2 +- x-pack/plugins/alerts/server/plugin.ts | 28 ++- .../create_execution_handler.test.ts | 19 +- .../task_runner/create_execution_handler.ts | 36 ++- .../server/task_runner/task_runner.test.ts | 36 ++- .../alerts/server/task_runner/task_runner.ts | 93 ++++++-- .../server/task_runner/task_runner_factory.ts | 40 +++- x-pack/plugins/alerts/server/types.ts | 40 +++- x-pack/plugins/apm/common/alert_types.ts | 26 ++- .../server/lib/alerts/alerting_es_client.ts | 13 +- .../alerts/register_error_count_alert_type.ts | 23 +- .../inventory_metric_threshold_executor.ts | 41 ++-- ...r_inventory_metric_threshold_alert_type.ts | 10 +- .../log_threshold/log_threshold_executor.ts | 21 +- .../register_log_threshold_alert_type.ts | 17 +- .../register_metric_threshold_alert_type.ts | 7 +- .../monitoring/server/alerts/base_alert.ts | 17 +- .../detection_engine/notifications/types.ts | 2 +- .../signals/bulk_create_ml_signals.ts | 8 +- .../signals/bulk_create_threshold_signals.ts | 8 +- .../signals/find_threshold_signals.ts | 8 +- .../detection_engine/signals/get_filter.ts | 8 +- .../signals/get_input_output_index.ts | 8 +- .../signals/siem_rule_action_groups.ts | 3 +- .../signals/single_bulk_create.ts | 10 +- .../signals/single_search_after.ts | 8 +- .../signals/threat_mapping/types.ts | 10 +- .../threshold_find_previous_signals.ts | 8 +- .../signals/threshold_get_bucket_filters.ts | 8 +- .../lib/detection_engine/signals/types.ts | 13 +- .../lib/detection_engine/signals/utils.ts | 14 +- .../alert_types/geo_containment/alert_type.ts | 7 +- .../geo_containment/geo_containment.ts | 3 +- .../alert_types/geo_containment/index.ts | 6 +- .../alert_types/geo_threshold/alert_type.ts | 3 +- .../alert_types/index_threshold/alert_type.ts | 6 +- .../action_connector_form/action_form.tsx | 2 +- .../components/alert_details.test.tsx | 3 +- .../components/alert_instances.tsx | 2 +- .../alert_form/alert_conditions.test.tsx | 12 +- .../sections/alert_form/alert_conditions.tsx | 21 +- .../alert_form/alert_conditions_group.tsx | 4 +- .../triggers_actions_ui/public/types.ts | 13 +- .../plugins/uptime/common/constants/alerts.ts | 13 +- .../lib/alerts/__tests__/status_check.test.ts | 8 +- .../server/lib/alerts/duration_anomaly.ts | 10 +- .../plugins/uptime/server/lib/alerts/index.ts | 19 +- .../uptime/server/lib/alerts/status_check.ts | 6 +- .../plugins/uptime/server/lib/alerts/tls.ts | 6 +- .../plugins/uptime/server/lib/alerts/types.ts | 5 +- .../server/lib/alerts/uptime_alert_wrapper.ts | 14 +- .../plugins/alerts/server/alert_types.ts | 32 +-- .../alerts_restricted/server/alert_types.ts | 10 +- .../fixtures/plugins/alerts/server/plugin.ts | 8 +- 69 files changed, 1011 insertions(+), 329 deletions(-) diff --git a/x-pack/examples/alerting_example/common/constants.ts b/x-pack/examples/alerting_example/common/constants.ts index 8e4ea4faf014c..721b8cb82f65f 100644 --- a/x-pack/examples/alerting_example/common/constants.ts +++ b/x-pack/examples/alerting_example/common/constants.ts @@ -10,15 +10,16 @@ export const ALERTING_EXAMPLE_APP_ID = 'AlertingExample'; // always firing export const DEFAULT_INSTANCES_TO_GENERATE = 5; +export interface AlwaysFiringThresholds { + small?: number; + medium?: number; + large?: number; +} export interface AlwaysFiringParams extends AlertTypeParams { instances?: number; - thresholds?: { - small?: number; - medium?: number; - large?: number; - }; + thresholds?: AlwaysFiringThresholds; } -export type AlwaysFiringActionGroupIds = keyof AlwaysFiringParams['thresholds']; +export type AlwaysFiringActionGroupIds = keyof AlwaysFiringThresholds; // Astros export enum Craft { diff --git a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx index cee7ee62e3210..42ce6df6d1a6f 100644 --- a/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx +++ b/x-pack/examples/alerting_example/public/alert_types/always_firing.tsx @@ -133,8 +133,10 @@ export const AlwaysFiringExpression: React.FunctionComponent< }; interface TShirtSelectorProps { - actionGroup?: ActionGroupWithCondition; - setTShirtThreshold: (actionGroup: ActionGroupWithCondition) => void; + actionGroup?: ActionGroupWithCondition; + setTShirtThreshold: ( + actionGroup: ActionGroupWithCondition + ) => void; } const TShirtSelector = ({ actionGroup, setTShirtThreshold }: TShirtSelectorProps) => { const [isOpen, setIsOpen] = useState(false); diff --git a/x-pack/examples/alerting_example/server/alert_types/always_firing.ts b/x-pack/examples/alerting_example/server/alert_types/always_firing.ts index 4fde4183b414e..fc837fee08b6f 100644 --- a/x-pack/examples/alerting_example/server/alert_types/always_firing.ts +++ b/x-pack/examples/alerting_example/server/alert_types/always_firing.ts @@ -11,6 +11,7 @@ import { DEFAULT_INSTANCES_TO_GENERATE, ALERTING_EXAMPLE_APP_ID, AlwaysFiringParams, + AlwaysFiringActionGroupIds, } from '../../common/constants'; type ActionGroups = 'small' | 'medium' | 'large'; @@ -39,7 +40,8 @@ export const alertType: AlertType< AlwaysFiringParams, { count?: number }, { triggerdOnCycle: number }, - never + never, + AlwaysFiringActionGroupIds > = { id: 'example.always-firing', name: 'Always firing', diff --git a/x-pack/examples/alerting_example/server/alert_types/astros.ts b/x-pack/examples/alerting_example/server/alert_types/astros.ts index 22c2f25c410cd..938baa8b317ba 100644 --- a/x-pack/examples/alerting_example/server/alert_types/astros.ts +++ b/x-pack/examples/alerting_example/server/alert_types/astros.ts @@ -41,7 +41,10 @@ function getCraftFilter(craft: string) { export const alertType: AlertType< { outerSpaceCapacity: number; craft: string; op: string }, { peopleInSpace: number }, - { craft: string } + { craft: string }, + never, + 'default', + 'hasLandedBackOnEarth' > = { id: 'example.people-in-space', name: 'People In Space Right Now', diff --git a/x-pack/plugins/alerts/README.md b/x-pack/plugins/alerts/README.md index 39dc23c7bbb73..2191b23eec11e 100644 --- a/x-pack/plugins/alerts/README.md +++ b/x-pack/plugins/alerts/README.md @@ -142,8 +142,41 @@ This example receives server and threshold as parameters. It will read the CPU u ```typescript import { schema } from '@kbn/config-schema'; +import { + Alert, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +} from 'x-pack/plugins/alerts/common'; ... -server.newPlatform.setup.plugins.alerts.registerType({ +interface MyAlertTypeParams extends AlertTypeParams { + server: string; + threshold: number; +} + +interface MyAlertTypeState extends AlertTypeState { + lastChecked: number; +} + +interface MyAlertTypeInstanceState extends AlertInstanceState { + cpuUsage: number; +} + +interface MyAlertTypeInstanceContext extends AlertInstanceContext { + server: string; + hasCpuUsageIncreased: boolean; +} + +type MyAlertTypeActionGroups = 'default' | 'warning'; + +const myAlertType: AlertType< + MyAlertTypeParams, + MyAlertTypeState, + MyAlertTypeInstanceState, + MyAlertTypeInstanceContext, + MyAlertTypeActionGroups +> = { id: 'my-alert-type', name: 'My alert type', validate: { @@ -180,7 +213,7 @@ server.newPlatform.setup.plugins.alerts.registerType({ services, params, state, - }: AlertExecutorOptions) { + }: AlertExecutorOptions) { // Let's assume params is { server: 'server_1', threshold: 0.8 } const { server, threshold } = params; @@ -219,7 +252,9 @@ server.newPlatform.setup.plugins.alerts.registerType({ }; }, producer: 'alerting', -}); +}; + +server.newPlatform.setup.plugins.alerts.registerType(myAlertType); ``` This example only receives threshold as a parameter. It will read the CPU usage of all the servers and schedule individual actions if the reading for a server is greater than the threshold. This is a better implementation than above as only one query is performed for all the servers instead of one query per server. diff --git a/x-pack/plugins/alerts/common/alert_type.ts b/x-pack/plugins/alerts/common/alert_type.ts index 4ab3ddc7ca810..d10d2467516cc 100644 --- a/x-pack/plugins/alerts/common/alert_type.ts +++ b/x-pack/plugins/alerts/common/alert_type.ts @@ -5,19 +5,29 @@ */ import { LicenseType } from '../../licensing/common/types'; +import { RecoveredActionGroupId, DefaultActionGroupId } from './builtin_action_groups'; -export interface AlertType { +export interface AlertType< + ActionGroupIds extends Exclude = DefaultActionGroupId, + RecoveryActionGroupId extends string = RecoveredActionGroupId +> { id: string; name: string; - actionGroups: ActionGroup[]; - recoveryActionGroup: ActionGroup; + actionGroups: Array>; + recoveryActionGroup: ActionGroup; actionVariables: string[]; - defaultActionGroupId: ActionGroup['id']; + defaultActionGroupId: ActionGroupIds; producer: string; minimumLicenseRequired: LicenseType; } -export interface ActionGroup { - id: string; +export interface ActionGroup { + id: ActionGroupIds; name: string; } + +export type ActionGroupIdsOf = T extends ActionGroup + ? groups + : T extends Readonly> + ? groups + : never; diff --git a/x-pack/plugins/alerts/common/builtin_action_groups.ts b/x-pack/plugins/alerts/common/builtin_action_groups.ts index e23bbcc54b24d..f2b7ec855b86e 100644 --- a/x-pack/plugins/alerts/common/builtin_action_groups.ts +++ b/x-pack/plugins/alerts/common/builtin_action_groups.ts @@ -6,13 +6,27 @@ import { i18n } from '@kbn/i18n'; import { ActionGroup } from './alert_type'; -export const RecoveredActionGroup: Readonly = { +export type DefaultActionGroupId = 'default'; + +export type RecoveredActionGroupId = typeof RecoveredActionGroup['id']; +export const RecoveredActionGroup: Readonly> = Object.freeze({ id: 'recovered', name: i18n.translate('xpack.alerts.builtinActionGroups.recovered', { defaultMessage: 'Recovered', }), -}; +}); + +export type ReservedActionGroups = + | RecoveryActionGroupId + | RecoveredActionGroupId; + +export type WithoutReservedActionGroups< + ActionGroupIds extends string, + RecoveryActionGroupId extends string +> = ActionGroupIds extends ReservedActionGroups ? never : ActionGroupIds; -export function getBuiltinActionGroups(customRecoveryGroup?: ActionGroup): ActionGroup[] { - return [customRecoveryGroup ?? Object.freeze(RecoveredActionGroup)]; +export function getBuiltinActionGroups( + customRecoveryGroup?: ActionGroup +): [ActionGroup>] { + return [customRecoveryGroup ?? RecoveredActionGroup]; } diff --git a/x-pack/plugins/alerts/server/alert_instance/alert_instance.test.ts b/x-pack/plugins/alerts/server/alert_instance/alert_instance.test.ts index b428f6c1a9134..1bd08fc3ac32d 100644 --- a/x-pack/plugins/alerts/server/alert_instance/alert_instance.test.ts +++ b/x-pack/plugins/alerts/server/alert_instance/alert_instance.test.ts @@ -6,6 +6,7 @@ import sinon from 'sinon'; import { AlertInstance } from './alert_instance'; +import { AlertInstanceState, AlertInstanceContext, DefaultActionGroupId } from '../../common'; let clock: sinon.SinonFakeTimers; @@ -17,12 +18,20 @@ afterAll(() => clock.restore()); describe('hasScheduledActions()', () => { test('defaults to false', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); expect(alertInstance.hasScheduledActions()).toEqual(false); }); test('returns true when scheduleActions is called', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); alertInstance.scheduleActions('default'); expect(alertInstance.hasScheduledActions()).toEqual(true); }); @@ -30,7 +39,11 @@ describe('hasScheduledActions()', () => { describe('isThrottled', () => { test(`should throttle when group didn't change and throttle period is still active`, () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ meta: { lastScheduledActions: { date: new Date(), @@ -44,7 +57,11 @@ describe('isThrottled', () => { }); test(`shouldn't throttle when group didn't change and throttle period expired`, () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ meta: { lastScheduledActions: { date: new Date(), @@ -58,7 +75,7 @@ describe('isThrottled', () => { }); test(`shouldn't throttle when group changes`, () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance({ meta: { lastScheduledActions: { date: new Date(), @@ -74,12 +91,20 @@ describe('isThrottled', () => { describe('scheduledActionGroupOrSubgroupHasChanged()', () => { test('should be false if no last scheduled and nothing scheduled', () => { - const alertInstance = new AlertInstance(); + 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({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ meta: { lastScheduledActions: { date: new Date(), @@ -92,7 +117,11 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { }); test('should be false if group and subgroup does not change', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ meta: { lastScheduledActions: { date: new Date(), @@ -106,7 +135,11 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { }); test('should be false if group does not change and subgroup goes from undefined to defined', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ meta: { lastScheduledActions: { date: new Date(), @@ -119,7 +152,11 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { }); test('should be false if group does not change and subgroup goes from defined to undefined', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ meta: { lastScheduledActions: { date: new Date(), @@ -133,13 +170,17 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { }); test('should be true if no last scheduled and has scheduled action', () => { - const alertInstance = new AlertInstance(); + 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({ + const alertInstance = new AlertInstance({ meta: { lastScheduledActions: { date: new Date(), @@ -152,7 +193,7 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { }); test('should be true if group does change and subgroup does change', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance({ meta: { lastScheduledActions: { date: new Date(), @@ -166,7 +207,11 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { }); test('should be true if group does not change and subgroup does change', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ meta: { lastScheduledActions: { date: new Date(), @@ -182,14 +227,22 @@ describe('scheduledActionGroupOrSubgroupHasChanged()', () => { describe('getScheduledActionOptions()', () => { test('defaults to undefined', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); expect(alertInstance.getScheduledActionOptions()).toBeUndefined(); }); }); describe('unscheduleActions()', () => { test('makes hasScheduledActions() return false', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); alertInstance.scheduleActions('default'); expect(alertInstance.hasScheduledActions()).toEqual(true); alertInstance.unscheduleActions(); @@ -197,7 +250,11 @@ describe('unscheduleActions()', () => { }); test('makes getScheduledActionOptions() return undefined', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); alertInstance.scheduleActions('default'); expect(alertInstance.getScheduledActionOptions()).toEqual({ actionGroup: 'default', @@ -212,14 +269,22 @@ describe('unscheduleActions()', () => { describe('getState()', () => { test('returns state passed to constructor', () => { const state = { foo: true }; - const alertInstance = new AlertInstance({ state }); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state }); expect(alertInstance.getState()).toEqual(state); }); }); describe('scheduleActions()', () => { test('makes hasScheduledActions() return true', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -233,7 +298,11 @@ describe('scheduleActions()', () => { }); test('makes isThrottled() return true when throttled', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -247,7 +316,11 @@ describe('scheduleActions()', () => { }); test('make isThrottled() return false when throttled expired', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -262,7 +335,11 @@ describe('scheduleActions()', () => { }); test('makes getScheduledActionOptions() return given options', () => { - const alertInstance = new AlertInstance({ state: { foo: true }, meta: {} }); + 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', @@ -272,7 +349,11 @@ describe('scheduleActions()', () => { }); test('cannot schdule for execution twice', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); alertInstance.scheduleActions('default', { field: true }); expect(() => alertInstance.scheduleActions('default', { field: false }) @@ -284,7 +365,11 @@ describe('scheduleActions()', () => { describe('scheduleActionsWithSubGroup()', () => { test('makes hasScheduledActions() return true', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -300,7 +385,11 @@ describe('scheduleActionsWithSubGroup()', () => { }); test('makes isThrottled() return true when throttled and subgroup is the same', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -317,7 +406,11 @@ describe('scheduleActionsWithSubGroup()', () => { }); test('makes isThrottled() return true when throttled and last schedule had no subgroup', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -333,7 +426,11 @@ describe('scheduleActionsWithSubGroup()', () => { }); test('makes isThrottled() return false when throttled and subgroup is the different', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -350,7 +447,11 @@ describe('scheduleActionsWithSubGroup()', () => { }); test('make isThrottled() return false when throttled expired', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -367,7 +468,11 @@ describe('scheduleActionsWithSubGroup()', () => { }); test('makes getScheduledActionOptions() return given options', () => { - const alertInstance = new AlertInstance({ state: { foo: true }, meta: {} }); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: {} }); alertInstance .replaceState({ otherField: true }) .scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); @@ -380,7 +485,11 @@ describe('scheduleActionsWithSubGroup()', () => { }); test('cannot schdule for execution twice', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); expect(() => alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: false }) @@ -390,7 +499,11 @@ describe('scheduleActionsWithSubGroup()', () => { }); test('cannot schdule for execution twice with different subgroups', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: true }); expect(() => alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: false }) @@ -400,7 +513,11 @@ describe('scheduleActionsWithSubGroup()', () => { }); test('cannot schdule for execution twice whether there are subgroups', () => { - const alertInstance = new AlertInstance(); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(); alertInstance.scheduleActions('default', { field: true }); expect(() => alertInstance.scheduleActionsWithSubGroup('default', 'subgroup', { field: false }) @@ -412,7 +529,11 @@ describe('scheduleActionsWithSubGroup()', () => { describe('replaceState()', () => { test('replaces previous state', () => { - const alertInstance = new AlertInstance({ state: { foo: true } }); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true } }); alertInstance.replaceState({ bar: true }); expect(alertInstance.getState()).toEqual({ bar: true }); alertInstance.replaceState({ baz: true }); @@ -422,7 +543,11 @@ describe('replaceState()', () => { describe('updateLastScheduledActions()', () => { test('replaces previous lastScheduledActions', () => { - const alertInstance = new AlertInstance({ meta: {} }); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ meta: {} }); alertInstance.updateLastScheduledActions('default'); expect(alertInstance.toJSON()).toEqual({ state: {}, @@ -438,7 +563,11 @@ describe('updateLastScheduledActions()', () => { describe('toJSON', () => { test('only serializes state and meta', () => { - const alertInstance = new AlertInstance({ + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >({ state: { foo: true }, meta: { lastScheduledActions: { @@ -464,7 +593,11 @@ describe('toRaw', () => { }, }, }; - const alertInstance = new AlertInstance(raw); + const alertInstance = new AlertInstance< + AlertInstanceState, + AlertInstanceContext, + DefaultActionGroupId + >(raw); expect(alertInstance.toRaw()).toEqual(raw); }); }); diff --git a/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts b/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts index 8841f3115d547..c49b38e157a07 100644 --- a/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts +++ b/x-pack/plugins/alerts/server/alert_instance/alert_instance.ts @@ -9,15 +9,17 @@ import { RawAlertInstance, rawAlertInstance, AlertInstanceContext, + DefaultActionGroupId, } from '../../common'; import { parseDuration } from '../lib'; interface ScheduledExecutionOptions< State extends AlertInstanceState, - Context extends AlertInstanceContext + Context extends AlertInstanceContext, + ActionGroupIds extends string = DefaultActionGroupId > { - actionGroup: string; + actionGroup: ActionGroupIds; subgroup?: string; context: Context; state: State; @@ -25,17 +27,19 @@ interface ScheduledExecutionOptions< export type PublicAlertInstance< State extends AlertInstanceState = AlertInstanceState, - Context extends AlertInstanceContext = AlertInstanceContext + Context extends AlertInstanceContext = AlertInstanceContext, + ActionGroupIds extends string = DefaultActionGroupId > = Pick< - AlertInstance, + AlertInstance, 'getState' | 'replaceState' | 'scheduleActions' | 'scheduleActionsWithSubGroup' >; export class AlertInstance< State extends AlertInstanceState = AlertInstanceState, - Context extends AlertInstanceContext = AlertInstanceContext + Context extends AlertInstanceContext = AlertInstanceContext, + ActionGroupIds extends string = never > { - private scheduledExecutionOptions?: ScheduledExecutionOptions; + private scheduledExecutionOptions?: ScheduledExecutionOptions; private meta: AlertInstanceMeta; private state: State; @@ -97,14 +101,14 @@ export class AlertInstance< private scheduledActionGroupIsUnchanged( lastScheduledActions: NonNullable, - scheduledExecutionOptions: ScheduledExecutionOptions + scheduledExecutionOptions: ScheduledExecutionOptions ) { return lastScheduledActions.group === scheduledExecutionOptions.actionGroup; } private scheduledActionSubgroupIsUnchanged( lastScheduledActions: NonNullable, - scheduledExecutionOptions: ScheduledExecutionOptions + scheduledExecutionOptions: ScheduledExecutionOptions ) { return lastScheduledActions.subgroup && scheduledExecutionOptions.subgroup ? lastScheduledActions.subgroup === scheduledExecutionOptions.subgroup @@ -128,7 +132,7 @@ export class AlertInstance< return this.state; } - scheduleActions(actionGroup: string, context: Context = {} as Context) { + scheduleActions(actionGroup: ActionGroupIds, context: Context = {} as Context) { this.ensureHasNoScheduledActions(); this.scheduledExecutionOptions = { actionGroup, @@ -139,7 +143,7 @@ export class AlertInstance< } scheduleActionsWithSubGroup( - actionGroup: string, + actionGroup: ActionGroupIds, subgroup: string, context: Context = {} as Context ) { @@ -164,7 +168,7 @@ export class AlertInstance< return this; } - updateLastScheduledActions(group: string, subgroup?: string) { + updateLastScheduledActions(group: ActionGroupIds, subgroup?: string) { this.meta.lastScheduledActions = { group, subgroup, date: new Date() }; } diff --git a/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts b/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts index 47f013a5d0e55..6ba4a8b57d9de 100644 --- a/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts +++ b/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts @@ -9,11 +9,12 @@ import { AlertInstance } from './alert_instance'; export function createAlertInstanceFactory< InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext ->(alertInstances: Record>) { - return (id: string): AlertInstance => { + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string +>(alertInstances: Record>) { + return (id: string): AlertInstance => { if (!alertInstances[id]) { - alertInstances[id] = new AlertInstance(); + alertInstances[id] = new AlertInstance(); } return alertInstances[id]; diff --git a/x-pack/plugins/alerts/server/alert_type_registry.test.ts b/x-pack/plugins/alerts/server/alert_type_registry.test.ts index 58b2cb74f2353..1fdd64d56d466 100644 --- a/x-pack/plugins/alerts/server/alert_type_registry.test.ts +++ b/x-pack/plugins/alerts/server/alert_type_registry.test.ts @@ -6,7 +6,7 @@ import { TaskRunnerFactory } from './task_runner'; import { AlertTypeRegistry, ConstructorOptions } from './alert_type_registry'; -import { AlertType } from './types'; +import { ActionGroup, AlertType } from './types'; import { taskManagerMock } from '../../task_manager/server/mocks'; import { ILicenseState } from './lib/license_state'; import { licenseStateMock } from './lib/license_state.mock'; @@ -55,7 +55,7 @@ describe('has()', () => { describe('register()', () => { test('throws if AlertType Id contains invalid characters', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -87,7 +87,7 @@ describe('register()', () => { }); test('throws if AlertType Id isnt a string', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: (123 as unknown) as string, name: 'Test', actionGroups: [ @@ -109,7 +109,7 @@ describe('register()', () => { }); test('throws if AlertType action groups contains reserved group id', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -117,10 +117,14 @@ describe('register()', () => { id: 'default', name: 'Default', }, - { + /** + * The type system will ensure you can't use the `recovered` action group + * but we also want to ensure this at runtime + */ + ({ id: 'recovered', name: 'Recovered', - }, + } as unknown) as ActionGroup<'NotReserved'>, ], defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', @@ -137,7 +141,7 @@ describe('register()', () => { }); test('allows an AlertType to specify a custom recovery group', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -172,7 +176,14 @@ describe('register()', () => { }); test('throws if the custom recovery group is contained in the AlertType action groups', () => { - const alertType: AlertType = { + const alertType: AlertType< + never, + never, + never, + never, + 'default' | 'backToAwesome', + 'backToAwesome' + > = { id: 'test', name: 'Test', actionGroups: [ @@ -204,7 +215,7 @@ describe('register()', () => { }); test('registers the executor with the task manager', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -234,7 +245,7 @@ describe('register()', () => { }); test('shallow clones the given alert type', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -475,8 +486,12 @@ describe('ensureAlertTypeEnabled', () => { }); }); -function alertTypeWithVariables(id: string, context: string, state: string): AlertType { - const baseAlert: AlertType = { +function alertTypeWithVariables( + id: ActionGroupIds, + context: string, + state: string +): AlertType { + const baseAlert: AlertType = { id, name: `${id}-name`, actionGroups: [], diff --git a/x-pack/plugins/alerts/server/alert_type_registry.ts b/x-pack/plugins/alerts/server/alert_type_registry.ts index 5e4188c1f3bc1..c26088b6bce3c 100644 --- a/x-pack/plugins/alerts/server/alert_type_registry.ts +++ b/x-pack/plugins/alerts/server/alert_type_registry.ts @@ -19,7 +19,12 @@ import { AlertInstanceState, AlertInstanceContext, } from './types'; -import { RecoveredActionGroup, getBuiltinActionGroups } from '../common'; +import { + RecoveredActionGroup, + getBuiltinActionGroups, + RecoveredActionGroupId, + ActionGroup, +} from '../common'; import { ILicenseState } from './lib/license_state'; import { getAlertTypeFeatureUsageName } from './lib/get_alert_type_feature_usage_name'; @@ -69,15 +74,36 @@ export type NormalizedAlertType< Params extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext -> = Omit, 'recoveryActionGroup'> & - Pick>, 'recoveryActionGroup'>; + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string +> = { + actionGroups: Array>; +} & Omit< + AlertType, + 'recoveryActionGroup' | 'actionGroups' +> & + Pick< + Required< + AlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > + >, + 'recoveryActionGroup' + >; export type UntypedNormalizedAlertType = NormalizedAlertType< AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string, + string >; export class AlertTypeRegistry { @@ -106,8 +132,19 @@ export class AlertTypeRegistry { Params extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext - >(alertType: AlertType) { + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string + >( + alertType: AlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > + ) { if (this.has(alertType.id)) { throw new Error( i18n.translate('xpack.alerts.alertTypeRegistry.register.duplicateAlertTypeError', { @@ -124,18 +161,28 @@ export class AlertTypeRegistry { Params, State, InstanceState, - InstanceContext + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId >(alertType); this.alertTypes.set( alertIdSchema.validate(alertType.id), - normalizedAlertType as UntypedNormalizedAlertType + /** stripping the typing is required in order to store the AlertTypes in a Map */ + (normalizedAlertType as unknown) as UntypedNormalizedAlertType ); this.taskManager.registerTaskDefinitions({ [`alerting:${alertType.id}`]: { title: alertType.name, createTaskRunner: (context: RunContext) => - this.taskRunnerFactory.create(normalizedAlertType as UntypedNormalizedAlertType, context), + this.taskRunnerFactory.create< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId | RecoveredActionGroupId + >(normalizedAlertType, context), }, }); // No need to notify usage on basic alert types @@ -151,8 +198,19 @@ export class AlertTypeRegistry { Params extends AlertTypeParams = AlertTypeParams, State extends AlertTypeState = AlertTypeState, InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext - >(id: string): NormalizedAlertType { + InstanceContext extends AlertInstanceContext = AlertInstanceContext, + ActionGroupIds extends string = string, + RecoveryActionGroupId extends string = string + >( + id: string + ): NormalizedAlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > { if (!this.has(id)) { throw Boom.badRequest( i18n.translate('xpack.alerts.alertTypeRegistry.get.missingAlertTypeError', { @@ -163,11 +221,18 @@ export class AlertTypeRegistry { }) ); } - return this.alertTypes.get(id)! as NormalizedAlertType< + /** + * When we store the AlertTypes in the Map we strip the typing. + * This means that returning a typed AlertType in `get` is an inherently + * unsafe operation. Down casting to `unknown` is the only way to achieve this. + */ + return (this.alertTypes.get(id)! as unknown) as NormalizedAlertType< Params, State, InstanceState, - InstanceContext + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId >; } @@ -217,15 +282,31 @@ function augmentActionGroupsWithReserved< Params extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string >( - alertType: AlertType -): NormalizedAlertType { + alertType: AlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > +): NormalizedAlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveredActionGroupId | RecoveryActionGroupId +> { const reservedActionGroups = getBuiltinActionGroups(alertType.recoveryActionGroup); const { id, actionGroups, recoveryActionGroup } = alertType; - const activeActionGroups = new Set(actionGroups.map((item) => item.id)); - const intersectingReservedActionGroups = intersection( + const activeActionGroups = new Set(actionGroups.map((item) => item.id)); + const intersectingReservedActionGroups = intersection( [...activeActionGroups.values()], reservedActionGroups.map((item) => item.id) ); diff --git a/x-pack/plugins/alerts/server/index.ts b/x-pack/plugins/alerts/server/index.ts index 7bb54cd87bc33..da56da671f9b0 100644 --- a/x-pack/plugins/alerts/server/index.ts +++ b/x-pack/plugins/alerts/server/index.ts @@ -16,6 +16,7 @@ export { ActionVariable, AlertType, ActionGroup, + ActionGroupIdsOf, AlertingPlugin, AlertExecutorOptions, AlertActionParams, diff --git a/x-pack/plugins/alerts/server/lib/license_state.test.ts b/x-pack/plugins/alerts/server/lib/license_state.test.ts index 94db4c946ab00..2bba0a910b65e 100644 --- a/x-pack/plugins/alerts/server/lib/license_state.test.ts +++ b/x-pack/plugins/alerts/server/lib/license_state.test.ts @@ -56,7 +56,7 @@ describe('getLicenseCheckForAlertType', () => { let license: Subject; let licenseState: ILicenseState; const mockNotifyUsage = jest.fn(); - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -190,7 +190,7 @@ describe('ensureLicenseForAlertType()', () => { let license: Subject; let licenseState: ILicenseState; const mockNotifyUsage = jest.fn(); - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ diff --git a/x-pack/plugins/alerts/server/lib/license_state.ts b/x-pack/plugins/alerts/server/lib/license_state.ts index dea5b3338a5be..e20ccea7c834f 100644 --- a/x-pack/plugins/alerts/server/lib/license_state.ts +++ b/x-pack/plugins/alerts/server/lib/license_state.ts @@ -13,7 +13,13 @@ import { LicensingPluginStart } from '../../../licensing/server'; import { ILicense, LicenseType } from '../../../licensing/common/types'; import { PLUGIN } from '../constants/plugin'; import { getAlertTypeFeatureUsageName } from './get_alert_type_feature_usage_name'; -import { AlertType } from '../types'; +import { + AlertType, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; import { AlertTypeDisabledError } from './errors/alert_type_disabled'; export type ILicenseState = PublicMethodsOf; @@ -130,7 +136,23 @@ export class LicenseState { } } - public ensureLicenseForAlertType(alertType: AlertType) { + public ensureLicenseForAlertType< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string + >( + alertType: AlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > + ) { this.notifyUsage(alertType.name, alertType.minimumLicenseRequired); const check = this.getLicenseCheckForAlertType( diff --git a/x-pack/plugins/alerts/server/plugin.test.ts b/x-pack/plugins/alerts/server/plugin.test.ts index 6288d27c6ebe0..ece6fa2328d68 100644 --- a/x-pack/plugins/alerts/server/plugin.test.ts +++ b/x-pack/plugins/alerts/server/plugin.test.ts @@ -58,7 +58,7 @@ describe('Alerting Plugin', () => { describe('registerType()', () => { let setup: PluginSetupContract; - const sampleAlertType: AlertType = { + const sampleAlertType: AlertType = { id: 'test', name: 'test', minimumLicenseRequired: 'basic', diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts index 63861f5050f25..d15ae0ca55ef9 100644 --- a/x-pack/plugins/alerts/server/plugin.ts +++ b/x-pack/plugins/alerts/server/plugin.ts @@ -102,9 +102,18 @@ export interface PluginSetupContract { Params extends AlertTypeParams = AlertTypeParams, State extends AlertTypeState = AlertTypeState, InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + InstanceContext extends AlertInstanceContext = AlertInstanceContext, + ActionGroupIds extends string = never, + RecoveryActionGroupId extends string = never >( - alertType: AlertType + alertType: AlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > ): void; } @@ -273,8 +282,19 @@ export class AlertingPlugin { Params extends AlertTypeParams = AlertTypeParams, State extends AlertTypeState = AlertTypeState, InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext - >(alertType: AlertType) { + InstanceContext extends AlertInstanceContext = AlertInstanceContext, + ActionGroupIds extends string = never, + RecoveryActionGroupId extends string = never + >( + alertType: AlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > + ) { if (!(alertType.minimumLicenseRequired in LICENSE_TYPE)) { throw new Error(`"${alertType.minimumLicenseRequired}" is not a valid license type`); } diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts index 5603b13a3b1f5..5ab44a6ccdb51 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts @@ -15,7 +15,7 @@ import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { KibanaRequest } from 'kibana/server'; import { asSavedObjectExecutionSource } from '../../../actions/server'; import { InjectActionParamsOpts } from './inject_action_params'; -import { UntypedNormalizedAlertType } from '../alert_type_registry'; +import { NormalizedAlertType } from '../alert_type_registry'; import { AlertTypeParams, AlertTypeState, @@ -27,7 +27,14 @@ jest.mock('./inject_action_params', () => ({ injectActionParams: jest.fn(), })); -const alertType: UntypedNormalizedAlertType = { +const alertType: NormalizedAlertType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + 'default' | 'other-group', + 'recovered' +> = { id: 'test', name: 'Test', actionGroups: [ @@ -53,7 +60,9 @@ const createExecutionHandlerParams: jest.Mocked< AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + 'default' | 'other-group', + 'recovered' > > = { actionsPlugin: mockActionsPlugin, @@ -348,7 +357,9 @@ test('state attribute gets parameterized', async () => { test(`logs an error when action group isn't part of actionGroups available for the alertType`, async () => { const executionHandler = createExecutionHandler(createExecutionHandlerParams); const result = await executionHandler({ - actionGroup: 'invalid-group', + // we have to trick the compiler as this is an invalid type and this test checks whether we + // enforce this at runtime as well as compile time + actionGroup: 'invalid-group' as 'default' | 'other-group', context: {}, state: {}, alertInstanceId: '2', diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts index 8b4412aeb23e5..c3d90c7bcf08b 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts @@ -27,7 +27,9 @@ export interface CreateExecutionHandlerOptions< Params extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string > { alertId: string; alertName: string; @@ -36,26 +38,39 @@ export interface CreateExecutionHandlerOptions< actions: AlertAction[]; spaceId: string; apiKey: RawAlert['apiKey']; - alertType: NormalizedAlertType; + alertType: NormalizedAlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + >; logger: Logger; eventLogger: IEventLogger; request: KibanaRequest; alertParams: AlertTypeParams; } -interface ExecutionHandlerOptions { - actionGroup: string; +interface ExecutionHandlerOptions { + actionGroup: ActionGroupIds; actionSubgroup?: string; alertInstanceId: string; context: AlertInstanceContext; state: AlertInstanceState; } +export type ExecutionHandler = ( + options: ExecutionHandlerOptions +) => Promise; + export function createExecutionHandler< Params extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string >({ logger, alertId, @@ -69,7 +84,14 @@ export function createExecutionHandler< eventLogger, request, alertParams, -}: CreateExecutionHandlerOptions) { +}: CreateExecutionHandlerOptions< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId +>): ExecutionHandler { const alertTypeActionGroups = new Map( alertType.actionGroups.map((actionGroup) => [actionGroup.id, actionGroup.name]) ); @@ -79,7 +101,7 @@ export function createExecutionHandler< context, state, alertInstanceId, - }: ExecutionHandlerOptions) => { + }: ExecutionHandlerOptions) => { if (!alertTypeActionGroups.has(actionGroup)) { logger.error(`Invalid action group "${actionGroup}" for alert "${alertType.id}".`); return; diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts index 967c5263b9730..75be9d371aee4 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts @@ -266,7 +266,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices .alertInstanceFactory('1') @@ -426,7 +427,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } @@ -542,7 +544,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertInstanceFactory('2').scheduleActions('default'); @@ -595,7 +598,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } @@ -696,7 +700,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } @@ -743,7 +748,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices .alertInstanceFactory('1') @@ -798,7 +804,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } @@ -973,7 +980,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } @@ -1080,7 +1088,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } @@ -1178,7 +1187,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } @@ -1447,7 +1457,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { throw new Error('OMG'); } @@ -1822,7 +1833,8 @@ describe('Task Runner', () => { AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + string >) => { throw new Error('OMG'); } diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.ts index c4187145e5a16..12f7c33ae5052 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -10,7 +10,7 @@ import { addSpaceIdToPath } from '../../../spaces/server'; import { Logger, KibanaRequest } from '../../../../../src/core/server'; import { TaskRunnerContext } from './task_runner_factory'; import { ConcreteTaskInstance, throwUnrecoverableError } from '../../../task_manager/server'; -import { createExecutionHandler } from './create_execution_handler'; +import { createExecutionHandler, ExecutionHandler } from './create_execution_handler'; import { AlertInstance, createAlertInstanceFactory } from '../alert_instance'; import { validateAlertTypeParams, @@ -44,6 +44,7 @@ import { AlertTypeState, AlertInstanceState, AlertInstanceContext, + WithoutReservedActionGroups, } from '../../common'; import { NormalizedAlertType } from '../alert_type_registry'; @@ -64,16 +65,32 @@ export class TaskRunner< Params extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string > { private context: TaskRunnerContext; private logger: Logger; private taskInstance: AlertTaskInstance; - private alertType: NormalizedAlertType; + private alertType: NormalizedAlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + >; private readonly alertTypeRegistry: AlertTypeRegistry; constructor( - alertType: NormalizedAlertType, + alertType: NormalizedAlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + >, taskInstance: ConcreteTaskInstance, context: TaskRunnerContext ) { @@ -144,7 +161,14 @@ export class TaskRunner< actions: Alert['actions'], alertParams: Params ) { - return createExecutionHandler({ + return createExecutionHandler< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + >({ alertId, alertName, tags, @@ -163,7 +187,7 @@ export class TaskRunner< async executeAlertInstance( alertInstanceId: string, alertInstance: AlertInstance, - executionHandler: ReturnType + executionHandler: ExecutionHandler ) { const { actionGroup, @@ -180,7 +204,7 @@ export class TaskRunner< services: Services, alert: SanitizedAlert, params: Params, - executionHandler: ReturnType, + executionHandler: ExecutionHandler, spaceId: string, event: Event ): Promise { @@ -218,9 +242,11 @@ export class TaskRunner< alertId, services: { ...services, - alertInstanceFactory: createAlertInstanceFactory( - alertInstances - ), + alertInstanceFactory: createAlertInstanceFactory< + InstanceState, + InstanceContext, + WithoutReservedActionGroups + >(alertInstances), }, params, state: alertTypeState as State, @@ -278,7 +304,7 @@ export class TaskRunner< if (!muteAll) { const mutedInstanceIdsSet = new Set(mutedInstanceIds); - scheduleActionsForRecoveredInstances({ + scheduleActionsForRecoveredInstances({ recoveryActionGroup: this.alertType.recoveryActionGroup, recoveredAlertInstances, executionHandler, @@ -615,20 +641,30 @@ function generateNewAndRecoveredInstanceEvents< interface ScheduleActionsForRecoveredInstancesParams< InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext + InstanceContext extends AlertInstanceContext, + RecoveryActionGroupId extends string > { logger: Logger; - recoveryActionGroup: ActionGroup; - recoveredAlertInstances: Dictionary>; - executionHandler: ReturnType; + recoveryActionGroup: ActionGroup; + recoveredAlertInstances: Dictionary< + AlertInstance + >; + executionHandler: ExecutionHandler; mutedInstanceIdsSet: Set; alertLabel: string; } function scheduleActionsForRecoveredInstances< InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext ->(params: ScheduleActionsForRecoveredInstancesParams) { + InstanceContext extends AlertInstanceContext, + RecoveryActionGroupId extends string +>( + params: ScheduleActionsForRecoveredInstancesParams< + InstanceState, + InstanceContext, + RecoveryActionGroupId + > +) { const { logger, recoveryActionGroup, @@ -660,18 +696,31 @@ function scheduleActionsForRecoveredInstances< interface LogActiveAndRecoveredInstancesParams< InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string > { logger: Logger; - activeAlertInstances: Dictionary>; - recoveredAlertInstances: Dictionary>; + activeAlertInstances: Dictionary>; + recoveredAlertInstances: Dictionary< + AlertInstance + >; alertLabel: string; } function logActiveAndRecoveredInstances< InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext ->(params: LogActiveAndRecoveredInstancesParams) { + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string +>( + params: LogActiveAndRecoveredInstancesParams< + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + > +) { const { logger, activeAlertInstances, recoveredAlertInstances, alertLabel } = params; const activeInstanceIds = Object.keys(activeAlertInstances); const recoveredInstanceIds = Object.keys(recoveredAlertInstances); diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts index e266608d80880..2d57467075987 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts @@ -13,11 +13,19 @@ import { import { RunContext } from '../../../task_manager/server'; import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; import { PluginStartContract as ActionsPluginStartContract } from '../../../actions/server'; -import { AlertTypeRegistry, GetServicesFunction, SpaceIdToNamespaceFunction } from '../types'; +import { + AlertTypeParams, + AlertTypeRegistry, + GetServicesFunction, + SpaceIdToNamespaceFunction, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; import { TaskRunner } from './task_runner'; import { IEventLogger } from '../../../event_log/server'; import { AlertsClient } from '../alerts_client'; -import { UntypedNormalizedAlertType } from '../alert_type_registry'; +import { NormalizedAlertType } from '../alert_type_registry'; export interface TaskRunnerContext { logger: Logger; @@ -44,11 +52,35 @@ export class TaskRunnerFactory { this.taskRunnerContext = taskRunnerContext; } - public create(alertType: UntypedNormalizedAlertType, { taskInstance }: RunContext) { + public create< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string + >( + alertType: NormalizedAlertType< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + >, + { taskInstance }: RunContext + ) { if (!this.isInitialized) { throw new Error('TaskRunnerFactory not initialized'); } - return new TaskRunner(alertType, taskInstance, this.taskRunnerContext!); + return new TaskRunner< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + >(alertType, taskInstance, this.taskRunnerContext!); } } diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index bb2d429a7c8b5..39c52d9653aaa 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -29,6 +29,7 @@ import { AlertExecutionStatusErrorReasons, AlertsHealth, AlertNotifyWhenType, + WithoutReservedActionGroups, } from '../common'; import { LicenseType } from '../../licensing/server'; @@ -58,21 +59,25 @@ export interface Services { export interface AlertServices< InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + InstanceContext extends AlertInstanceContext = AlertInstanceContext, + ActionGroupIds extends string = never > extends Services { - alertInstanceFactory: (id: string) => PublicAlertInstance; + alertInstanceFactory: ( + id: string + ) => PublicAlertInstance; } export interface AlertExecutorOptions< Params extends AlertTypeParams = never, State extends AlertTypeState = never, InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never + InstanceContext extends AlertInstanceContext = never, + ActionGroupIds extends string = never > { alertId: string; startedAt: Date; previousStartedAt: Date | null; - services: AlertServices; + services: AlertServices; params: Params; state: State; spaceId: string; @@ -92,9 +97,10 @@ export type ExecutorType< Params extends AlertTypeParams = never, State extends AlertTypeState = never, InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never + InstanceContext extends AlertInstanceContext = never, + ActionGroupIds extends string = never > = ( - options: AlertExecutorOptions + options: AlertExecutorOptions ) => Promise; export interface AlertTypeParamsValidator { @@ -104,17 +110,29 @@ export interface AlertType< Params extends AlertTypeParams = never, State extends AlertTypeState = never, InstanceState extends AlertInstanceState = never, - InstanceContext extends AlertInstanceContext = never + InstanceContext extends AlertInstanceContext = never, + ActionGroupIds extends string = never, + RecoveryActionGroupId extends string = never > { id: string; name: string; validate?: { params?: AlertTypeParamsValidator; }; - actionGroups: ActionGroup[]; - defaultActionGroupId: ActionGroup['id']; - recoveryActionGroup?: ActionGroup; - executor: ExecutorType; + actionGroups: Array>; + defaultActionGroupId: ActionGroup['id']; + recoveryActionGroup?: ActionGroup; + executor: ExecutorType< + Params, + State, + InstanceState, + InstanceContext, + /** + * Ensure that the reserved ActionGroups (such as `Recovered`) are not + * available for scheduling in the Executor + */ + WithoutReservedActionGroups + >; producer: string; actionVariables?: { context?: ActionVariable[]; diff --git a/x-pack/plugins/apm/common/alert_types.ts b/x-pack/plugins/apm/common/alert_types.ts index 7cc36253ef581..bb42c8acd167a 100644 --- a/x-pack/plugins/apm/common/alert_types.ts +++ b/x-pack/plugins/apm/common/alert_types.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { ValuesType } from 'utility-types'; +import { ActionGroup } from '../../alerts/common'; import { ANOMALY_SEVERITY, ANOMALY_THRESHOLD } from '../../ml/common'; export enum AlertType { @@ -15,20 +16,31 @@ export enum AlertType { TransactionDurationAnomaly = 'apm.transaction_duration_anomaly', } -const THRESHOLD_MET_GROUP = { - id: 'threshold_met', +export const THRESHOLD_MET_GROUP_ID = 'threshold_met'; +export type ThresholdMetActionGroupId = typeof THRESHOLD_MET_GROUP_ID; +const THRESHOLD_MET_GROUP: ActionGroup = { + id: THRESHOLD_MET_GROUP_ID, name: i18n.translate('xpack.apm.a.thresholdMet', { defaultMessage: 'Threshold met', }), }; -export const ALERT_TYPES_CONFIG = { +export const ALERT_TYPES_CONFIG: Record< + AlertType, + { + name: string; + actionGroups: Array>; + defaultActionGroupId: ThresholdMetActionGroupId; + minimumLicenseRequired: string; + producer: string; + } +> = { [AlertType.ErrorCount]: { name: i18n.translate('xpack.apm.errorCountAlert.name', { defaultMessage: 'Error count threshold', }), actionGroups: [THRESHOLD_MET_GROUP], - defaultActionGroupId: 'threshold_met', + defaultActionGroupId: THRESHOLD_MET_GROUP_ID, minimumLicenseRequired: 'basic', producer: 'apm', }, @@ -37,7 +49,7 @@ export const ALERT_TYPES_CONFIG = { defaultMessage: 'Transaction duration threshold', }), actionGroups: [THRESHOLD_MET_GROUP], - defaultActionGroupId: 'threshold_met', + defaultActionGroupId: THRESHOLD_MET_GROUP_ID, minimumLicenseRequired: 'basic', producer: 'apm', }, @@ -46,7 +58,7 @@ export const ALERT_TYPES_CONFIG = { defaultMessage: 'Transaction duration anomaly', }), actionGroups: [THRESHOLD_MET_GROUP], - defaultActionGroupId: 'threshold_met', + defaultActionGroupId: THRESHOLD_MET_GROUP_ID, minimumLicenseRequired: 'basic', producer: 'apm', }, @@ -55,7 +67,7 @@ export const ALERT_TYPES_CONFIG = { defaultMessage: 'Transaction error rate threshold', }), actionGroups: [THRESHOLD_MET_GROUP], - defaultActionGroupId: 'threshold_met', + defaultActionGroupId: THRESHOLD_MET_GROUP_ID, minimumLicenseRequired: 'basic', producer: 'apm', }, diff --git a/x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts b/x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts index 161d5d03fcb40..4d1f53c9d4f94 100644 --- a/x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts +++ b/x-pack/plugins/apm/server/lib/alerts/alerting_es_client.ts @@ -4,14 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ThresholdMetActionGroupId } from '../../../common/alert_types'; import { ESSearchRequest, ESSearchResponse, } from '../../../../../typings/elasticsearch'; -import { AlertServices } from '../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../alerts/server'; export function alertingEsClient( - services: AlertServices, + services: AlertServices< + AlertInstanceState, + AlertInstanceContext, + ThresholdMetActionGroupId + >, params: TParams ): Promise> { return services.callCluster('search', { diff --git a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts index 36fdf45d805f1..764e706834a70 100644 --- a/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts +++ b/x-pack/plugins/apm/server/lib/alerts/register_error_count_alert_type.ts @@ -4,13 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema } from '@kbn/config-schema'; +import { schema, TypeOf } from '@kbn/config-schema'; import { isEmpty } from 'lodash'; import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { APMConfig } from '../..'; -import { AlertingPlugin } from '../../../../alerts/server'; -import { AlertType, ALERT_TYPES_CONFIG } from '../../../common/alert_types'; +import { + AlertingPlugin, + AlertInstanceContext, + AlertInstanceState, + AlertTypeState, +} from '../../../../alerts/server'; +import { + AlertType, + ALERT_TYPES_CONFIG, + ThresholdMetActionGroupId, +} from '../../../common/alert_types'; import { PROCESSOR_EVENT, SERVICE_ENVIRONMENT, @@ -41,7 +50,13 @@ export function registerErrorCountAlertType({ alerts, config$, }: RegisterAlertParams) { - alerts.registerType({ + alerts.registerType< + TypeOf, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + ThresholdMetActionGroupId + >({ id: AlertType.ErrorCount, name: alertTypeConfig.name, actionGroups: alertTypeConfig.actionGroups, diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 54cf8658a3f0d..6d851eeaab542 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -10,6 +10,7 @@ import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_m import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { AlertStates, InventoryMetricConditions } from './types'; import { + ActionGroup, AlertInstanceContext, AlertInstanceState, RecoveredActionGroup, @@ -27,6 +28,7 @@ import { stateToAlertMessage, } from '../common/messages'; import { evaluateCondition } from './evaluate_condition'; +import { InventoryMetricThresholdAllowedActionGroups } from './register_inventory_metric_threshold_alert_type'; interface InventoryMetricThresholdParams { criteria: InventoryMetricConditions[]; @@ -46,7 +48,8 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = Record, Record, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + InventoryMetricThresholdAllowedActionGroups >) => { const { criteria, @@ -115,18 +118,25 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = } if (reason) { const actionGroupId = - nextState === AlertStates.OK ? RecoveredActionGroup.id : FIRED_ACTIONS.id; - alertInstance.scheduleActions(actionGroupId, { - group: item, - alertState: stateToAlertMessage[nextState], - reason, - timestamp: moment().toISOString(), - value: mapToConditionsLookup(results, (result) => - formatMetric(result[item].metric, result[item].currentValue) - ), - threshold: mapToConditionsLookup(criteria, (c) => c.threshold), - metric: mapToConditionsLookup(criteria, (c) => c.metric), - }); + nextState === AlertStates.OK ? RecoveredActionGroup.id : FIRED_ACTIONS_ID; + alertInstance.scheduleActions( + /** + * TODO: We're lying to the compiler here as explicitly calling `scheduleActions` on + * the RecoveredActionGroup isn't allowed + */ + (actionGroupId as unknown) as InventoryMetricThresholdAllowedActionGroups, + { + group: item, + alertState: stateToAlertMessage[nextState], + reason, + timestamp: moment().toISOString(), + value: mapToConditionsLookup(results, (result) => + formatMetric(result[item].metric, result[item].currentValue) + ), + threshold: mapToConditionsLookup(criteria, (c) => c.threshold), + metric: mapToConditionsLookup(criteria, (c) => c.metric), + } + ); } alertInstance.replaceState({ @@ -160,8 +170,9 @@ const mapToConditionsLookup = ( {} ); -export const FIRED_ACTIONS = { - id: 'metrics.invenotry_threshold.fired', +export const FIRED_ACTIONS_ID = 'metrics.invenotry_threshold.fired'; +export const FIRED_ACTIONS: ActionGroup = { + id: FIRED_ACTIONS_ID, name: i18n.translate('xpack.infra.metrics.alerting.inventory.threshold.fired', { defaultMessage: 'Fired', }), diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts index a2e8eff34ef98..48efe8fd45a3c 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts @@ -9,6 +9,7 @@ import { AlertType, AlertInstanceState, AlertInstanceContext } from '../../../.. import { createInventoryMetricThresholdExecutor, FIRED_ACTIONS, + FIRED_ACTIONS_ID, } from './inventory_metric_threshold_executor'; import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, Comparator } from './types'; import { InfraBackendLibs } from '../../infra_types'; @@ -22,6 +23,7 @@ import { metricActionVariableDescription, thresholdActionVariableDescription, } from '../common/messages'; +import { RecoveredActionGroupId } from '../../../../../alerts/common'; const condition = schema.object({ threshold: schema.arrayOf(schema.number()), @@ -40,6 +42,8 @@ const condition = schema.object({ ), }); +export type InventoryMetricThresholdAllowedActionGroups = typeof FIRED_ACTIONS_ID; + export const registerMetricInventoryThresholdAlertType = ( libs: InfraBackendLibs ): AlertType< @@ -49,7 +53,9 @@ export const registerMetricInventoryThresholdAlertType = ( Record, Record, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + InventoryMetricThresholdAllowedActionGroups, + RecoveredActionGroupId > => ({ id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.metrics.inventory.alertName', { @@ -69,7 +75,7 @@ export const registerMetricInventoryThresholdAlertType = ( { unknowns: 'allow' } ), }, - defaultActionGroupId: FIRED_ACTIONS.id, + defaultActionGroupId: FIRED_ACTIONS_ID, actionGroups: [FIRED_ACTIONS], producer: 'infrastructure', minimumLicenseRequired: 'basic', diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index 09d7e482772c2..f4a9e8fdef3ff 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -13,6 +13,8 @@ import { AlertTypeState, AlertInstanceContext, AlertInstanceState, + ActionGroup, + ActionGroupIdsOf, } from '../../../../../alerts/server'; import { AlertStates, @@ -37,12 +39,18 @@ import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { decodeOrThrow } from '../../../../common/runtime_types'; import { UNGROUPED_FACTORY_KEY } from '../common/utils'; -type LogThresholdAlertServices = AlertServices; +type LogThresholdActionGroups = ActionGroupIdsOf; +type LogThresholdAlertServices = AlertServices< + AlertInstanceState, + AlertInstanceContext, + LogThresholdActionGroups +>; type LogThresholdAlertExecutorOptions = AlertExecutorOptions< AlertTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + LogThresholdActionGroups >; const COMPOSITE_GROUP_SIZE = 40; @@ -344,9 +352,9 @@ export const processGroupByRatioResults = ( }; type AlertInstanceUpdater = ( - alertInstance: AlertInstance, + alertInstance: AlertInstance, state: AlertStates, - actions?: Array<{ actionGroup: string; context: AlertInstanceContext }> + actions?: Array<{ actionGroup: LogThresholdActionGroups; context: AlertInstanceContext }> ) => void; export const updateAlertInstance: AlertInstanceUpdater = (alertInstance, state, actions) => { @@ -653,8 +661,9 @@ const createConditionsMessageForCriteria = (criteria: CountCriteria) => { // When the Alerting plugin implements support for multiple action groups, add additional // action groups here to send different messages, e.g. a recovery notification -export const FIRED_ACTIONS = { - id: 'logs.threshold.fired', +export const LogsThresholdFiredActionGroupId = 'logs.threshold.fired'; +export const FIRED_ACTIONS: ActionGroup<'logs.threshold.fired'> = { + id: LogsThresholdFiredActionGroupId, name: i18n.translate('xpack.infra.logs.alerting.threshold.fired', { defaultMessage: 'Fired', }), diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts index e248d3b3ddcfa..236ab9b97fdc3 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts @@ -4,7 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { PluginSetupContract } from '../../../../../alerts/server'; +import { + PluginSetupContract, + AlertTypeParams, + AlertTypeState, + AlertInstanceContext, + AlertInstanceState, + ActionGroupIdsOf, +} from '../../../../../alerts/server'; import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor'; import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, @@ -79,7 +86,13 @@ export async function registerLogThresholdAlertType( ); } - alertingPlugin.registerType({ + alertingPlugin.registerType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + ActionGroupIdsOf + >({ id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.logs.alertName', { defaultMessage: 'Log threshold', diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts index 000c89f5899ef..77126e7d9454c 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts @@ -10,6 +10,7 @@ import { AlertInstanceState, AlertInstanceContext, AlertExecutorOptions, + ActionGroupIdsOf, } from '../../../../../alerts/server'; import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api/metrics_explorer'; import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; @@ -33,7 +34,8 @@ export type MetricThresholdAlertType = AlertType< Record, Record, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + ActionGroupIdsOf >; export type MetricThresholdAlertExecutorOptions = AlertExecutorOptions< /** @@ -42,7 +44,8 @@ export type MetricThresholdAlertExecutorOptions = AlertExecutorOptions< Record, Record, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + ActionGroupIdsOf >; export function registerMetricThresholdAlertType(libs: InfraBackendLibs): MetricThresholdAlertType { diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index 46adfebfd17bf..405518d5de378 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -93,7 +93,7 @@ export class BaseAlert { this.scopedLogger = Globals.app.getLogger(alertOptions.id!); } - public getAlertType(): AlertType { + public getAlertType(): AlertType { const { id, name, actionVariables } = this.alertOptions; return { id, @@ -108,8 +108,11 @@ export class BaseAlert { ], defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', - executor: (options: AlertExecutorOptions & { state: ExecutedState }): Promise => - this.execute(options), + executor: ( + options: AlertExecutorOptions & { + state: ExecutedState; + } + ): Promise => this.execute(options), producer: 'monitoring', actionVariables: { context: actionVariables, @@ -238,7 +241,9 @@ export class BaseAlert { services, params, state, - }: AlertExecutorOptions & { state: ExecutedState }): Promise { + }: AlertExecutorOptions & { + state: ExecutedState; + }): Promise { this.scopedLogger.debug( `Executing alert with params: ${JSON.stringify(params)} and state: ${JSON.stringify(state)}` ); @@ -333,7 +338,7 @@ export class BaseAlert { protected async processData( data: AlertData[], clusters: AlertCluster[], - services: AlertServices, + services: AlertServices, state: ExecutedState ) { const currentUTC = +new Date(); @@ -387,7 +392,7 @@ export class BaseAlert { protected async processLegacyData( data: AlertData[], clusters: AlertCluster[], - services: AlertServices, + services: AlertServices, state: ExecutedState ) { const currentUTC = +new Date(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts index e4e9df552101b..b450be6c0ac08 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts @@ -105,7 +105,7 @@ export const isNotificationAlertExecutor = ( }; export type NotificationAlertTypeDefinition = Omit< - AlertType, + AlertType, 'executor' > & { executor: ({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index d530fe10c6498..d46bfc8cda069 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -8,7 +8,11 @@ import { flow, omit } from 'lodash/fp'; import set from 'set-value'; import { Logger } from '../../../../../../../src/core/server'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { RuleTypeParams, RefreshTypes } from '../types'; import { singleBulkCreate, SingleBulkCreateResponse } from './single_bulk_create'; @@ -20,7 +24,7 @@ interface BulkCreateMlSignalsParams { actions: RuleAlertAction[]; someResult: AnomalyResults; ruleParams: RuleTypeParams; - services: AlertServices; + services: AlertServices; logger: Logger; id: string; signalsIndex: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_threshold_signals.ts index 3cad33b278749..438f08656a90f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_threshold_signals.ts @@ -12,7 +12,11 @@ import { TimestampOverrideOrUndefined, } from '../../../../common/detection_engine/schemas/common/schemas'; import { Logger } from '../../../../../../../src/core/server'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { RuleTypeParams, RefreshTypes } from '../types'; import { singleBulkCreate, SingleBulkCreateResponse } from './single_bulk_create'; @@ -24,7 +28,7 @@ interface BulkCreateThresholdSignalsParams { actions: RuleAlertAction[]; someResult: SignalSearchResponse; ruleParams: RuleTypeParams; - services: AlertServices; + services: AlertServices; inputIndexPattern: string[]; logger: Logger; id: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts index 239edcd1f1845..d52c2f5253711 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts @@ -12,7 +12,11 @@ import { } from '../../../../common/detection_engine/schemas/common/schemas'; import { singleSearchAfter } from './single_search_after'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; import { Logger } from '../../../../../../../src/core/server'; import { SignalSearchResponse } from './types'; import { BuildRuleMessage } from './rule_messages'; @@ -21,7 +25,7 @@ interface FindThresholdSignalsParams { from: string; to: string; inputIndexPattern: string[]; - services: AlertServices; + services: AlertServices; logger: Logger; filter: unknown; threshold: Threshold; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts index 522f4bfa5ef98..d4e59bb97bb72 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_filter.ts @@ -15,7 +15,11 @@ import { Language, } from '../../../../common/detection_engine/schemas/common/schemas'; import { ExceptionListItemSchema } from '../../../../../lists/common/schemas'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; import { PartialFilter } from '../types'; import { BadRequestError } from '../errors/bad_request_error'; import { QueryFilter } from './types'; @@ -26,7 +30,7 @@ interface GetFilterArgs { language: LanguageOrUndefined; query: QueryOrUndefined; savedId: SavedIdOrUndefined; - services: AlertServices; + services: AlertServices; index: IndexOrUndefined; lists: ExceptionListItemSchema[]; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.ts index 03a63fe315a33..b6730ff55cfd2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/get_input_output_index.ts @@ -5,10 +5,14 @@ */ import { DEFAULT_INDEX_KEY, DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; export const getInputIndex = async ( - services: AlertServices, + services: AlertServices, version: string, inputIndex: string[] | null | undefined ): Promise => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/siem_rule_action_groups.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/siem_rule_action_groups.ts index 46fdb739bf143..6b5480accaf62 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/siem_rule_action_groups.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/siem_rule_action_groups.ts @@ -5,8 +5,9 @@ */ import { i18n } from '@kbn/i18n'; +import { ActionGroup } from '../../../../../alerts/common'; -export const siemRuleActionGroups = [ +export const siemRuleActionGroups: Array> = [ { id: 'default', name: i18n.translate( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_bulk_create.ts index 943b70794a9b1..02abb05785642 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_bulk_create.ts @@ -6,7 +6,11 @@ import { countBy, isEmpty, get } from 'lodash'; import { performance } from 'perf_hooks'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; import { SignalSearchResponse, BulkResponse, SignalHit, WrappedSignalHit } from './types'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { RuleTypeParams, RefreshTypes } from '../types'; @@ -19,7 +23,7 @@ import { isEventTypeSignal } from './build_event_type_signal'; interface SingleBulkCreateParams { filteredEvents: SignalSearchResponse; ruleParams: RuleTypeParams; - services: AlertServices; + services: AlertServices; logger: Logger; id: string; signalsIndex: string; @@ -222,7 +226,7 @@ export const singleBulkCreate = async ({ export const bulkInsertSignals = async ( signals: WrappedSignalHit[], logger: Logger, - services: AlertServices, + services: AlertServices, refresh: RefreshTypes ): Promise => { // index documents after creating an ID based on the diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts index 79e1f9896d63f..3a4538e8a9245 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts @@ -5,7 +5,11 @@ */ import { performance } from 'perf_hooks'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; import { Logger } from '../../../../../../../src/core/server'; import { SignalSearchResponse } from './types'; import { BuildRuleMessage } from './rule_messages'; @@ -22,7 +26,7 @@ interface SingleSearchAfterParams { index: string[]; from: string; to: string; - services: AlertServices; + services: AlertServices; logger: Logger; pageSize: number; sortOrder?: SortOrderOrUndefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts index faad51e4751e8..c328bf5bbb4ca 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts @@ -20,7 +20,11 @@ import { ItemsPerSearch, } from '../../../../../common/detection_engine/schemas/types/threat_mapping'; import { PartialFilter, RuleTypeParams } from '../../types'; -import { AlertServices } from '../../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../../alerts/server'; import { ExceptionListItemSchema } from '../../../../../../lists/common/schemas'; import { ILegacyScopedClusterClient, Logger } from '../../../../../../../../src/core/server'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; @@ -38,7 +42,7 @@ export interface CreateThreatSignalsOptions { filters: PartialFilter[]; language: LanguageOrUndefined; savedId: string | undefined; - services: AlertServices; + services: AlertServices; exceptionItems: ExceptionListItemSchema[]; gap: Duration | null; previousStartedAt: Date | null; @@ -77,7 +81,7 @@ export interface CreateThreatSignalOptions { filters: PartialFilter[]; language: LanguageOrUndefined; savedId: string | undefined; - services: AlertServices; + services: AlertServices; exceptionItems: ExceptionListItemSchema[]; gap: Duration | null; previousStartedAt: Date | null; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts index 6e7f63deb06f7..b91ad86637208 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts @@ -7,7 +7,11 @@ import { TimestampOverrideOrUndefined } from '../../../../common/detection_engine/schemas/common/schemas'; import { singleSearchAfter } from './single_search_after'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; import { Logger } from '../../../../../../../src/core/server'; import { SignalSearchResponse } from './types'; import { BuildRuleMessage } from './rule_messages'; @@ -16,7 +20,7 @@ interface FindPreviousThresholdSignalsParams { from: string; to: string; indexPattern: string[]; - services: AlertServices; + services: AlertServices; logger: Logger; ruleId: string; bucketByField: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_get_bucket_filters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_get_bucket_filters.ts index bf060da1e76b8..33eb13be6313f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_get_bucket_filters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_get_bucket_filters.ts @@ -10,7 +10,11 @@ import { Filter } from 'src/plugins/data/common'; import { ESFilter } from '../../../../../../typings/elasticsearch'; import { TimestampOverrideOrUndefined } from '../../../../common/detection_engine/schemas/common/schemas'; -import { AlertServices } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, +} from '../../../../../alerts/server'; import { Logger } from '../../../../../../../src/core/server'; import { ThresholdQueryBucket } from './types'; import { BuildRuleMessage } from './rule_messages'; @@ -20,7 +24,7 @@ interface GetThresholdBucketFiltersParams { from: string; to: string; indexPattern: string[]; - services: AlertServices; + services: AlertServices; logger: Logger; ruleId: string; bucketByField: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 62339f50d939c..5ae411678aa03 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -141,7 +141,13 @@ export type RuleExecutorOptions = AlertExecutorOptions< // since we are only increasing the strictness of params. export const isAlertExecutor = ( obj: SignalRuleAlertTypeDefinition -): obj is AlertType => { +): obj is AlertType< + RuleTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + 'default' +> => { return true; }; @@ -149,7 +155,8 @@ export type SignalRuleAlertTypeDefinition = AlertType< RuleTypeParams, AlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + 'default' >; export interface Ancestor { @@ -224,7 +231,7 @@ export interface SearchAfterAndBulkCreateParams { gap: moment.Duration | null; previousStartedAt: Date | null | undefined; ruleParams: RuleTypeParams; - services: AlertServices; + services: AlertServices; listClient: ListClient; exceptionsList: ExceptionListItemSchema[]; logger: Logger; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index ab14643f30e41..153696e85c2a5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -10,7 +10,12 @@ import dateMath from '@elastic/datemath'; import { TimestampOverrideOrUndefined } from '../../../../common/detection_engine/schemas/common/schemas'; import { Logger, SavedObjectsClientContract } from '../../../../../../../src/core/server'; -import { AlertServices, parseDuration } from '../../../../../alerts/server'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertServices, + parseDuration, +} from '../../../../../alerts/server'; import { ExceptionListClient, ListClient, ListPluginSetup } from '../../../../../lists/server'; import { ExceptionListItemSchema } from '../../../../../lists/common/schemas'; import { ListArray } from '../../../../common/detection_engine/schemas/types/lists'; @@ -52,7 +57,10 @@ export const shorthandMap = { }, }; -export const checkPrivileges = async (services: AlertServices, indices: string[]) => +export const checkPrivileges = async ( + services: AlertServices, + indices: string[] +) => services.callCluster('transport.request', { path: '/_security/user/_has_privileges', method: 'POST', @@ -154,7 +162,7 @@ export const getListsClient = ({ lists: ListPluginSetup | undefined; spaceId: string; updatedByUser: string | null; - services: AlertServices; + services: AlertServices; savedObjectClient: SavedObjectsClientContract; }): { listClient: ListClient; diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts index 0d8628d00df85..85e02b21cc78c 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts @@ -20,6 +20,7 @@ import { Query } from '../../../../../../src/plugins/data/common/query'; export const GEO_CONTAINMENT_ID = '.geo-containment'; export const ActionGroupId = 'Tracked entity contained'; +export const RecoveryActionGroupId = 'notGeoContained'; const actionVariableContextEntityIdLabel = i18n.translate( 'xpack.stackAlerts.geoContainment.actionVariableContextEntityIdLabel', @@ -141,7 +142,9 @@ export type GeoContainmentAlertType = AlertType< GeoContainmentParams, GeoContainmentState, GeoContainmentInstanceState, - GeoContainmentInstanceContext + GeoContainmentInstanceContext, + typeof ActionGroupId, + typeof RecoveryActionGroupId >; export function getAlertType(logger: Logger): GeoContainmentAlertType { @@ -161,7 +164,7 @@ export function getAlertType(logger: Logger): GeoContainmentAlertType { name: alertTypeName, actionGroups: [{ id: ActionGroupId, name: actionGroupName }], recoveryActionGroup: { - id: 'notGeoContained', + id: RecoveryActionGroupId, name: i18n.translate('xpack.stackAlerts.geoContainment.notGeoContained', { defaultMessage: 'No longer contained', }), diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts index 612eff3014985..24232e47225f0 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts @@ -100,7 +100,8 @@ export function getActiveEntriesAndGenerateAlerts( currLocationMap: Map, alertInstanceFactory: AlertServices< GeoContainmentInstanceState, - GeoContainmentInstanceContext + GeoContainmentInstanceContext, + typeof ActionGroupId >['alertInstanceFactory'], shapesIdsNamesMap: Record, currIntervalEndTime: Date diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts index 86e9e4fa3d672..21dddea5364df 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts @@ -12,6 +12,8 @@ import { GeoContainmentInstanceState, GeoContainmentInstanceContext, getAlertType, + ActionGroupId, + RecoveryActionGroupId, } from './alert_type'; interface RegisterParams { @@ -25,6 +27,8 @@ export function register(params: RegisterParams) { GeoContainmentParams, GeoContainmentState, GeoContainmentInstanceState, - GeoContainmentInstanceContext + GeoContainmentInstanceContext, + typeof ActionGroupId, + typeof RecoveryActionGroupId >(getAlertType(logger)); } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts index 2eccf2ff96fb0..27478049d4880 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts @@ -209,7 +209,8 @@ export type GeoThresholdAlertType = AlertType< GeoThresholdParams, GeoThresholdState, GeoThresholdInstanceState, - GeoThresholdInstanceContext + GeoThresholdInstanceContext, + typeof ActionGroupId >; export function getAlertType(logger: Logger): GeoThresholdAlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoThreshold.alertTypeTitle', { diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts index 9600395c78218..3a7e795bd5dbf 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.ts @@ -42,7 +42,7 @@ export const ComparatorFnNames = new Set(ComparatorFns.keys()); export function getAlertType( logger: Logger, data: Promise -): AlertType { +): AlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.indexThreshold.alertTypeTitle', { defaultMessage: 'Index threshold', }); @@ -148,7 +148,9 @@ export function getAlertType( producer: STACK_ALERTS_FEATURE_ID, }; - async function executor(options: AlertExecutorOptions) { + async function executor( + options: AlertExecutorOptions + ) { const { alertId, name, services, params } = options; const compareFn = ComparatorFns.get(params.thresholdComparator); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 1cb1a68986192..6da3a16308af3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -39,7 +39,7 @@ import { ActionGroup, AlertActionParam } from '../../../../../alerts/common'; import { useKibana } from '../../../common/lib/kibana'; import { DefaultActionParamsGetter } from '../../lib/get_defaults_for_action_params'; -export interface ActionGroupWithMessageVariables extends ActionGroup { +export interface ActionGroupWithMessageVariables extends ActionGroup { omitOptionalMessageVariables?: boolean; defaultActionMessage?: string; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx index 30ca2c620f1d7..13f1aea91c7ac 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx @@ -11,6 +11,7 @@ import { Alert, ActionType, AlertTypeModel, AlertType } from '../../../../types' import { EuiTitle, EuiBadge, EuiFlexItem, EuiSwitch, EuiButtonEmpty, EuiText } from '@elastic/eui'; import { ViewInApp } from './view_in_app'; import { + ActionGroup, AlertExecutionStatusErrorReasons, ALERTS_FEATURE_ID, } from '../../../../../../alerts/common'; @@ -47,7 +48,7 @@ const mockAlertApis = { const authorizedConsumers = { [ALERTS_FEATURE_ID]: { read: true, all: true }, }; -const recoveryActionGroup = { id: 'recovered', name: 'Recovered' }; +const recoveryActionGroup: ActionGroup<'recovered'> = { id: 'recovered', name: 'Recovered' }; describe('alert_details', () => { // mock Api handlers diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx index 893f085cd664a..8252412cd261c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx @@ -231,7 +231,7 @@ const INACTIVE_LABEL = i18n.translate( function getActionGroupName(alertType: AlertType, actionGroupId?: string): string | undefined { actionGroupId = actionGroupId || alertType.defaultActionGroupId; const actionGroup = alertType?.actionGroups?.find( - (group: ActionGroup) => group.id === actionGroupId + (group: ActionGroup) => group.id === actionGroupId ); return actionGroup?.name; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx index 8029b43a2cf53..c4d0e5d3c8e49 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.test.tsx @@ -61,7 +61,7 @@ describe('alert_conditions', () => { const ConditionForm = ({ actionGroup, }: { - actionGroup?: ActionGroupWithCondition<{ someProp: string }>; + actionGroup?: ActionGroupWithCondition<{ someProp: string }, string>; }) => { return ( @@ -113,7 +113,7 @@ describe('alert_conditions', () => { const ConditionForm = ({ actionGroup, }: { - actionGroup?: ActionGroupWithCondition<{ someProp: string }>; + actionGroup?: ActionGroupWithCondition<{ someProp: string }, string>; }) => { return ( @@ -165,7 +165,7 @@ describe('alert_conditions', () => { const ConditionForm = ({ actionGroup, }: { - actionGroup?: ActionGroupWithCondition<{ someProp: string }>; + actionGroup?: ActionGroupWithCondition<{ someProp: string }, string>; }) => { return ( @@ -218,8 +218,10 @@ describe('alert_conditions', () => { actionGroup, someCallbackProp, }: { - actionGroup?: ActionGroupWithCondition<{ someProp: string }>; - someCallbackProp: (actionGroup: ActionGroupWithCondition<{ someProp: string }>) => void; + actionGroup?: ActionGroupWithCondition<{ someProp: string }, string>; + someCallbackProp: ( + actionGroup: ActionGroupWithCondition<{ someProp: string }, string> + ) => void; }) => { if (!actionGroup) { return
; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx index 1eb086dd6a2c5..63b654ea1a225 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions.tsx @@ -11,7 +11,10 @@ import { ActionGroup, getBuiltinActionGroups } from '../../../../../alerts/commo const BUILT_IN_ACTION_GROUPS: Set = new Set(getBuiltinActionGroups().map(({ id }) => id)); -export type ActionGroupWithCondition = ActionGroup & +export type ActionGroupWithCondition< + T, + ActionGroupIds extends string +> = ActionGroup & ( | // allow isRequired=false with or without conditions { @@ -25,22 +28,26 @@ export type ActionGroupWithCondition = ActionGroup & } ); -export interface AlertConditionsProps { +export interface AlertConditionsProps { headline?: string; - actionGroups: Array>; - onInitializeConditionsFor?: (actionGroup: ActionGroupWithCondition) => void; - onResetConditionsFor?: (actionGroup: ActionGroupWithCondition) => void; + actionGroups: Array>; + onInitializeConditionsFor?: ( + actionGroup: ActionGroupWithCondition + ) => void; + onResetConditionsFor?: ( + actionGroup: ActionGroupWithCondition + ) => void; includeBuiltInActionGroups?: boolean; } -export const AlertConditions = ({ +export const AlertConditions = ({ headline, actionGroups, onInitializeConditionsFor, onResetConditionsFor, includeBuiltInActionGroups = false, children, -}: PropsWithChildren>) => { +}: PropsWithChildren>) => { const [withConditions, withoutConditions] = partition( includeBuiltInActionGroups ? actionGroups diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx index 879f276317503..f92891750c468 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_conditions_group.tsx @@ -9,8 +9,8 @@ import { EuiFormRow, EuiButtonIcon, EuiTitle } from '@elastic/eui'; import { AlertConditionsProps, ActionGroupWithCondition } from './alert_conditions'; export type AlertConditionsGroupProps = { - actionGroup?: ActionGroupWithCondition; -} & Pick, 'onResetConditionsFor'>; + actionGroup?: ActionGroupWithCondition; +} & Pick, 'onResetConditionsFor'>; export const AlertConditionsGroup = ({ actionGroup, diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 3ba50ae908631..0cf859ad64173 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -155,9 +155,11 @@ export const OPTIONAL_ACTION_VARIABLES = ['context'] as const; export type ActionVariables = AsActionVariables & Partial>; -export interface AlertType - extends Pick< - CommonAlertType, +export interface AlertType< + ActionGroupIds extends string = string, + RecoveryActionGroupId extends string = string +> extends Pick< + CommonAlertType, | 'id' | 'name' | 'actionGroups' @@ -184,7 +186,8 @@ export interface AlertTableItem extends Alert { export interface AlertTypeParamsExpressionProps< Params extends AlertTypeParams = AlertTypeParams, - MetaData = Record + MetaData = Record, + ActionGroupIds extends string = string > { alertParams: Params; alertInterval: string; @@ -196,7 +199,7 @@ export interface AlertTypeParamsExpressionProps< ) => void; errors: IErrorObject; defaultActionGroupId: string; - actionGroups: ActionGroup[]; + actionGroups: Array>; metadata?: MetaData; charts: ChartsPluginSetup; data: DataPublicPluginStart; diff --git a/x-pack/plugins/uptime/common/constants/alerts.ts b/x-pack/plugins/uptime/common/constants/alerts.ts index 61a7a02bf8b30..afb3c82bcf496 100644 --- a/x-pack/plugins/uptime/common/constants/alerts.ts +++ b/x-pack/plugins/uptime/common/constants/alerts.ts @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -interface ActionGroupDefinition { - id: string; - name: string; -} +import { ActionGroup } from '../../../alerts/common'; -type ActionGroupDefinitions = Record; - -export const ACTION_GROUP_DEFINITIONS: ActionGroupDefinitions = { +export const ACTION_GROUP_DEFINITIONS: { + MONITOR_STATUS: ActionGroup<'xpack.uptime.alerts.actionGroups.monitorStatus'>; + TLS: ActionGroup<'xpack.uptime.alerts.actionGroups.tls'>; + DURATION_ANOMALY: ActionGroup<'xpack.uptime.alerts.actionGroups.durationAnomaly'>; +} = { MONITOR_STATUS: { id: 'xpack.uptime.alerts.actionGroups.monitorStatus', name: 'Uptime Down Monitor', diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts index d5fd92476dd16..d88f3de902218 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts @@ -874,7 +874,13 @@ describe('status check alert', () => { }); describe('alert factory', () => { - let alert: AlertType; + let alert: AlertType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + 'xpack.uptime.alerts.actionGroups.monitorStatus' + >; beforeEach(() => { const { server, libs, plugins } = bootstrapDependencies(); diff --git a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts index f5e79ad43336b..b8acf347a2a6d 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/duration_anomaly.ts @@ -7,6 +7,7 @@ import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; import moment from 'moment'; import { schema } from '@kbn/config-schema'; +import { ActionGroupIdsOf } from '../../../../alerts/common'; import { updateState } from './common'; import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants/alerts'; import { commonStateTranslations, durationAnomalyTranslations } from './translations'; @@ -20,6 +21,7 @@ import { getLatestMonitor } from '../requests/get_latest_monitor'; import { uptimeAlertWrapper } from './uptime_alert_wrapper'; const { DURATION_ANOMALY } = ACTION_GROUP_DEFINITIONS; +export type ActionGroupIds = ActionGroupIdsOf; export const getAnomalySummary = (anomaly: AnomaliesTableRecord, monitorInfo: Ping) => { return { @@ -61,8 +63,12 @@ const getAnomalies = async ( ); }; -export const durationAnomalyAlertFactory: UptimeAlertTypeFactory = (_server, _libs, plugins) => - uptimeAlertWrapper({ +export const durationAnomalyAlertFactory: UptimeAlertTypeFactory = ( + _server, + _libs, + plugins +) => + uptimeAlertWrapper({ id: 'xpack.uptime.alerts.durationAnomaly', name: durationAnomalyTranslations.alertFactoryName, validate: { diff --git a/x-pack/plugins/uptime/server/lib/alerts/index.ts b/x-pack/plugins/uptime/server/lib/alerts/index.ts index c8d3037f98aeb..0b4ff0b522396 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/index.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/index.ts @@ -5,12 +5,15 @@ */ import { UptimeAlertTypeFactory } from './types'; -import { statusCheckAlertFactory } from './status_check'; -import { tlsAlertFactory } from './tls'; -import { durationAnomalyAlertFactory } from './duration_anomaly'; - -export const uptimeAlertTypeFactories: UptimeAlertTypeFactory[] = [ - statusCheckAlertFactory, - tlsAlertFactory, +import { statusCheckAlertFactory, ActionGroupIds as statusCheckActionGroup } from './status_check'; +import { tlsAlertFactory, ActionGroupIds as tlsActionGroup } from './tls'; +import { durationAnomalyAlertFactory, -]; + ActionGroupIds as durationAnomalyActionGroup, +} from './duration_anomaly'; + +export const uptimeAlertTypeFactories: [ + UptimeAlertTypeFactory, + UptimeAlertTypeFactory, + UptimeAlertTypeFactory +] = [statusCheckAlertFactory, tlsAlertFactory, durationAnomalyAlertFactory]; diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 56ca7a85784c5..1bcad155bd0dc 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -7,6 +7,7 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import Mustache from 'mustache'; +import { ActionGroupIdsOf } from '../../../../alerts/common'; import { UptimeAlertTypeFactory } from './types'; import { esKuery } from '../../../../../../src/plugins/data/server'; import { JsonObject } from '../../../../../../src/plugins/kibana_utils/common'; @@ -28,6 +29,7 @@ import { getUptimeIndexPattern, IndexPatternTitleAndFields } from '../requests/g import { UMServerLibs, UptimeESClient } from '../lib'; const { MONITOR_STATUS } = ACTION_GROUP_DEFINITIONS; +export type ActionGroupIds = ActionGroupIdsOf; const getMonIdByLoc = (monitorId: string, location: string) => { return monitorId + '-' + location; @@ -178,8 +180,8 @@ const getInstanceId = (monitorInfo: Ping, monIdByLoc: string) => { return `${urlText}_${monIdByLoc}`; }; -export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) => - uptimeAlertWrapper({ +export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) => + uptimeAlertWrapper({ id: 'xpack.uptime.alerts.monitorStatus', name: i18n.translate('xpack.uptime.alerts.monitorStatus', { defaultMessage: 'Uptime monitor status', diff --git a/x-pack/plugins/uptime/server/lib/alerts/tls.ts b/x-pack/plugins/uptime/server/lib/alerts/tls.ts index b6501f7d92059..f138e2799aa3c 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/tls.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/tls.ts @@ -14,8 +14,10 @@ import { Cert, CertResult } from '../../../common/runtime_types'; import { commonStateTranslations, tlsTranslations } from './translations'; import { DEFAULT_FROM, DEFAULT_TO } from '../../rest_api/certs/certs'; import { uptimeAlertWrapper } from './uptime_alert_wrapper'; +import { ActionGroupIdsOf } from '../../../../alerts/common'; const { TLS } = ACTION_GROUP_DEFINITIONS; +export type ActionGroupIds = ActionGroupIdsOf; const DEFAULT_SIZE = 20; @@ -82,8 +84,8 @@ export const getCertSummary = ( }; }; -export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, libs) => - uptimeAlertWrapper({ +export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, libs) => + uptimeAlertWrapper({ id: 'xpack.uptime.alerts.tls', name: tlsTranslations.alertFactoryName, validate: { diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts index d143e33fb8e96..36f5bc1973d33 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/types.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts @@ -10,7 +10,7 @@ import { AlertType, AlertInstanceState, AlertInstanceContext } from '../../../.. export type UptimeAlertTypeParam = Record; export type UptimeAlertTypeState = Record; -export type UptimeAlertTypeFactory = ( +export type UptimeAlertTypeFactory = ( server: UptimeCoreSetup, libs: UMServerLibs, plugins: UptimeCorePlugins @@ -18,5 +18,6 @@ export type UptimeAlertTypeFactory = ( UptimeAlertTypeParam, UptimeAlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + ActionGroupIds >; diff --git a/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts b/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts index a4a2f2c64db1b..85770144e7379 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts @@ -15,8 +15,8 @@ import { DynamicSettings } from '../../../common/runtime_types'; import { createUptimeESClient, UptimeESClient } from '../lib'; import { UptimeAlertTypeFactory, UptimeAlertTypeParam, UptimeAlertTypeState } from './types'; -export interface UptimeAlertType - extends Omit, 'executor' | 'producer'> { +export interface UptimeAlertType + extends Omit>, 'executor' | 'producer'> { executor: ({ options, uptimeEsClient, @@ -26,7 +26,8 @@ export interface UptimeAlertType UptimeAlertTypeParam, UptimeAlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + ActionGroupIds >; uptimeEsClient: UptimeESClient; dynamicSettings: DynamicSettings; @@ -34,7 +35,9 @@ export interface UptimeAlertType }) => Promise; } -export const uptimeAlertWrapper = (uptimeAlert: UptimeAlertType) => ({ +export const uptimeAlertWrapper = ( + uptimeAlert: UptimeAlertType +) => ({ ...uptimeAlert, producer: 'uptime', executor: async ( @@ -42,7 +45,8 @@ export const uptimeAlertWrapper = (uptimeAlert: UptimeAlertType) => ({ UptimeAlertTypeParam, UptimeAlertTypeState, AlertInstanceState, - AlertInstanceContext + AlertInstanceContext, + ActionGroupIds > ) => { const { diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index 30c19f735b75d..d5b952e991b30 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -61,7 +61,13 @@ function getAlwaysFiringAlertType() { interface InstanceContext extends AlertInstanceContext { instanceContextValue: boolean; } - const result: AlertType = { + const result: AlertType< + ParamsType & AlertTypeParams, + State, + InstanceState, + InstanceContext, + 'default' | 'other' + > = { id: 'test.always-firing', name: 'Test: Always Firing', actionGroups: [ @@ -149,7 +155,7 @@ function getCumulativeFiringAlertType() { interface InstanceState extends AlertInstanceState { instanceStateValue: boolean; } - const result: AlertType<{}, State, InstanceState, {}> = { + const result: AlertType<{}, State, InstanceState, {}, 'default' | 'other'> = { id: 'test.cumulative-firing', name: 'Test: Cumulative Firing', actionGroups: [ @@ -189,7 +195,7 @@ function getNeverFiringAlertType() { interface State extends AlertTypeState { globalStateValue: boolean; } - const result: AlertType = { + const result: AlertType = { id: 'test.never-firing', name: 'Test: Never firing', actionGroups: [ @@ -229,7 +235,7 @@ function getFailingAlertType() { reference: schema.string(), }); type ParamsType = TypeOf; - const result: AlertType = { + const result: AlertType = { id: 'test.failing', name: 'Test: Failing', validate: { @@ -271,7 +277,7 @@ function getAuthorizationAlertType(core: CoreSetup) { reference: schema.string(), }); type ParamsType = TypeOf; - const result: AlertType = { + const result: AlertType = { id: 'test.authorization', name: 'Test: Authorization', actionGroups: [ @@ -358,7 +364,7 @@ function getValidationAlertType() { param1: schema.string(), }); type ParamsType = TypeOf; - const result: AlertType = { + const result: AlertType = { id: 'test.validation', name: 'Test: Validation', actionGroups: [ @@ -390,7 +396,7 @@ function getPatternFiringAlertType() { interface State extends AlertTypeState { patternIndex?: number; } - const result: AlertType = { + const result: AlertType = { id: 'test.patternFiring', name: 'Test: Firing on a Pattern', actionGroups: [{ id: 'default', name: 'Default' }], @@ -454,7 +460,7 @@ export function defineAlertTypes( core: CoreSetup, { alerts }: Pick ) { - const noopAlertType: AlertType = { + const noopAlertType: AlertType<{}, {}, {}, {}, 'default'> = { id: 'test.noop', name: 'Test: Noop', actionGroups: [{ id: 'default', name: 'Default' }], @@ -463,7 +469,7 @@ export function defineAlertTypes( minimumLicenseRequired: 'basic', async executor() {}, }; - const goldNoopAlertType: AlertType = { + const goldNoopAlertType: AlertType<{}, {}, {}, {}, 'default'> = { id: 'test.gold.noop', name: 'Test: Noop', actionGroups: [{ id: 'default', name: 'Default' }], @@ -472,7 +478,7 @@ export function defineAlertTypes( minimumLicenseRequired: 'gold', async executor() {}, }; - const onlyContextVariablesAlertType: AlertType = { + const onlyContextVariablesAlertType: AlertType<{}, {}, {}, {}, 'default'> = { id: 'test.onlyContextVariables', name: 'Test: Only Context Variables', actionGroups: [{ id: 'default', name: 'Default' }], @@ -484,7 +490,7 @@ export function defineAlertTypes( }, async executor() {}, }; - const onlyStateVariablesAlertType: AlertType = { + const onlyStateVariablesAlertType: AlertType<{}, {}, {}, {}, 'default'> = { id: 'test.onlyStateVariables', name: 'Test: Only State Variables', actionGroups: [{ id: 'default', name: 'Default' }], @@ -496,7 +502,7 @@ export function defineAlertTypes( minimumLicenseRequired: 'basic', async executor() {}, }; - const throwAlertType: AlertType = { + const throwAlertType: AlertType<{}, {}, {}, {}, 'default'> = { id: 'test.throw', name: 'Test: Throw', actionGroups: [ @@ -512,7 +518,7 @@ export function defineAlertTypes( throw new Error('this alert is intended to fail'); }, }; - const longRunningAlertType: AlertType = { + const longRunningAlertType: AlertType<{}, {}, {}, {}, 'default'> = { id: 'test.longRunning', name: 'Test: Long Running', actionGroups: [ diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/alert_types.ts index 3a81d41a2ca9c..f1821699642a7 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts_restricted/server/alert_types.ts @@ -6,13 +6,13 @@ import { CoreSetup } from 'src/core/server'; import { FixtureStartDeps, FixtureSetupDeps } from './plugin'; -import { AlertType, AlertExecutorOptions } from '../../../../../../../plugins/alerts/server'; +import { AlertType } from '../../../../../../../plugins/alerts/server'; export function defineAlertTypes( core: CoreSetup, { alerts }: Pick ) { - const noopRestrictedAlertType: AlertType = { + const noopRestrictedAlertType: AlertType<{}, {}, {}, {}, 'default', 'restrictedRecovered'> = { id: 'test.restricted-noop', name: 'Test: Restricted Noop', actionGroups: [{ id: 'default', name: 'Default' }], @@ -20,16 +20,16 @@ export function defineAlertTypes( defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', recoveryActionGroup: { id: 'restrictedRecovered', name: 'Restricted Recovery' }, - async executor({ services, params, state }: AlertExecutorOptions) {}, + async executor() {}, }; - const noopUnrestrictedAlertType: AlertType = { + const noopUnrestrictedAlertType: AlertType<{}, {}, {}, {}, 'default'> = { id: 'test.unrestricted-noop', name: 'Test: Unrestricted Noop', actionGroups: [{ id: 'default', name: 'Default' }], producer: 'alertsRestrictedFixture', defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', - async executor({ services, params, state }: AlertExecutorOptions) {}, + async executor() {}, }; alerts.registerType(noopRestrictedAlertType); alerts.registerType(noopUnrestrictedAlertType); diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts index cf09286fe1ba6..1e334051e4fee 100644 --- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts @@ -17,7 +17,7 @@ export interface AlertingExampleDeps { features: FeaturesPluginSetup; } -export const noopAlertType: AlertType = { +export const noopAlertType: AlertType<{}, {}, {}, {}, 'default'> = { id: 'test.noop', name: 'Test: Noop', actionGroups: [{ id: 'default', name: 'Default' }], @@ -33,7 +33,9 @@ export const alwaysFiringAlertType: AlertType< globalStateValue: boolean; groupInSeriesIndex: number; }, - { instanceStateValue: boolean; globalStateValue: boolean; groupInSeriesIndex: number } + { instanceStateValue: boolean; globalStateValue: boolean; groupInSeriesIndex: number }, + never, + 'default' | 'other' > = { id: 'test.always-firing', name: 'Always Firing', @@ -61,7 +63,7 @@ export const alwaysFiringAlertType: AlertType< }, }; -export const failingAlertType: AlertType = { +export const failingAlertType: AlertType = { id: 'test.failing', name: 'Test: Failing', actionGroups: [