From c4863a4e7e7408ee8924ce7575d143c854a54d5f Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Fri, 18 Dec 2020 10:49:24 +0000 Subject: [PATCH 01/13] removed defaults from AlertType --- .../alerts/server/alert_type_registry.ts | 35 ++++++--- .../server/alerts_client/alerts_client.ts | 8 +- .../lib/validate_alert_type_params.test.ts | 12 +++ .../server/lib/validate_alert_type_params.ts | 5 +- .../create_execution_handler.test.ts | 52 +++++++------ .../task_runner/create_execution_handler.ts | 6 +- .../server/task_runner/task_runner.test.ts | 74 +++++++++---------- .../alerts/server/task_runner/task_runner.ts | 6 +- .../task_runner/task_runner_factory.test.ts | 4 +- .../server/task_runner/task_runner_factory.ts | 4 +- x-pack/plugins/alerts/server/types.ts | 15 +++- ...r_inventory_metric_threshold_alert_type.ts | 12 ++- .../register_metric_threshold_alert_type.ts | 12 ++- .../detection_engine/notifications/types.ts | 10 ++- .../lib/detection_engine/signals/types.ts | 12 ++- .../alert_types/geo_containment/alert_type.ts | 11 ++- .../alert_types/geo_threshold/alert_type.ts | 11 ++- .../lib/alerts/__tests__/status_check.test.ts | 10 ++- .../plugins/uptime/server/lib/alerts/types.ts | 10 ++- .../server/lib/alerts/uptime_alert_wrapper.ts | 15 +++- 20 files changed, 213 insertions(+), 111 deletions(-) diff --git a/x-pack/plugins/alerts/server/alert_type_registry.ts b/x-pack/plugins/alerts/server/alert_type_registry.ts index d436d1987c027..fd691da5732d4 100644 --- a/x-pack/plugins/alerts/server/alert_type_registry.ts +++ b/x-pack/plugins/alerts/server/alert_type_registry.ts @@ -32,7 +32,7 @@ export interface ConstructorOptions { export interface RegistryAlertType extends Pick< - NormalizedAlertType, + UntypedNormalizedAlertType, | 'name' | 'actionGroups' | 'recoveryActionGroup' @@ -66,16 +66,23 @@ const alertIdSchema = schema.string({ }); export type NormalizedAlertType< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext > = Omit, 'recoveryActionGroup'> & Pick>, 'recoveryActionGroup'>; +export type UntypedNormalizedAlertType = NormalizedAlertType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + export class AlertTypeRegistry { private readonly taskManager: TaskManagerSetupContract; - private readonly alertTypes: Map = new Map(); + private readonly alertTypes: Map = new Map(); private readonly taskRunnerFactory: TaskRunnerFactory; private readonly licenseState: ILicenseState; private readonly licensing: LicensingPluginSetup; @@ -113,14 +120,22 @@ export class AlertTypeRegistry { } alertType.actionVariables = normalizedActionVariables(alertType.actionVariables); - const normalizedAlertType = augmentActionGroupsWithReserved(alertType as AlertType); + const normalizedAlertType = augmentActionGroupsWithReserved< + Params, + State, + InstanceState, + InstanceContext + >(alertType); - this.alertTypes.set(alertIdSchema.validate(alertType.id), normalizedAlertType); + this.alertTypes.set( + alertIdSchema.validate(alertType.id), + normalizedAlertType as UntypedNormalizedAlertType + ); this.taskManager.registerTaskDefinitions({ [`alerting:${alertType.id}`]: { title: alertType.name, createTaskRunner: (context: RunContext) => - this.taskRunnerFactory.create(normalizedAlertType, context), + this.taskRunnerFactory.create(normalizedAlertType as UntypedNormalizedAlertType, context), }, }); // No need to notify usage on basic alert types @@ -170,7 +185,7 @@ export class AlertTypeRegistry { producer, minimumLicenseRequired, }, - ]: [string, NormalizedAlertType]) => ({ + ]: [string, UntypedNormalizedAlertType]) => ({ id, name, actionGroups, diff --git a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index f21cd2b02943a..b4d52a6f1ccfb 100644 --- a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -23,7 +23,6 @@ import { RawAlert, AlertTypeRegistry, AlertAction, - AlertType, IntervalSchedule, SanitizedAlert, AlertTaskState, @@ -44,7 +43,7 @@ import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/se import { TaskManagerStartContract } from '../../../task_manager/server'; import { taskInstanceToAlertTaskInstance } from '../task_runner/alert_task_instance'; import { deleteTaskIfItExists } from '../lib/delete_task_if_it_exists'; -import { RegistryAlertType } from '../alert_type_registry'; +import { RegistryAlertType, UntypedNormalizedAlertType } from '../alert_type_registry'; import { AlertsAuthorization, WriteOperations, ReadOperations } from '../authorization'; import { IEventLogClient } from '../../../../plugins/event_log/server'; import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date'; @@ -1386,7 +1385,10 @@ export class AlertsClient { }; } - private validateActions(alertType: AlertType, actions: NormalizedAlertAction[]): void { + private validateActions( + alertType: UntypedNormalizedAlertType, + actions: NormalizedAlertAction[] + ): void { const { actionGroups: alertTypeActionGroups } = alertType; const usedAlertActionGroups = actions.map((action) => action.group); const availableAlertTypeActionGroups = new Set(map(alertTypeActionGroups, 'id')); diff --git a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts index 2814eaef3e02a..c2034abb09842 100644 --- a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts +++ b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts @@ -19,6 +19,10 @@ test('should return passed in params when validation not defined', () => { }, ], defaultActionGroupId: 'default', + recoveryActionGroup: { + id: 'recovery', + name: 'Recovery', + }, minimumLicenseRequired: 'basic', async executor() {}, producer: 'alerts', @@ -42,6 +46,10 @@ test('should validate and apply defaults when params is valid', () => { }, ], defaultActionGroupId: 'default', + recoveryActionGroup: { + id: 'recovery', + name: 'Recovery', + }, minimumLicenseRequired: 'basic', validate: { params: schema.object({ @@ -73,6 +81,10 @@ test('should validate and throw error when params is invalid', () => { }, ], defaultActionGroupId: 'default', + recoveryActionGroup: { + id: 'recove', + name: 'Recove', + }, minimumLicenseRequired: 'basic', validate: { params: schema.object({ diff --git a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts index a443143d8cbde..ce91769d35fe5 100644 --- a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts +++ b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts @@ -5,10 +5,11 @@ */ import Boom from '@hapi/boom'; -import { AlertType, AlertExecutorOptions } from '../types'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; +import { AlertExecutorOptions } from '../types'; export function validateAlertTypeParams( - alertType: AlertType, + alertType: UntypedNormalizedAlertType, params: Record ): AlertExecutorOptions['params'] { const validator = alertType.validate && alertType.validate.params; 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 b414e726f0101..276ef1d87d07d 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 @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertType } from '../types'; -import { createExecutionHandler } from './create_execution_handler'; +import { createExecutionHandler, CreateExecutionHandlerOptions } from './create_execution_handler'; import { loggingSystemMock } from '../../../../../src/core/server/mocks'; import { actionsMock, @@ -16,12 +15,13 @@ 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'; jest.mock('./inject_action_params', () => ({ injectActionParams: jest.fn(), })); -const alertType: AlertType = { +const alertType: UntypedNormalizedAlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -39,18 +39,19 @@ const alertType: AlertType = { }; const actionsClient = actionsClientMock.create(); -const createExecutionHandlerParams = { - actionsPlugin: actionsMock.createStart(), + +const mockActionsPlugin = actionsMock.createStart(); +const mockEventLogger = eventLoggerMock.create(); +const createExecutionHandlerParams: jest.Mocked = { + actionsPlugin: mockActionsPlugin, spaceId: 'default', alertId: '1', alertName: 'name-of-alert', tags: ['tag-A', 'tag-B'], apiKey: 'MTIzOmFiYw==', - spaceIdToNamespace: jest.fn().mockReturnValue(undefined), - getBasePath: jest.fn().mockReturnValue(undefined), alertType, logger: loggingSystemMock.create().get(), - eventLogger: eventLoggerMock.create(), + eventLogger: mockEventLogger, actions: [ { id: '1', @@ -79,12 +80,10 @@ beforeEach(() => { .injectActionParams.mockImplementation( ({ actionParams }: InjectActionParamsOpts) => actionParams ); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); - createExecutionHandlerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( - actionsClient - ); - createExecutionHandlerParams.actionsPlugin.renderActionParameterTemplates.mockImplementation( + mockActionsPlugin.isActionTypeEnabled.mockReturnValue(true); + mockActionsPlugin.isActionExecutable.mockReturnValue(true); + mockActionsPlugin.getActionsClientWithRequest.mockResolvedValue(actionsClient); + mockActionsPlugin.renderActionParameterTemplates.mockImplementation( renderActionParameterTemplatesDefault ); }); @@ -97,9 +96,9 @@ test('enqueues execution per selected action', async () => { context: {}, alertInstanceId: '2', }); - expect( - createExecutionHandlerParams.actionsPlugin.getActionsClientWithRequest - ).toHaveBeenCalledWith(createExecutionHandlerParams.request); + expect(mockActionsPlugin.getActionsClientWithRequest).toHaveBeenCalledWith( + createExecutionHandlerParams.request + ); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` Array [ @@ -124,9 +123,8 @@ test('enqueues execution per selected action', async () => { ] `); - const eventLogger = createExecutionHandlerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` + expect(mockEventLogger.logEvent).toHaveBeenCalledTimes(1); + expect(mockEventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` Array [ Array [ Object { @@ -171,9 +169,9 @@ test('enqueues execution per selected action', async () => { test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => { // Mock two calls, one for check against actions[0] and the second for actions[1] - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValueOnce(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValueOnce(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValueOnce(true); + mockActionsPlugin.isActionExecutable.mockReturnValueOnce(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValueOnce(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValueOnce(true); const executionHandler = createExecutionHandler({ ...createExecutionHandlerParams, actions: [ @@ -214,9 +212,9 @@ test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => }); test('trow error error message when action type is disabled', async () => { - createExecutionHandlerParams.actionsPlugin.preconfiguredActions = []; - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValue(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(false); + mockActionsPlugin.preconfiguredActions = []; + mockActionsPlugin.isActionExecutable.mockReturnValue(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValue(false); const executionHandler = createExecutionHandler({ ...createExecutionHandlerParams, actions: [ @@ -243,7 +241,7 @@ test('trow error error message when action type is disabled', async () => { expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(0); - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockImplementation(() => true); + mockActionsPlugin.isActionExecutable.mockImplementation(() => true); const executionHandlerForPreconfiguredAction = createExecutionHandler({ ...createExecutionHandlerParams, actions: [...createExecutionHandlerParams.actions], 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 8c7ad79483194..f9bb269e9b22e 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 @@ -17,12 +17,12 @@ import { AlertAction, AlertInstanceState, AlertInstanceContext, - AlertType, AlertTypeParams, RawAlert, } from '../types'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; -interface CreateExecutionHandlerOptions { +export interface CreateExecutionHandlerOptions { alertId: string; alertName: string; tags?: string[]; @@ -30,7 +30,7 @@ interface CreateExecutionHandlerOptions { actions: AlertAction[]; spaceId: string; apiKey: RawAlert['apiKey']; - alertType: AlertType; + alertType: UntypedNormalizedAlertType; logger: Logger; eventLogger: IEventLogger; request: KibanaRequest; 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 7545f9a18c4ce..2ddc65fba2eab 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 @@ -28,9 +28,9 @@ import { IEventLogger } from '../../../event_log/server'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import { Alert, RecoveredActionGroup } from '../../common'; import { omit } from 'lodash'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; import { alertTypeRegistryMock } from '../alert_type_registry.mock'; -const alertType = { +const alertType: jest.Mocked = { id: 'test', name: 'My test alert', actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], @@ -150,7 +150,7 @@ describe('Task Runner', () => { test('successfully executes the task', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -239,14 +239,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices .alertInstanceFactory('1') .scheduleActionsWithSubGroup('default', 'subDefault'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -392,12 +392,12 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -501,13 +501,13 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertInstanceFactory('2').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -547,12 +547,12 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -641,12 +641,12 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -681,14 +681,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices .alertInstanceFactory('1') .scheduleActionsWithSubGroup('default', 'subgroup1'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -729,12 +729,12 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -897,12 +897,12 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -997,12 +997,12 @@ describe('Task Runner', () => { }; alertTypeWithCustomRecovery.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertTypeWithCustomRecovery as NormalizedAlertType, + alertTypeWithCustomRecovery, { ...mockedTaskInstance, state: { @@ -1088,13 +1088,13 @@ describe('Task Runner', () => { test('persists alertInstances passed in from state, only if they are scheduled for execution', async () => { alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const date = new Date().toISOString(); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -1224,7 +1224,7 @@ describe('Task Runner', () => { param1: schema.string(), }), }, - } as NormalizedAlertType, + }, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1252,7 +1252,7 @@ describe('Task Runner', () => { test('uses API key when provided', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1285,7 +1285,7 @@ describe('Task Runner', () => { test(`doesn't use API key when not provided`, async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1315,7 +1315,7 @@ describe('Task Runner', () => { test('rescheduled the Alert if the schedule has update during a task run', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1350,13 +1350,13 @@ describe('Task Runner', () => { test('recovers gracefully when the AlertType executor throws an exception', async () => { alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { throw new Error('OMG'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1423,7 +1423,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1482,7 +1482,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1549,7 +1549,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1616,7 +1616,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1686,7 +1686,7 @@ describe('Task Runner', () => { const legacyTaskInstance = omit(mockedTaskInstance, 'schedule'); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, legacyTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1718,13 +1718,13 @@ describe('Task Runner', () => { }; alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ services: executorServices }: AlertExecutorOptions) => { throw new Error('OMG'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: originalAlertSate, @@ -1755,7 +1755,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); 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 17ab090610745..5c530c414bbe9 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -40,7 +40,7 @@ import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error import { AlertsClient } from '../alerts_client'; import { partiallyUpdateAlert } from '../saved_objects'; import { ActionGroup } from '../../common'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; const FALLBACK_RETRY_INTERVAL = '5m'; @@ -59,11 +59,11 @@ export class TaskRunner { private context: TaskRunnerContext; private logger: Logger; private taskInstance: AlertTaskInstance; - private alertType: NormalizedAlertType; + private alertType: UntypedNormalizedAlertType; private readonly alertTypeRegistry: AlertTypeRegistry; constructor( - alertType: NormalizedAlertType, + alertType: UntypedNormalizedAlertType, taskInstance: ConcreteTaskInstance, context: TaskRunnerContext ) { diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts index 6c58b64fffa92..3a5a130f582ed 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts @@ -16,10 +16,10 @@ import { import { actionsMock } from '../../../actions/server/mocks'; import { alertsMock, alertsClientMock } from '../mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; import { alertTypeRegistryMock } from '../alert_type_registry.mock'; -const alertType: NormalizedAlertType = { +const alertType: UntypedNormalizedAlertType = { id: 'test', name: 'My test alert', actionGroups: [{ id: 'default', name: 'Default' }], 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 1fe94972bd4b0..e266608d80880 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 @@ -17,7 +17,7 @@ import { AlertTypeRegistry, GetServicesFunction, SpaceIdToNamespaceFunction } fr import { TaskRunner } from './task_runner'; import { IEventLogger } from '../../../event_log/server'; import { AlertsClient } from '../alerts_client'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; export interface TaskRunnerContext { logger: Logger; @@ -44,7 +44,7 @@ export class TaskRunnerFactory { this.taskRunnerContext = taskRunnerContext; } - public create(alertType: NormalizedAlertType, { taskInstance }: RunContext) { + public create(alertType: UntypedNormalizedAlertType, { taskInstance }: RunContext) { if (!this.isInitialized) { throw new Error('TaskRunnerFactory not initialized'); } diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 8704068c3e51a..7cad2b585462b 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -96,10 +96,10 @@ export type ExecutorType< ) => Promise; export interface AlertType< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > { id: string; name: string; @@ -119,6 +119,13 @@ export interface AlertType< minimumLicenseRequired: LicenseType; } +export type UntypedAlertType = AlertType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + export interface RawAlertAction extends SavedObjectAttributes { group: string; actionRef: string; 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 6ec6210ecb344..ef7a80311b925 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 @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { AlertType } from '../../../../../alerts/server'; +import { + AlertType, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../../alerts/server'; import { createInventoryMetricThresholdExecutor, FIRED_ACTIONS, @@ -39,7 +45,9 @@ const condition = schema.object({ ), }); -export const registerMetricInventoryThresholdAlertType = (libs: InfraBackendLibs): AlertType => ({ +export const registerMetricInventoryThresholdAlertType = ( + libs: InfraBackendLibs +): AlertType => ({ id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, name: 'Inventory', validate: { 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 1a10765eaf734..134e7c336394d 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 @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { AlertType } from '../../../../../alerts/server'; +import { + AlertType, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../../alerts/server'; import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api/metrics_explorer'; import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; import { METRIC_THRESHOLD_ALERT_TYPE_ID, Comparator } from './types'; @@ -20,7 +26,9 @@ import { thresholdActionVariableDescription, } from '../common/messages'; -export function registerMetricThresholdAlertType(libs: InfraBackendLibs): AlertType { +export function registerMetricThresholdAlertType( + libs: InfraBackendLibs +): AlertType { const baseCriterion = { threshold: schema.arrayOf(schema.number()), comparator: oneOfLiterals(Object.values(Comparator)), 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 cc9fb149a7e1b..c3cde1b319646 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 @@ -8,7 +8,10 @@ import { AlertsClient, PartialAlert, AlertType, + AlertTypeParams, AlertTypeState, + AlertInstanceState, + AlertInstanceContext, AlertExecutorOptions, } from '../../../../../alerts/server'; import { Alert } from '../../../../../alerts/common'; @@ -97,11 +100,14 @@ export type NotificationExecutorOptions = Omit & // since we are only increasing the strictness of params. export const isNotificationAlertExecutor = ( obj: NotificationAlertTypeDefinition -): obj is AlertType => { +): obj is AlertType => { return true; }; -export type NotificationAlertTypeDefinition = Omit & { +export type NotificationAlertTypeDefinition = Omit< + AlertType, + 'executor' +> & { executor: ({ services, params, 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 4167d056df885..38a8ebfd06396 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 @@ -10,7 +10,10 @@ import { Status } from '../../../../common/detection_engine/schemas/common/schem import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; import { AlertType, + AlertTypeParams, AlertTypeState, + AlertInstanceState, + AlertInstanceContext, AlertExecutorOptions, AlertServices, } from '../../../../../alerts/server'; @@ -134,11 +137,16 @@ export type RuleExecutorOptions = Omit & { // This returns true because by default a RuleAlertTypeDefinition is an AlertType // since we are only increasing the strictness of params. -export const isAlertExecutor = (obj: SignalRuleAlertTypeDefinition): obj is AlertType => { +export const isAlertExecutor = ( + obj: SignalRuleAlertTypeDefinition +): obj is AlertType => { return true; }; -export type SignalRuleAlertTypeDefinition = Omit & { +export type SignalRuleAlertTypeDefinition = Omit< + AlertType, + 'executor' +> & { executor: ({ services, params, state }: RuleExecutorOptions) => Promise; }; 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 164ce993eebac..a2b8d7eac7c5e 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 @@ -9,7 +9,12 @@ import { schema } from '@kbn/config-schema'; import { Logger } from 'src/core/server'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { getGeoContainmentExecutor } from './geo_containment'; -import { AlertType } from '../../../../alerts/server'; +import { + AlertType, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../alerts/server'; import { Query } from '../../../../../../src/plugins/data/common/query'; export const GEO_CONTAINMENT_ID = '.geo-containment'; @@ -112,7 +117,9 @@ export interface GeoContainmentParams { boundaryIndexQuery?: Query; } -export function getAlertType(logger: Logger): AlertType { +export function getAlertType( + logger: Logger +): AlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoContainment.alertTypeTitle', { defaultMessage: 'Geo tracking containment', }); 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 93a6c0d29cf3c..1a6461c2e8398 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 @@ -9,7 +9,12 @@ import { schema } from '@kbn/config-schema'; import { Logger } from 'src/core/server'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { getGeoThresholdExecutor } from './geo_threshold'; -import { AlertType } from '../../../../alerts/server'; +import { + AlertType, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../alerts/server'; import { Query } from '../../../../../../src/plugins/data/common/query'; export const GEO_THRESHOLD_ID = '.geo-threshold'; @@ -172,7 +177,9 @@ export interface GeoThresholdParams { boundaryIndexQuery?: Query; } -export function getAlertType(logger: Logger): AlertType { +export function getAlertType( + logger: Logger +): AlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoThreshold.alertTypeTitle', { defaultMessage: 'Geo tracking threshold', }); 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 4f9fefa4188e5..d5fd92476dd16 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 @@ -11,7 +11,13 @@ import { getStatusMessage, getUniqueIdsByLoc, } from '../status_check'; -import { AlertType } from '../../../../../alerts/server'; +import { + AlertType, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../../alerts/server'; import { IRouter } from 'kibana/server'; import { UMServerLibs } from '../../lib'; import { UptimeCorePlugins, UptimeCoreSetup } from '../../adapters'; @@ -868,7 +874,7 @@ describe('status check alert', () => { }); describe('alert factory', () => { - let alert: AlertType; + let alert: AlertType; beforeEach(() => { const { server, libs, plugins } = bootstrapDependencies(); diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts index 0a80b36046860..65f9abfd2e60d 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/types.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts @@ -6,10 +6,16 @@ import { UptimeCorePlugins, UptimeCoreSetup } from '../adapters'; import { UMServerLibs } from '../lib'; -import { AlertType } from '../../../../alerts/server'; +import { + AlertType, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../alerts/server'; export type UptimeAlertTypeFactory = ( server: UptimeCoreSetup, libs: UMServerLibs, plugins: UptimeCorePlugins -) => AlertType; +) => AlertType; 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 965287ffbde8e..b1f1edd752c1b 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 @@ -5,12 +5,23 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import { AlertExecutorOptions, AlertType, AlertTypeState } from '../../../../alerts/server'; +import { + AlertExecutorOptions, + AlertType, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../alerts/server'; import { savedObjectsAdapter } from '../saved_objects'; import { DynamicSettings } from '../../../common/runtime_types'; import { createUptimeESClient, UptimeESClient } from '../lib'; -export interface UptimeAlertType extends Omit { +export interface UptimeAlertType + extends Omit< + AlertType, + 'executor' | 'producer' + > { executor: ({ options, uptimeEsClient, From bfcab2aa0f6c017da89a6cd4f97530fa61c1848c Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 21 Dec 2020 10:35:08 +0000 Subject: [PATCH 02/13] removed `any` usage from AlertTypeState and AlertTypeParams --- x-pack/plugins/alerts/common/alert.ts | 12 +- .../create_alert_instance_factory.ts | 10 +- .../alerts/server/alert_type_registry.ts | 8 +- .../server/alerts_client/alerts_client.ts | 72 ++++++---- .../server/alerts_client/tests/create.test.ts | 12 +- ...rt_instance_summary_from_event_log.test.ts | 4 +- .../alert_instance_summary_from_event_log.ts | 2 +- .../lib/validate_alert_type_params.test.ts | 84 ++---------- .../server/lib/validate_alert_type_params.ts | 14 +- x-pack/plugins/alerts/server/mocks.ts | 31 ++++- .../alerts/server/routes/create.test.ts | 2 +- x-pack/plugins/alerts/server/routes/create.ts | 12 +- .../plugins/alerts/server/routes/get.test.ts | 4 +- .../task_runner/alert_task_instance.test.ts | 4 +- .../server/task_runner/alert_task_instance.ts | 5 +- .../create_execution_handler.test.ts | 15 ++- .../task_runner/create_execution_handler.ts | 23 +++- .../server/task_runner/task_runner.test.ts | 118 ++++++++++++++-- .../alerts/server/task_runner/task_runner.ts | 127 ++++++++++++------ x-pack/plugins/alerts/server/types.ts | 25 ++-- .../infra/public/alerting/inventory/index.ts | 14 +- .../public/alerting/metric_threshold/index.ts | 14 +- .../inventory_metric_threshold_executor.ts | 13 +- ...r_inventory_metric_threshold_alert_type.ts | 15 +-- .../log_threshold/log_threshold_executor.ts | 39 ++++-- .../metric_threshold/lib/evaluate_alert.ts | 18 +-- .../metric_threshold_executor.test.ts | 32 ++++- .../metric_threshold_executor.ts | 19 ++- .../register_metric_threshold_alert_type.ts | 20 ++- .../plugins/monitoring/common/types/alerts.ts | 6 +- .../alerts/components/duration/validation.tsx | 2 + .../cpu_usage_alert/cpu_usage_alert.tsx | 4 +- .../public/alerts/disk_usage_alert/index.tsx | 4 +- .../alerts/memory_usage_alert/index.tsx | 4 +- .../monitoring/server/alerts/base_alert.ts | 6 +- .../notifications/create_notifications.ts | 6 +- .../notifications/find_notifications.ts | 4 +- .../notifications/read_notifications.ts | 4 +- .../detection_engine/notifications/types.ts | 22 +-- .../notifications/update_notifications.ts | 6 +- .../routes/rules/create_rules_bulk_route.ts | 9 +- .../routes/rules/create_rules_route.ts | 9 +- .../detection_engine/routes/rules/utils.ts | 9 +- .../routes/rules/validate.test.ts | 5 +- .../detection_engine/routes/rules/validate.ts | 9 +- .../detection_engine/rules/create_rules.ts | 10 +- .../lib/detection_engine/rules/find_rules.ts | 3 +- .../rules/install_prepacked_rules.ts | 6 +- .../rules/patch_rules.mock.ts | 30 ++++- .../lib/detection_engine/rules/patch_rules.ts | 10 +- .../lib/detection_engine/rules/read_rules.ts | 3 +- .../lib/detection_engine/rules/types.ts | 14 +- .../detection_engine/rules/update_rules.ts | 10 +- .../signals/signal_rule_alert_type.test.ts | 5 +- .../signals/signal_rule_alert_type.ts | 8 +- .../lib/detection_engine/signals/types.ts | 24 ++-- .../server/lib/detection_engine/types.ts | 3 +- .../alert_types/geo_containment/types.ts | 3 +- .../public/alert_types/geo_threshold/types.ts | 3 +- .../public/alert_types/threshold/types.ts | 4 +- .../alert_types/geo_containment/alert_type.ts | 33 ++++- .../geo_containment/geo_containment.ts | 43 +++--- .../alert_types/geo_containment/index.ts | 15 ++- .../tests/geo_containment.test.ts | 17 ++- .../alert_types/geo_threshold/alert_type.ts | 40 +++++- .../geo_threshold/geo_threshold.ts | 34 ++--- .../public/application/lib/alert_api.test.ts | 11 +- .../triggers_actions_ui/public/plugin.ts | 4 +- .../triggers_actions_ui/public/types.ts | 21 +-- .../uptime/public/state/actions/types.ts | 5 +- .../uptime/public/state/alerts/alerts.ts | 27 ++-- .../plugins/uptime/public/state/api/alerts.ts | 11 +- .../plugins/uptime/server/lib/alerts/types.ts | 17 +-- .../server/lib/alerts/uptime_alert_wrapper.ts | 27 ++-- 74 files changed, 852 insertions(+), 466 deletions(-) diff --git a/x-pack/plugins/alerts/common/alert.ts b/x-pack/plugins/alerts/common/alert.ts index d74f66898eff6..ed3fbcf2ddc9b 100644 --- a/x-pack/plugins/alerts/common/alert.ts +++ b/x-pack/plugins/alerts/common/alert.ts @@ -7,10 +7,8 @@ import { SavedObjectAttribute, SavedObjectAttributes } from 'kibana/server'; import { AlertNotifyWhenType } from './alert_notify_when_type'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type AlertTypeState = Record; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type AlertTypeParams = Record; +export type AlertTypeState = Record; +export type AlertTypeParams = Record; export interface IntervalSchedule extends SavedObjectAttributes { interval: string; @@ -52,7 +50,7 @@ export interface AlertAggregations { alertExecutionStatus: { [status: string]: number }; } -export interface Alert { +export interface Alert { id: string; enabled: boolean; name: string; @@ -61,7 +59,7 @@ export interface Alert { consumer: string; schedule: IntervalSchedule; actions: AlertAction[]; - params: AlertTypeParams; + params: Params; scheduledTaskId?: string; createdBy: string | null; updatedBy: string | null; @@ -76,7 +74,7 @@ export interface Alert { executionStatus: AlertExecutionStatus; } -export type SanitizedAlert = Omit; +export type SanitizedAlert = Omit, 'apiKey'>; export enum HealthStatus { OK = 'ok', 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 0b29262ddcc07..47f013a5d0e55 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 @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertInstanceContext, AlertInstanceState } from '../types'; import { AlertInstance } from './alert_instance'; -export function createAlertInstanceFactory(alertInstances: Record) { - return (id: string): AlertInstance => { +export function createAlertInstanceFactory< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(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.ts b/x-pack/plugins/alerts/server/alert_type_registry.ts index fd691da5732d4..5e4188c1f3bc1 100644 --- a/x-pack/plugins/alerts/server/alert_type_registry.ts +++ b/x-pack/plugins/alerts/server/alert_type_registry.ts @@ -103,10 +103,10 @@ export class AlertTypeRegistry { } public register< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext >(alertType: AlertType) { if (this.has(alertType.id)) { throw new Error( diff --git a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index b4d52a6f1ccfb..e21fee4ce3d61 100644 --- a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -29,6 +29,7 @@ import { AlertInstanceSummary, AlertExecutionStatusValues, AlertNotifyWhenType, + AlertTypeParams, } from '../types'; import { validateAlertTypeParams, @@ -126,16 +127,16 @@ interface AggregateResult { alertExecutionStatus: { [status: string]: number }; } -export interface FindResult { +export interface FindResult { page: number; perPage: number; total: number; - data: SanitizedAlert[]; + data: Array>; } -export interface CreateOptions { +export interface CreateOptions { data: Omit< - Alert, + Alert, | 'id' | 'createdBy' | 'updatedBy' @@ -153,14 +154,14 @@ export interface CreateOptions { }; } -interface UpdateOptions { +interface UpdateOptions { id: string; data: { name: string; tags: string[]; schedule: IntervalSchedule; actions: NormalizedAlertAction[]; - params: Record; + params: Params; throttle: string | null; notifyWhen: AlertNotifyWhenType | null; }; @@ -222,7 +223,10 @@ export class AlertsClient { this.auditLogger = auditLogger; } - public async create({ data, options }: CreateOptions): Promise { + public async create({ + data, + options, + }: CreateOptions): Promise> { const id = SavedObjectsUtils.generateId(); try { @@ -247,7 +251,10 @@ export class AlertsClient { // Throws an error if alert type isn't registered const alertType = this.alertTypeRegistry.get(data.alertTypeId); - const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params); + const validatedAlertTypeParams = validateAlertTypeParams( + data.params, + alertType.validate?.params + ); const username = await this.getUserName(); const createdAPIKey = data.enabled @@ -333,10 +340,14 @@ export class AlertsClient { }); createdAlert.attributes.scheduledTaskId = scheduledTask.id; } - return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references); + return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references); } - public async get({ id }: { id: string }): Promise { + public async get({ + id, + }: { + id: string; + }): Promise> { const result = await this.unsecuredSavedObjectsClient.get('alert', id); try { await this.authorization.ensureAuthorized( @@ -360,7 +371,7 @@ export class AlertsClient { savedObject: { type: 'alert', id }, }) ); - return this.getAlertFromRaw(result.id, result.attributes, result.references); + return this.getAlertFromRaw(result.id, result.attributes, result.references); } public async getAlertState({ id }: { id: string }): Promise { @@ -425,9 +436,9 @@ export class AlertsClient { }); } - public async find({ + public async find({ options: { fields, ...options } = {}, - }: { options?: FindOptions } = {}): Promise { + }: { options?: FindOptions } = {}): Promise> { let authorizationTuple; try { authorizationTuple = await this.authorization.getFindAuthorizationFilter(); @@ -474,7 +485,7 @@ export class AlertsClient { ); throw error; } - return this.getAlertFromRaw( + return this.getAlertFromRaw( id, fields ? (pick(attributes, fields) as RawAlert) : attributes, references @@ -604,15 +615,21 @@ export class AlertsClient { return removeResult; } - public async update({ id, data }: UpdateOptions): Promise { + public async update({ + id, + data, + }: UpdateOptions): Promise> { return await retryIfConflicts( this.logger, `alertsClient.update('${id}')`, - async () => await this.updateWithOCC({ id, data }) + async () => await this.updateWithOCC({ id, data }) ); } - private async updateWithOCC({ id, data }: UpdateOptions): Promise { + private async updateWithOCC({ + id, + data, + }: UpdateOptions): Promise> { let alertSavedObject: SavedObject; try { @@ -657,7 +674,7 @@ export class AlertsClient { this.alertTypeRegistry.ensureAlertTypeEnabled(alertSavedObject.attributes.alertTypeId); - const updateResult = await this.updateAlert({ id, data }, alertSavedObject); + const updateResult = await this.updateAlert({ id, data }, alertSavedObject); await Promise.all([ alertSavedObject.attributes.apiKey @@ -691,14 +708,17 @@ export class AlertsClient { return updateResult; } - private async updateAlert( - { id, data }: UpdateOptions, + private async updateAlert( + { id, data }: UpdateOptions, { attributes, version }: SavedObject - ): Promise { + ): Promise> { const alertType = this.alertTypeRegistry.get(attributes.alertTypeId); // Validate - const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params); + const validatedAlertTypeParams = validateAlertTypeParams( + data.params, + alertType.validate?.params + ); this.validateActions(alertType, data.actions); const { actions, references } = await this.denormalizeActions(data.actions); @@ -1342,7 +1362,7 @@ export class AlertsClient { }) as Alert['actions']; } - private getAlertFromRaw( + private getAlertFromRaw( id: string, rawAlert: RawAlert, references: SavedObjectReference[] | undefined @@ -1350,14 +1370,14 @@ export class AlertsClient { // In order to support the partial update API of Saved Objects we have to support // partial updates of an Alert, but when we receive an actual RawAlert, it is safe // to cast the result to an Alert - return this.getPartialAlertFromRaw(id, rawAlert, references) as Alert; + return this.getPartialAlertFromRaw(id, rawAlert, references) as Alert; } - private getPartialAlertFromRaw( + private getPartialAlertFromRaw( id: string, { createdAt, updatedAt, meta, notifyWhen, scheduledTaskId, ...rawAlert }: Partial, references: SavedObjectReference[] | undefined - ): PartialAlert { + ): PartialAlert { // Not the prettiest code here, but if we want to use most of the // alert fields from the rawAlert using `...rawAlert` kind of access, we // need to specifically delete the executionStatus as it's a different type diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts index 5f830a6c5bc51..0424a1295c9b9 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts @@ -59,7 +59,11 @@ beforeEach(() => { setGlobalDate(); -function getMockData(overwrites: Record = {}): CreateOptions['data'] { +function getMockData( + overwrites: Record = {} +): CreateOptions<{ + bar: boolean; +}>['data'] { return { enabled: true, name: 'abc', @@ -93,7 +97,11 @@ describe('create()', () => { }); describe('authorization', () => { - function tryToExecuteOperation(options: CreateOptions): Promise { + function tryToExecuteOperation( + options: CreateOptions<{ + bar: boolean; + }> + ): Promise { unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ { diff --git a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts index d6357494546b0..0f91e5d0c24a9 100644 --- a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts @@ -635,11 +635,11 @@ export class EventsFactory { } } -function createAlert(overrides: Partial): SanitizedAlert { +function createAlert(overrides: Partial): SanitizedAlert<{ bar: boolean }> { return { ...BaseAlert, ...overrides }; } -const BaseAlert: SanitizedAlert = { +const BaseAlert: SanitizedAlert<{ bar: boolean }> = { id: 'alert-123', alertTypeId: '123', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts index f540f9a9b884c..a020eecd448a4 100644 --- a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts +++ b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts @@ -9,7 +9,7 @@ import { IEvent } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER, LEGACY_EVENT_LOG_ACTIONS } from '../plugin'; export interface AlertInstanceSummaryFromEventLogParams { - alert: SanitizedAlert; + alert: SanitizedAlert<{ bar: boolean }>; events: IEvent[]; dateStart: string; dateEnd: string; diff --git a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts index c2034abb09842..634b6885aa59b 100644 --- a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts +++ b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts @@ -8,59 +8,19 @@ import { schema } from '@kbn/config-schema'; import { validateAlertTypeParams } from './validate_alert_type_params'; test('should return passed in params when validation not defined', () => { - const result = validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - recoveryActionGroup: { - id: 'recovery', - name: 'Recovery', - }, - minimumLicenseRequired: 'basic', - async executor() {}, - producer: 'alerts', - }, - { - foo: true, - } - ); + const result = validateAlertTypeParams({ + foo: true, + }); expect(result).toEqual({ foo: true }); }); test('should validate and apply defaults when params is valid', () => { const result = validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - recoveryActionGroup: { - id: 'recovery', - name: 'Recovery', - }, - minimumLicenseRequired: 'basic', - validate: { - params: schema.object({ - param1: schema.string(), - param2: schema.string({ defaultValue: 'default-value' }), - }), - }, - async executor() {}, - producer: 'alerts', - }, - { param1: 'value' } + { param1: 'value' }, + schema.object({ + param1: schema.string(), + param2: schema.string({ defaultValue: 'default-value' }), + }) ); expect(result).toEqual({ param1: 'value', @@ -71,30 +31,10 @@ test('should validate and apply defaults when params is valid', () => { test('should validate and throw error when params is invalid', () => { expect(() => validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - recoveryActionGroup: { - id: 'recove', - name: 'Recove', - }, - minimumLicenseRequired: 'basic', - validate: { - params: schema.object({ - param1: schema.string(), - }), - }, - async executor() {}, - producer: 'alerts', - }, - {} + {}, + schema.object({ + param1: schema.string(), + }) ) ).toThrowErrorMatchingInlineSnapshot( `"params invalid: [param1]: expected value of type [string] but got [undefined]"` diff --git a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts index ce91769d35fe5..2f510f90a2367 100644 --- a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts +++ b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts @@ -5,16 +5,14 @@ */ import Boom from '@hapi/boom'; -import { UntypedNormalizedAlertType } from '../alert_type_registry'; -import { AlertExecutorOptions } from '../types'; +import { AlertTypeParams, AlertTypeParamsValidator } from '../types'; -export function validateAlertTypeParams( - alertType: UntypedNormalizedAlertType, - params: Record -): AlertExecutorOptions['params'] { - const validator = alertType.validate && alertType.validate.params; +export function validateAlertTypeParams( + params: Record, + validator?: AlertTypeParamsValidator +): Params { if (!validator) { - return params as AlertExecutorOptions['params']; + return params as Params; } try { diff --git a/x-pack/plugins/alerts/server/mocks.ts b/x-pack/plugins/alerts/server/mocks.ts index cfae4c650bd42..679ae17c6fdd7 100644 --- a/x-pack/plugins/alerts/server/mocks.ts +++ b/x-pack/plugins/alerts/server/mocks.ts @@ -11,6 +11,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock, } from '../../../../src/core/server/mocks'; +import { AlertInstanceContext, AlertInstanceState } from './types'; export { alertsClientMock }; @@ -30,8 +31,14 @@ const createStartMock = () => { return mock; }; -export type AlertInstanceMock = jest.Mocked; -const createAlertInstanceFactoryMock = () => { +export type AlertInstanceMock< + State extends AlertInstanceState = AlertInstanceState, + Context extends AlertInstanceContext = AlertInstanceContext +> = jest.Mocked>; +const createAlertInstanceFactoryMock = < + InstanceState extends AlertInstanceState = AlertInstanceState, + InstanceContext extends AlertInstanceContext = AlertInstanceContext +>() => { const mock = { hasScheduledActions: jest.fn(), isThrottled: jest.fn(), @@ -50,14 +57,17 @@ const createAlertInstanceFactoryMock = () => { mock.unscheduleActions.mockReturnValue(mock); mock.scheduleActions.mockReturnValue(mock); - return (mock as unknown) as AlertInstanceMock; + return (mock as unknown) as AlertInstanceMock; }; -const createAlertServicesMock = () => { - const alertInstanceFactoryMock = createAlertInstanceFactoryMock(); +const createAlertServicesMock = < + InstanceState extends AlertInstanceState = AlertInstanceState, + InstanceContext extends AlertInstanceContext = AlertInstanceContext +>() => { + const alertInstanceFactoryMock = createAlertInstanceFactoryMock(); return { alertInstanceFactory: jest - .fn, [string]>() + .fn>, [string]>() .mockReturnValue(alertInstanceFactoryMock), callCluster: elasticsearchServiceMock.createLegacyScopedClusterClient().callAsCurrentUser, getLegacyScopedClusterClient: jest.fn(), @@ -66,6 +76,15 @@ const createAlertServicesMock = () => { }; }; export type AlertServicesMock = ReturnType; +// export type AlertServicesMock< +// InstanceState extends AlertInstanceState = AlertInstanceState, +// InstanceContext extends AlertInstanceContext = AlertInstanceContext +// > = { +// alertInstanceFactory: jest.Mock< +// jest.Mocked>, +// [string] +// >; +// } & Omit, 'alertInstanceFactory'>; export const alertsMock = { createAlertInstanceFactory: createAlertInstanceFactoryMock, diff --git a/x-pack/plugins/alerts/server/routes/create.test.ts b/x-pack/plugins/alerts/server/routes/create.test.ts index 5597b315158cd..fc531821f25b6 100644 --- a/x-pack/plugins/alerts/server/routes/create.test.ts +++ b/x-pack/plugins/alerts/server/routes/create.test.ts @@ -49,7 +49,7 @@ describe('createAlertRoute', () => { ], }; - const createResult: Alert = { + const createResult: Alert<{ bar: boolean }> = { ...mockedAlert, enabled: true, muteAll: false, diff --git a/x-pack/plugins/alerts/server/routes/create.ts b/x-pack/plugins/alerts/server/routes/create.ts index a34a3118985fa..a79a9d40b236f 100644 --- a/x-pack/plugins/alerts/server/routes/create.ts +++ b/x-pack/plugins/alerts/server/routes/create.ts @@ -16,7 +16,13 @@ import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { validateDurationSchema } from '../lib'; import { handleDisabledApiKeysError } from './lib/error_handler'; -import { Alert, AlertNotifyWhenType, BASE_ALERT_API_PATH, validateNotifyWhenType } from '../types'; +import { + Alert, + AlertNotifyWhenType, + AlertTypeParams, + BASE_ALERT_API_PATH, + validateNotifyWhenType, +} from '../types'; import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; export const bodySchema = schema.object({ @@ -65,7 +71,9 @@ export const createAlertRoute = (router: IRouter, licenseState: ILicenseState) = const alert = req.body; const notifyWhen = alert?.notifyWhen ? (alert.notifyWhen as AlertNotifyWhenType) : null; try { - const alertRes: Alert = await alertsClient.create({ data: { ...alert, notifyWhen } }); + const alertRes: Alert = await alertsClient.create({ + data: { ...alert, notifyWhen }, + }); return res.ok({ body: alertRes, }); diff --git a/x-pack/plugins/alerts/server/routes/get.test.ts b/x-pack/plugins/alerts/server/routes/get.test.ts index 21e52ece82d2d..747f9b11e2b47 100644 --- a/x-pack/plugins/alerts/server/routes/get.test.ts +++ b/x-pack/plugins/alerts/server/routes/get.test.ts @@ -22,7 +22,9 @@ beforeEach(() => { }); describe('getAlertRoute', () => { - const mockedAlert: Alert = { + const mockedAlert: Alert<{ + bar: true; + }> = { id: '1', alertTypeId: '1', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts index 09236ec5e0ad1..1bd8b75e2133d 100644 --- a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts @@ -9,7 +9,9 @@ import { AlertTaskInstance, taskInstanceToAlertTaskInstance } from './alert_task import uuid from 'uuid'; import { SanitizedAlert } from '../types'; -const alert: SanitizedAlert = { +const alert: SanitizedAlert<{ + bar: boolean; +}> = { id: 'alert-123', alertTypeId: '123', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts index a290f3fa33c70..ab074cfdffa1c 100644 --- a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts @@ -13,6 +13,7 @@ import { alertParamsSchema, alertStateSchema, AlertTaskParams, + AlertTypeParams, } from '../../common'; export interface AlertTaskInstance extends ConcreteTaskInstance { @@ -23,9 +24,9 @@ export interface AlertTaskInstance extends ConcreteTaskInstance { const enumerateErrorFields = (e: t.Errors) => `${e.map(({ context }) => context.map(({ key }) => key).join('.'))}`; -export function taskInstanceToAlertTaskInstance( +export function taskInstanceToAlertTaskInstance( taskInstance: ConcreteTaskInstance, - alert?: SanitizedAlert + alert?: SanitizedAlert ): AlertTaskInstance { return { ...taskInstance, 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 276ef1d87d07d..5603b13a3b1f5 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 @@ -16,6 +16,12 @@ import { KibanaRequest } from 'kibana/server'; import { asSavedObjectExecutionSource } from '../../../actions/server'; import { InjectActionParamsOpts } from './inject_action_params'; import { UntypedNormalizedAlertType } from '../alert_type_registry'; +import { + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; jest.mock('./inject_action_params', () => ({ injectActionParams: jest.fn(), @@ -42,7 +48,14 @@ const actionsClient = actionsClientMock.create(); const mockActionsPlugin = actionsMock.createStart(); const mockEventLogger = eventLoggerMock.create(); -const createExecutionHandlerParams: jest.Mocked = { +const createExecutionHandlerParams: jest.Mocked< + CreateExecutionHandlerOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + > +> = { actionsPlugin: mockActionsPlugin, spaceId: 'default', alertId: '1', 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 f9bb269e9b22e..8b4412aeb23e5 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 @@ -15,14 +15,20 @@ import { EVENT_LOG_ACTIONS } from '../plugin'; import { injectActionParams } from './inject_action_params'; import { AlertAction, + AlertTypeParams, + AlertTypeState, AlertInstanceState, AlertInstanceContext, - AlertTypeParams, RawAlert, } from '../types'; -import { UntypedNormalizedAlertType } from '../alert_type_registry'; +import { NormalizedAlertType } from '../alert_type_registry'; -export interface CreateExecutionHandlerOptions { +export interface CreateExecutionHandlerOptions< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { alertId: string; alertName: string; tags?: string[]; @@ -30,7 +36,7 @@ export interface CreateExecutionHandlerOptions { actions: AlertAction[]; spaceId: string; apiKey: RawAlert['apiKey']; - alertType: UntypedNormalizedAlertType; + alertType: NormalizedAlertType; logger: Logger; eventLogger: IEventLogger; request: KibanaRequest; @@ -45,7 +51,12 @@ interface ExecutionHandlerOptions { state: AlertInstanceState; } -export function createExecutionHandler({ +export function createExecutionHandler< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>({ logger, alertId, alertName, @@ -58,7 +69,7 @@ export function createExecutionHandler({ eventLogger, request, alertParams, -}: CreateExecutionHandlerOptions) { +}: CreateExecutionHandlerOptions) { const alertTypeActionGroups = new Map( alertType.actionGroups.map((actionGroup) => [actionGroup.id, actionGroup.name]) ); 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 2ddc65fba2eab..33e032bf51899 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 @@ -6,7 +6,13 @@ import sinon from 'sinon'; import { schema } from '@kbn/config-schema'; -import { AlertExecutorOptions } from '../types'; +import { + AlertExecutorOptions, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; import { ConcreteTaskInstance, isUnrecoverableError, @@ -91,7 +97,7 @@ describe('Task Runner', () => { alertTypeRegistry, }; - const mockedAlertTypeSavedObject: Alert = { + const mockedAlertTypeSavedObject: Alert = { id: '1', consumer: 'bar', createdAt: new Date('2019-02-12T21:01:22.479Z'), @@ -239,7 +245,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices .alertInstanceFactory('1') .scheduleActionsWithSubGroup('default', 'subDefault'); @@ -392,7 +405,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); @@ -501,7 +521,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertInstanceFactory('2').scheduleActions('default'); } @@ -547,7 +574,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); @@ -641,7 +675,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); @@ -681,7 +722,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices .alertInstanceFactory('1') .scheduleActionsWithSubGroup('default', 'subgroup1'); @@ -729,7 +777,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); @@ -897,7 +952,14 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); @@ -997,7 +1059,14 @@ describe('Task Runner', () => { }; alertTypeWithCustomRecovery.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); @@ -1088,7 +1157,14 @@ describe('Task Runner', () => { test('persists alertInstances passed in from state, only if they are scheduled for execution', async () => { alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); @@ -1350,7 +1426,14 @@ describe('Task Runner', () => { test('recovers gracefully when the AlertType executor throws an exception', async () => { alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { throw new Error('OMG'); } ); @@ -1718,7 +1801,14 @@ describe('Task Runner', () => { }; alertType.executor.mockImplementation( - async ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { 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 5c530c414bbe9..3ecf1a8ebc767 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -26,7 +26,6 @@ import { RawAlertInstance, AlertTaskState, Alert, - AlertExecutorOptions, SanitizedAlert, AlertExecutionStatus, AlertExecutionStatusErrorReasons, @@ -39,8 +38,14 @@ import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_l import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error'; import { AlertsClient } from '../alerts_client'; import { partiallyUpdateAlert } from '../saved_objects'; -import { ActionGroup } from '../../common'; -import { UntypedNormalizedAlertType } from '../alert_type_registry'; +import { + ActionGroup, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../common'; +import { NormalizedAlertType } from '../alert_type_registry'; const FALLBACK_RETRY_INTERVAL = '5m'; @@ -55,15 +60,20 @@ interface AlertTaskInstance extends ConcreteTaskInstance { state: AlertTaskState; } -export class TaskRunner { +export class TaskRunner< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { private context: TaskRunnerContext; private logger: Logger; private taskInstance: AlertTaskInstance; - private alertType: UntypedNormalizedAlertType; + private alertType: NormalizedAlertType; private readonly alertTypeRegistry: AlertTypeRegistry; constructor( - alertType: UntypedNormalizedAlertType, + alertType: NormalizedAlertType, taskInstance: ConcreteTaskInstance, context: TaskRunnerContext ) { @@ -131,8 +141,8 @@ export class TaskRunner { tags: string[] | undefined, spaceId: string, apiKey: RawAlert['apiKey'], - actions: Alert['actions'], - alertParams: RawAlert['params'] + actions: Alert['actions'], + alertParams: Params ) { return createExecutionHandler({ alertId, @@ -152,7 +162,7 @@ export class TaskRunner { async executeAlertInstance( alertInstanceId: string, - alertInstance: AlertInstance, + alertInstance: AlertInstance, executionHandler: ReturnType ) { const { @@ -168,8 +178,8 @@ export class TaskRunner { async executeAlertInstances( services: Services, - alert: SanitizedAlert, - params: AlertExecutorOptions['params'], + alert: SanitizedAlert, + params: Params, executionHandler: ReturnType, spaceId: string, event: Event @@ -190,9 +200,12 @@ export class TaskRunner { } = this.taskInstance; const namespace = this.context.spaceIdToNamespace(spaceId); - const alertInstances = mapValues, AlertInstance>( + const alertInstances = mapValues< + Record, + AlertInstance + >( alertRawInstances, - (rawAlertInstance) => new AlertInstance(rawAlertInstance) + (rawAlertInstance) => new AlertInstance(rawAlertInstance) ); const originalAlertInstances = cloneDeep(alertInstances); @@ -205,10 +218,12 @@ export class TaskRunner { alertId, services: { ...services, - alertInstanceFactory: createAlertInstanceFactory(alertInstances), + alertInstanceFactory: createAlertInstanceFactory( + alertInstances + ), }, params, - state: alertTypeState, + state: alertTypeState as State, startedAt: this.taskInstance.startedAt!, previousStartedAt: previousStartedAt ? new Date(previousStartedAt) : null, spaceId, @@ -232,12 +247,15 @@ export class TaskRunner { event.event.outcome = 'success'; // Cleanup alert instances that are no longer scheduling actions to avoid over populating the alertInstances object - const instancesWithScheduledActions = pickBy(alertInstances, (alertInstance: AlertInstance) => - alertInstance.hasScheduledActions() + const instancesWithScheduledActions = pickBy( + alertInstances, + (alertInstance: AlertInstance) => + alertInstance.hasScheduledActions() ); const recoveredAlertInstances = pickBy( alertInstances, - (alertInstance: AlertInstance) => !alertInstance.hasScheduledActions() + (alertInstance: AlertInstance) => + !alertInstance.hasScheduledActions() ); logActiveAndRecoveredInstances({ @@ -272,7 +290,10 @@ export class TaskRunner { const instancesToExecute = notifyWhen === 'onActionGroupChange' ? Object.entries(instancesWithScheduledActions).filter( - ([alertInstanceName, alertInstance]: [string, AlertInstance]) => { + ([alertInstanceName, alertInstance]: [ + string, + AlertInstance + ]) => { const shouldExecuteAction = alertInstance.scheduledActionGroupOrSubgroupHasChanged(); if (!shouldExecuteAction) { this.logger.debug( @@ -283,7 +304,10 @@ export class TaskRunner { } ) : Object.entries(instancesWithScheduledActions).filter( - ([alertInstanceName, alertInstance]: [string, AlertInstance]) => { + ([alertInstanceName, alertInstance]: [ + string, + AlertInstance + ]) => { const throttled = alertInstance.isThrottled(throttle); const muted = mutedInstanceIdsSet.has(alertInstanceName); const shouldExecuteAction = !throttled && !muted; @@ -299,8 +323,9 @@ export class TaskRunner { ); await Promise.all( - instancesToExecute.map(([id, alertInstance]: [string, AlertInstance]) => - this.executeAlertInstance(id, alertInstance, executionHandler) + instancesToExecute.map( + ([id, alertInstance]: [string, AlertInstance]) => + this.executeAlertInstance(id, alertInstance, executionHandler) ) ); } else { @@ -309,17 +334,17 @@ export class TaskRunner { return { alertTypeState: updatedAlertTypeState || undefined, - alertInstances: mapValues, RawAlertInstance>( - instancesWithScheduledActions, - (alertInstance) => alertInstance.toRaw() - ), + alertInstances: mapValues< + Record>, + RawAlertInstance + >(instancesWithScheduledActions, (alertInstance) => alertInstance.toRaw()), }; } async validateAndExecuteAlert( services: Services, apiKey: RawAlert['apiKey'], - alert: SanitizedAlert, + alert: SanitizedAlert, event: Event ) { const { @@ -327,7 +352,7 @@ export class TaskRunner { } = this.taskInstance; // Validate - const validatedParams = validateAlertTypeParams(this.alertType, alert.params); + const validatedParams = validateAlertTypeParams(alert.params, this.alertType.validate?.params); const executionHandler = this.getExecutionHandler( alertId, alert.name, @@ -359,7 +384,7 @@ export class TaskRunner { } const [services, alertsClient] = this.getServicesWithSpaceLevelPermissions(spaceId, apiKey); - let alert: SanitizedAlert; + let alert: SanitizedAlert; // Ensure API key is still valid and user has access try { @@ -500,19 +525,23 @@ export class TaskRunner { } } -interface GenerateNewAndRecoveredInstanceEventsParams { +interface GenerateNewAndRecoveredInstanceEventsParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { eventLogger: IEventLogger; - originalAlertInstances: Dictionary; - currentAlertInstances: Dictionary; - recoveredAlertInstances: Dictionary; + originalAlertInstances: Dictionary>; + currentAlertInstances: Dictionary>; + recoveredAlertInstances: Dictionary>; alertId: string; alertLabel: string; namespace: string | undefined; } -function generateNewAndRecoveredInstanceEvents( - params: GenerateNewAndRecoveredInstanceEventsParams -) { +function generateNewAndRecoveredInstanceEvents< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: GenerateNewAndRecoveredInstanceEventsParams) { const { eventLogger, alertId, @@ -583,16 +612,22 @@ function generateNewAndRecoveredInstanceEvents( } } -interface ScheduleActionsForRecoveredInstancesParams { +interface ScheduleActionsForRecoveredInstancesParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { logger: Logger; recoveryActionGroup: ActionGroup; - recoveredAlertInstances: Dictionary; + recoveredAlertInstances: Dictionary>; executionHandler: ReturnType; mutedInstanceIdsSet: Set; alertLabel: string; } -function scheduleActionsForRecoveredInstances(params: ScheduleActionsForRecoveredInstancesParams) { +function scheduleActionsForRecoveredInstances< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: ScheduleActionsForRecoveredInstancesParams) { const { logger, recoveryActionGroup, @@ -622,14 +657,20 @@ function scheduleActionsForRecoveredInstances(params: ScheduleActionsForRecovere } } -interface LogActiveAndRecoveredInstancesParams { +interface LogActiveAndRecoveredInstancesParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { logger: Logger; - activeAlertInstances: Dictionary; - recoveredAlertInstances: Dictionary; + activeAlertInstances: Dictionary>; + recoveredAlertInstances: Dictionary>; alertLabel: string; } -function logActiveAndRecoveredInstances(params: LogActiveAndRecoveredInstancesParams) { +function logActiveAndRecoveredInstances< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: LogActiveAndRecoveredInstancesParams) { const { logger, activeAlertInstances, recoveredAlertInstances, alertLabel } = params; const activeInstanceIds = Object.keys(activeAlertInstances); const recoveredInstanceIds = Object.keys(recoveredAlertInstances); diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 7cad2b585462b..027f875e2d08d 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -61,10 +61,10 @@ export interface AlertServices< } export interface AlertExecutorOptions< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > { alertId: string; startedAt: Date; @@ -85,16 +85,18 @@ export interface ActionVariable { description: string; } -// signature of the alert type executor function export type ExecutorType< - Params, - State, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > = ( options: AlertExecutorOptions ) => Promise; +export interface AlertTypeParamsValidator { + validate: (object: unknown) => Params; +} export interface AlertType< Params extends AlertTypeParams = never, State extends AlertTypeState = never, @@ -104,7 +106,7 @@ export interface AlertType< id: string; name: string; validate?: { - params?: { validate: (object: unknown) => Params }; + params?: AlertTypeParamsValidator; }; actionGroups: ActionGroup[]; defaultActionGroupId: ActionGroup['id']; @@ -149,7 +151,8 @@ export interface RawAlertExecutionStatus extends SavedObjectAttributes { }; } -export type PartialAlert = Pick & Partial>; +export type PartialAlert = Pick, 'id'> & + Partial, 'id'>>; export interface RawAlert extends SavedObjectAttributes { enabled: boolean; diff --git a/x-pack/plugins/infra/public/alerting/inventory/index.ts b/x-pack/plugins/infra/public/alerting/inventory/index.ts index d7afd73c0e3a7..58a9eabcd3f3b 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/index.ts +++ b/x-pack/plugins/infra/public/alerting/inventory/index.ts @@ -5,13 +5,21 @@ */ import { i18n } from '@kbn/i18n'; import React from 'react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../server/lib/alerting/inventory_metric_threshold/types'; +import { + InventoryMetricConditions, + METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../server/lib/alerting/inventory_metric_threshold/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; +import { AlertTypeParams } from '../../../../alerts/common'; import { validateMetricThreshold } from './components/validation'; -export function createInventoryMetricAlertType(): AlertTypeModel { +interface InventoryMetricAlertTypeParams extends AlertTypeParams { + criteria: InventoryMetricConditions[]; +} + +export function createInventoryMetricAlertType(): AlertTypeModel { return { id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.metrics.inventory.alertFlyout.alertName', { diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts index 05c69e5ccb78b..0cdac9c58c77e 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts @@ -8,10 +8,18 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { validateMetricThreshold } from './components/validation'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../server/lib/alerting/metric_threshold/types'; +import { AlertTypeParams } from '../../../../alerts/common'; +import { + MetricExpressionParams, + METRIC_THRESHOLD_ALERT_TYPE_ID, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../server/lib/alerting/metric_threshold/types'; + +interface MetricThresholdAlertTypeParams extends AlertTypeParams { + criteria: MetricExpressionParams[]; +} -export function createMetricThresholdAlertType(): AlertTypeModel { +export function createMetricThresholdAlertType(): AlertTypeModel { return { id: METRIC_THRESHOLD_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.metrics.alertFlyout.alertName', { 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 1941ec6326ddb..b389666889dc7 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 @@ -9,7 +9,11 @@ import moment from 'moment'; import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label'; import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { AlertStates, InventoryMetricConditions } from './types'; -import { RecoveredActionGroup } from '../../../../../alerts/common'; +import { + AlertInstanceContext, + AlertInstanceState, + RecoveredActionGroup, +} from '../../../../../alerts/common'; import { AlertExecutorOptions } from '../../../../../alerts/server'; import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; import { InfraBackendLibs } from '../../infra_types'; @@ -35,7 +39,12 @@ interface InventoryMetricThresholdParams { export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) => async ({ services, params, -}: AlertExecutorOptions) => { +}: AlertExecutorOptions< + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>) => { const { criteria, filterQuery, 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 ef7a80311b925..24843e9526ea5 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 @@ -4,13 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { - AlertType, - AlertTypeParams, - AlertTypeState, - AlertInstanceState, - AlertInstanceContext, -} from '../../../../../alerts/server'; +import { AlertType, AlertInstanceState, AlertInstanceContext } from '../../../../../alerts/server'; import { createInventoryMetricThresholdExecutor, FIRED_ACTIONS, @@ -47,7 +41,12 @@ const condition = schema.object({ export const registerMetricInventoryThresholdAlertType = ( libs: InfraBackendLibs -): AlertType => ({ +): AlertType< + Record, + Record, + AlertInstanceState, + AlertInstanceContext +> => ({ id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, name: 'Inventory', validate: { 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 d3d34cd2aad58..dccab5168fb60 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 @@ -9,7 +9,10 @@ import { AlertExecutorOptions, AlertServices, AlertInstance, + AlertTypeParams, + AlertTypeState, AlertInstanceContext, + AlertInstanceState, } from '../../../../../alerts/server'; import { AlertStates, @@ -34,6 +37,14 @@ 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 LogThresholdAlertExecutorOptions = AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + const COMPOSITE_GROUP_SIZE = 40; const checkValueAgainstComparatorMap: { @@ -46,7 +57,7 @@ const checkValueAgainstComparatorMap: { }; export const createLogThresholdExecutor = (libs: InfraBackendLibs) => - async function ({ services, params }: AlertExecutorOptions) { + async function ({ services, params }: LogThresholdAlertExecutorOptions) { const { alertInstanceFactory, savedObjectsClient, callCluster } = services; const { sources } = libs; @@ -88,8 +99,8 @@ async function executeAlert( alertParams: CountAlertParams, timestampField: string, indexPattern: string, - callCluster: AlertServices['callCluster'], - alertInstanceFactory: AlertServices['alertInstanceFactory'] + callCluster: LogThresholdAlertServices['callCluster'], + alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] ) { const query = getESQuery(alertParams, timestampField, indexPattern); @@ -118,8 +129,8 @@ async function executeRatioAlert( alertParams: RatioAlertParams, timestampField: string, indexPattern: string, - callCluster: AlertServices['callCluster'], - alertInstanceFactory: AlertServices['alertInstanceFactory'] + callCluster: LogThresholdAlertServices['callCluster'], + alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] ) { // Ratio alert params are separated out into two standard sets of alert params const numeratorParams: AlertParams = { @@ -175,7 +186,7 @@ const getESQuery = ( export const processUngroupedResults = ( results: UngroupedSearchQueryResponse, params: CountAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -204,7 +215,7 @@ export const processUngroupedRatioResults = ( numeratorResults: UngroupedSearchQueryResponse, denominatorResults: UngroupedSearchQueryResponse, params: RatioAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -259,7 +270,7 @@ const getReducedGroupByResults = ( export const processGroupByResults = ( results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], params: CountAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -292,7 +303,7 @@ export const processGroupByRatioResults = ( numeratorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], denominatorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], params: RatioAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -599,11 +610,17 @@ const getQueryMappingForComparator = (comparator: Comparator) => { return queryMappings[comparator]; }; -const getUngroupedResults = async (query: object, callCluster: AlertServices['callCluster']) => { +const getUngroupedResults = async ( + query: object, + callCluster: LogThresholdAlertServices['callCluster'] +) => { return decodeOrThrow(UngroupedSearchQueryResponseRT)(await callCluster('search', query)); }; -const getGroupedResults = async (query: object, callCluster: AlertServices['callCluster']) => { +const getGroupedResults = async ( + query: object, + callCluster: LogThresholdAlertServices['callCluster'] +) => { let compositeGroupBuckets: GroupedSearchQueryResponse['aggregations']['groups']['buckets'] = []; let lastAfterKey: GroupedSearchQueryResponse['aggregations']['groups']['after_key'] | undefined; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts index 49f82c7ccec0b..d51d9435fc904 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts @@ -12,7 +12,7 @@ import { import { InfraSource } from '../../../../../common/http_api/source_api'; import { InfraDatabaseSearchResponse } from '../../../adapters/framework/adapter_types'; import { createAfterKeyHandler } from '../../../../utils/create_afterkey_handler'; -import { AlertServices, AlertExecutorOptions } from '../../../../../../alerts/server'; +import { AlertServices } from '../../../../../../alerts/server'; import { getAllCompositeData } from '../../../../utils/get_all_composite_data'; import { DOCUMENT_COUNT_I18N } from '../../common/messages'; import { UNGROUPED_FACTORY_KEY } from '../../common/utils'; @@ -35,17 +35,19 @@ interface CompositeAggregationsResponse { }; } -export const evaluateAlert = ( +export interface EvaluatedAlertParams { + criteria: MetricExpressionParams[]; + groupBy: string | undefined | string[]; + filterQuery: string | undefined; +} + +export const evaluateAlert = ( callCluster: AlertServices['callCluster'], - params: AlertExecutorOptions['params'], + params: Params, config: InfraSource['configuration'], timeframe?: { start: number; end: number } ) => { - const { criteria, groupBy, filterQuery } = params as { - criteria: MetricExpressionParams[]; - groupBy: string | undefined | string[]; - filterQuery: string | undefined; - }; + const { criteria, groupBy, filterQuery } = params; return Promise.all( criteria.map(async (criterion) => { const currentValues = await getMetric( diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index a1d6428f3b52b..cada17caf9656 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -7,13 +7,13 @@ import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold import { Comparator, AlertStates } from './types'; import * as mocks from './test_mocks'; import { RecoveredActionGroup } from '../../../../../alerts/common'; -import { AlertExecutorOptions } from '../../../../../alerts/server'; import { alertsMock, AlertServicesMock, AlertInstanceMock, } from '../../../../../alerts/server/mocks'; import { InfraSources } from '../../sources'; +import { MetricThresholdAlertExecutorOptions } from './register_metric_threshold_alert_type'; interface AlertTestInstance { instance: AlertInstanceMock; @@ -23,11 +23,23 @@ interface AlertTestInstance { let persistAlertInstances = false; +const mockOptions = { + alertId: '', + startedAt: new Date(), + previousStartedAt: null, + state: {}, + spaceId: '', + name: '', + tags: [], + createdBy: null, + updatedBy: null, +}; + describe('The metric threshold alert type', () => { describe('querying the entire infrastructure', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => - executor({ + executor(({ services, params: { sourceId, @@ -39,7 +51,7 @@ describe('The metric threshold alert type', () => { }, ], }, - }); + } as unknown) as MetricThresholdAlertExecutorOptions); test('alerts as expected with the > comparator', async () => { await execute(Comparator.GT, [0.75]); expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); @@ -109,6 +121,7 @@ describe('The metric threshold alert type', () => { describe('querying with a groupBy parameter', () => { const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { groupBy: 'something', @@ -159,6 +172,7 @@ describe('The metric threshold alert type', () => { groupBy: string = '' ) => executor({ + ...mockOptions, services, params: { groupBy, @@ -216,6 +230,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -242,6 +257,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -268,6 +284,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -294,6 +311,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (alertOnNoData: boolean) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -323,6 +341,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = () => executor({ + ...mockOptions, services, params: { criteria: [ @@ -348,6 +367,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -392,6 +412,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = () => executor({ + ...mockOptions, services, params: { sourceId: 'default', @@ -435,10 +456,7 @@ const mockLibs: any = { configuration: createMockStaticConfiguration({}), }; -const executor = createMetricThresholdExecutor(mockLibs) as (opts: { - params: AlertExecutorOptions['params']; - services: { callCluster: AlertExecutorOptions['params']['callCluster'] }; -}) => Promise; +const executor = createMetricThresholdExecutor(mockLibs); const services: AlertServicesMock = alertsMock.createAlertServices(); services.callCluster.mockImplementation(async (_: string, { body, index }: any) => { diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 60790648d9a9b..d63b42cd3b146 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -7,7 +7,6 @@ import { first, last } from 'lodash'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { RecoveredActionGroup } from '../../../../../alerts/common'; -import { AlertExecutorOptions } from '../../../../../alerts/server'; import { InfraBackendLibs } from '../../infra_types'; import { buildErrorAlertReason, @@ -18,10 +17,16 @@ import { } from '../common/messages'; import { createFormatter } from '../../../../common/formatters'; import { AlertStates } from './types'; -import { evaluateAlert } from './lib/evaluate_alert'; +import { evaluateAlert, EvaluatedAlertParams } from './lib/evaluate_alert'; +import { + MetricThresholdAlertExecutorOptions, + MetricThresholdAlertType, +} from './register_metric_threshold_alert_type'; -export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => - async function (options: AlertExecutorOptions) { +export const createMetricThresholdExecutor = ( + libs: InfraBackendLibs +): MetricThresholdAlertType['executor'] => + async function (options: MetricThresholdAlertExecutorOptions) { const { services, params } = options; const { criteria } = params; if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); @@ -36,7 +41,11 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => sourceId || 'default' ); const config = source.configuration; - const alertResults = await evaluateAlert(services.callCluster, params, config); + const alertResults = await evaluateAlert( + services.callCluster, + params as EvaluatedAlertParams, + config + ); // Because each alert result has the same group definitions, just grab the groups from the first one. const groups = Object.keys(first(alertResults)!); 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 134e7c336394d..ad66732ec2143 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 @@ -6,10 +6,9 @@ import { schema } from '@kbn/config-schema'; import { AlertType, - AlertTypeParams, - AlertTypeState, AlertInstanceState, AlertInstanceContext, + AlertExecutorOptions, } from '../../../../../alerts/server'; import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api/metrics_explorer'; import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; @@ -26,9 +25,20 @@ import { thresholdActionVariableDescription, } from '../common/messages'; -export function registerMetricThresholdAlertType( - libs: InfraBackendLibs -): AlertType { +export type MetricThresholdAlertType = AlertType< + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>; +export type MetricThresholdAlertExecutorOptions = AlertExecutorOptions< + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>; + +export function registerMetricThresholdAlertType(libs: InfraBackendLibs): MetricThresholdAlertType { const baseCriterion = { threshold: schema.arrayOf(schema.number()), comparator: oneOfLiterals(Object.values(Comparator)), diff --git a/x-pack/plugins/monitoring/common/types/alerts.ts b/x-pack/plugins/monitoring/common/types/alerts.ts index 0f10e0e48962b..91d39a5fafa16 100644 --- a/x-pack/plugins/monitoring/common/types/alerts.ts +++ b/x-pack/plugins/monitoring/common/types/alerts.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert, SanitizedAlert } from '../../../alerts/common'; +import { Alert, AlertTypeParams, SanitizedAlert } from '../../../alerts/common'; import { AlertParamType, AlertMessageTokenType, AlertSeverity } from '../enums'; -export type CommonAlert = Alert | SanitizedAlert; +export type CommonAlert = Alert | SanitizedAlert; export interface CommonAlertStatus { states: CommonAlertState[]; - rawAlert: Alert | SanitizedAlert; + rawAlert: Alert | SanitizedAlert; } export interface CommonAlertState { diff --git a/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx b/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx index 892ee0926ca38..c4e5ff343da17 100644 --- a/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx +++ b/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx @@ -5,9 +5,11 @@ */ import { i18n } from '@kbn/i18n'; +import { AlertTypeParams } from '../../../../../alerts/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ValidationResult } from '../../../../../triggers_actions_ui/public/types'; +export type MonitoringAlertTypeParams = ValidateOptions & AlertTypeParams; interface ValidateOptions { duration: string; threshold: number; diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx index 5054c47245f0f..7b5a8e3c5333f 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx @@ -7,10 +7,10 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_CPU_USAGE, ALERT_DETAILS } from '../../../common/constants'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; -export function createCpuUsageAlertType(): AlertTypeModel { +export function createCpuUsageAlertType(): AlertTypeModel { return { id: ALERT_CPU_USAGE, name: ALERT_DETAILS[ALERT_CPU_USAGE].label, diff --git a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx index 00b70658e4289..30f3400e8fa40 100644 --- a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx @@ -5,14 +5,14 @@ */ import React from 'react'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_DISK_USAGE, ALERT_DETAILS } from '../../../common/constants'; -export function createDiskUsageAlertType(): AlertTypeModel { +export function createDiskUsageAlertType(): AlertTypeModel { return { id: ALERT_DISK_USAGE, name: ALERT_DETAILS[ALERT_DISK_USAGE].label, diff --git a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx index 062c32c758794..54aada962ebd8 100644 --- a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx @@ -5,14 +5,14 @@ */ import React from 'react'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_MEMORY_USAGE, ALERT_DETAILS } from '../../../common/constants'; -export function createMemoryUsageAlertType(): AlertTypeModel { +export function createMemoryUsageAlertType(): AlertTypeModel { return { id: ALERT_MEMORY_USAGE, name: ALERT_DETAILS[ALERT_MEMORY_USAGE].label, diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index ebff72a255777..e31fc233de41b 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -13,7 +13,7 @@ import { AlertsClient, AlertServices, } from '../../../alerts/server'; -import { Alert, RawAlertInstance, SanitizedAlert } from '../../../alerts/common'; +import { Alert, AlertTypeParams, RawAlertInstance, SanitizedAlert } from '../../../alerts/common'; import { ActionsClient } from '../../../actions/server'; import { AlertState, @@ -135,7 +135,7 @@ export class BaseAlert { alertsClient: AlertsClient, actionsClient: ActionsClient, actions: AlertEnableAction[] - ): Promise { + ): Promise> { const existingAlertData = await alertsClient.find({ options: { search: this.alertOptions.id, @@ -170,7 +170,7 @@ export class BaseAlert { throttle = '1d', interval = '1m', } = this.alertOptions; - return await alertsClient.create({ + return await alertsClient.create({ data: { enabled: true, tags: [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts index 5731a51aeabc1..f0895f1367289 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts @@ -6,7 +6,7 @@ import { Alert } from '../../../../../alerts/common'; import { SERVER_APP_ID, NOTIFICATIONS_ID } from '../../../../common/constants'; -import { CreateNotificationParams } from './types'; +import { CreateNotificationParams, RuleNotificationAlertTypeParams } from './types'; import { addTags } from './add_tags'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; @@ -17,8 +17,8 @@ export const createNotifications = async ({ ruleAlertId, interval, name, -}: CreateNotificationParams): Promise => - alertsClient.create({ +}: CreateNotificationParams): Promise> => + alertsClient.create({ data: { name, tags: addTags([], ruleAlertId), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts index 5d3a328dd6fbb..1bf07a1574be6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FindResult } from '../../../../../alerts/server'; +import { AlertTypeParams, FindResult } from '../../../../../alerts/server'; import { NOTIFICATIONS_ID } from '../../../../common/constants'; import { FindNotificationParams } from './types'; @@ -24,7 +24,7 @@ export const findNotifications = async ({ filter, sortField, sortOrder, -}: FindNotificationParams): Promise => +}: FindNotificationParams): Promise> => alertsClient.find({ options: { fields, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts index fe9101335b4f5..74df77bf9bf74 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SanitizedAlert } from '../../../../../alerts/common'; +import { AlertTypeParams, SanitizedAlert } from '../../../../../alerts/common'; import { ReadNotificationParams, isAlertType } from './types'; import { findNotifications } from './find_notifications'; import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; @@ -13,7 +13,7 @@ export const readNotifications = async ({ alertsClient, id, ruleAlertId, -}: ReadNotificationParams): Promise => { +}: ReadNotificationParams): Promise | null> => { if (id != null) { try { const notification = await alertsClient.get({ id }); 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 c3cde1b319646..e4e9df552101b 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 @@ -18,11 +18,10 @@ import { Alert } from '../../../../../alerts/common'; import { NOTIFICATIONS_ID } from '../../../../common/constants'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; -export interface RuleNotificationAlertType extends Alert { - params: { - ruleAlertId: string; - }; +export interface RuleNotificationAlertTypeParams extends AlertTypeParams { + ruleAlertId: string; } +export type RuleNotificationAlertType = Alert; export interface FindNotificationParams { alertsClient: AlertsClient; @@ -79,22 +78,23 @@ export interface ReadNotificationParams { } export const isAlertTypes = ( - partialAlert: PartialAlert[] + partialAlert: Array> ): partialAlert is RuleNotificationAlertType[] => { return partialAlert.every((rule) => isAlertType(rule)); }; export const isAlertType = ( - partialAlert: PartialAlert + partialAlert: PartialAlert ): partialAlert is RuleNotificationAlertType => { return partialAlert.alertTypeId === NOTIFICATIONS_ID; }; -export type NotificationExecutorOptions = Omit & { - params: { - ruleAlertId: string; - }; -}; +export type NotificationExecutorOptions = AlertExecutorOptions< + RuleNotificationAlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; // This returns true because by default a NotificationAlertTypeDefinition is an AlertType // since we are only increasing the strictness of params. diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts index d6c8973215117..8528d53b51f5b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts @@ -6,7 +6,7 @@ import { PartialAlert } from '../../../../../alerts/server'; import { readNotifications } from './read_notifications'; -import { UpdateNotificationParams } from './types'; +import { RuleNotificationAlertTypeParams, UpdateNotificationParams } from './types'; import { addTags } from './add_tags'; import { createNotifications } from './create_notifications'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; @@ -18,11 +18,11 @@ export const updateNotifications = async ({ ruleAlertId, name, interval, -}: UpdateNotificationParams): Promise => { +}: UpdateNotificationParams): Promise | null> => { const notification = await readNotifications({ alertsClient, id: undefined, ruleAlertId }); if (interval && notification) { - return alertsClient.update({ + return alertsClient.update({ id: notification.id, data: { tags: addTags([], ruleAlertId), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index b185b8780abe2..3473948b000c7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -22,6 +22,8 @@ import { buildRouteValidation } from '../../../../utils/build_validation/route_v import { transformBulkError, createBulkErrorObject, buildSiemResponse } from '../utils'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'; +import { RuleTypeParams } from '../../types'; +import { Alert } from '../../../../../../alerts/common'; export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => { router.post( @@ -95,9 +97,12 @@ export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => }); } - const createdRule = await alertsClient.create({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const createdRule = (await alertsClient.create({ data: internalRule, - }); + })) as Alert; const ruleActions = await updateRulesNotifications({ ruleAlertId: createdRule.id, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index b52248f670188..c59d5d2a36fd5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -19,6 +19,8 @@ import { createRulesSchema } from '../../../../../common/detection_engine/schema import { newTransformValidate } from './validate'; import { createRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/create_rules_type_dependents'; import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'; +import { RuleTypeParams } from '../../types'; +import { Alert } from '../../../../../../alerts/common'; export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void => { router.post( @@ -85,9 +87,12 @@ export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void // This will create the endpoint list if it does not exist yet await context.lists?.getExceptionListClient().createEndpointList(); - const createdRule = await alertsClient.create({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const createdRule = (await alertsClient.create({ data: internalRule, - }); + })) as Alert; const ruleActions = await updateRulesNotifications({ ruleAlertId: createdRule.id, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts index 7a6cd707eb185..bd7f11b2d8756 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts @@ -31,6 +31,7 @@ import { OutputError, } from '../utils'; import { RuleActions } from '../../rule_actions/types'; +import { RuleTypeParams } from '../../types'; type PromiseFromStreams = ImportRulesSchemaDecoded | Error; @@ -172,7 +173,7 @@ export const transformAlertsToRules = (alerts: RuleAlertType[]): Array, ruleActions: Array, ruleStatuses?: Array> ): { @@ -203,7 +204,7 @@ export const transformFindAlerts = ( }; export const transform = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): Partial | null => { @@ -220,7 +221,7 @@ export const transform = ( export const transformOrBulkError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, ruleActions: RuleActions, ruleStatus?: unknown ): Partial | BulkError => { @@ -241,7 +242,7 @@ export const transformOrBulkError = ( export const transformOrImportError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, existingImportSuccessError: ImportSuccessError ): ImportSuccessError => { if (isAlertType(alert)) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts index 8653bdc0427e4..51b08cb3fce89 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts @@ -15,6 +15,7 @@ import { RulesSchema } from '../../../../../common/detection_engine/schemas/resp import { getResult, getFindResultStatus } from '../__mocks__/request_responses'; import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; +import { RuleTypeParams } from '../../types'; export const ruleOutput = (): RulesSchema => ({ actions: [], @@ -88,7 +89,7 @@ describe('validate', () => { describe('transformValidateFindAlerts', () => { test('it should do a validation correctly of a find alert', () => { - const findResult: FindResult = { + const findResult: FindResult = { data: [getResult()], page: 1, perPage: 0, @@ -111,7 +112,7 @@ describe('validate', () => { }); test('it should do an in-validation correctly of a partial alert', () => { - const findResult: FindResult = { + const findResult: FindResult = { data: [getResult()], page: 1, perPage: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts index 382186df16cd1..3e3b85a407fc2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts @@ -31,9 +31,10 @@ import { import { createBulkErrorObject, BulkError } from '../utils'; import { transformFindAlerts, transform, transformAlertToRule } from './utils'; import { RuleActions } from '../../rule_actions/types'; +import { RuleTypeParams } from '../../types'; export const transformValidateFindAlerts = ( - findResults: FindResult, + findResults: FindResult, ruleActions: Array, ruleStatuses?: Array> ): [ @@ -63,7 +64,7 @@ export const transformValidateFindAlerts = ( }; export const transformValidate = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): [RulesSchema | null, string | null] => { @@ -76,7 +77,7 @@ export const transformValidate = ( }; export const newTransformValidate = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): [FullResponseSchema | null, string | null] => { @@ -90,7 +91,7 @@ export const newTransformValidate = ( export const transformValidateBulkError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObjectsFindResponse ): RulesSchema | BulkError => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts index 0519a98df1fae..6339e92eb012b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts @@ -9,6 +9,7 @@ import { Alert } from '../../../../../alerts/common'; import { SERVER_APP_ID, SIGNALS_ID } from '../../../../common/constants'; import { CreateRulesOptions } from './types'; import { addTags } from './add_tags'; +import { PartialFilter, RuleTypeParams } from '../types'; export const createRules = async ({ alertsClient, @@ -59,8 +60,8 @@ export const createRules = async ({ version, exceptionsList, actions, -}: CreateRulesOptions): Promise => { - return alertsClient.create({ +}: CreateRulesOptions): Promise> => { + return alertsClient.create({ data: { name, tags: addTags(tags, ruleId, immutable), @@ -95,7 +96,10 @@ export const createRules = async ({ severityMapping, threat, threshold, - threatFilters, + /** + * TODO: Fix typing inconsistancy between `RuleTypeParams` and `CreateRulesOptions` + */ + threatFilters: threatFilters as PartialFilter[] | undefined, threatIndex, threatQuery, concurrentSearches, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts index 18b851c440e20..5ab97262515da 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts @@ -6,6 +6,7 @@ import { FindResult } from '../../../../../alerts/server'; import { SIGNALS_ID } from '../../../../common/constants'; +import { RuleTypeParams } from '../types'; import { FindRuleOptions } from './types'; export const getFilter = (filter: string | null | undefined) => { @@ -24,7 +25,7 @@ export const findRules = async ({ filter, sortField, sortOrder, -}: FindRuleOptions): Promise => { +}: FindRuleOptions): Promise> => { return alertsClient.find({ options: { fields, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts index 4c01318f02cde..1f43706c17fec 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -5,7 +5,7 @@ */ import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; -import { Alert } from '../../../../../alerts/common'; +import { Alert, AlertTypeParams } from '../../../../../alerts/common'; import { AlertsClient } from '../../../../../alerts/server'; import { createRules } from './create_rules'; import { PartialFilter } from '../types'; @@ -14,8 +14,8 @@ export const installPrepackagedRules = ( alertsClient: AlertsClient, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string -): Array> => - rules.reduce>>((acc, rule) => { +): Array>> => + rules.reduce>>>((acc, rule) => { const { anomaly_threshold: anomalyThreshold, author, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts index b2303d48b0517..484dac8e31fb4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts @@ -9,8 +9,9 @@ import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; import { SanitizedAlert } from '../../../../../alerts/common'; +import { RuleTypeParams } from '../types'; -const rule: SanitizedAlert = { +const rule: SanitizedAlert = { id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', name: 'Detect Root/Admin Users', tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`], @@ -67,6 +68,8 @@ const rule: SanitizedAlert = { note: '# Investigative notes', version: 1, exceptionsList: [ + /** + TODO: fix this mock. Which the typing has revealed is wrong { field: 'source.ip', values_operator: 'included', @@ -96,8 +99,31 @@ const rule: SanitizedAlert = { ], }, ], - }, + },*/ ], + /** + * The fields below were missing as the type was partial and hence not technically correct + */ + author: [], + buildingBlockType: undefined, + eventCategoryOverride: undefined, + license: undefined, + savedId: undefined, + interval: undefined, + riskScoreMapping: undefined, + ruleNameOverride: undefined, + name: undefined, + severityMapping: undefined, + tags: undefined, + threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, + concurrentSearches: undefined, + itemsPerSearch: undefined, + timestampOverride: undefined, }, createdAt: new Date('2019-12-13T16:40:33.400Z'), updatedAt: new Date('2019-12-13T16:40:33.400Z'), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts index c86526cee9302..c1720c4fa3587 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts @@ -13,6 +13,7 @@ import { addTags } from './add_tags'; import { calculateVersion, calculateName, calculateInterval, removeUndefined } from './utils'; import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_saved_objects_client'; import { internalRuleUpdate } from '../schemas/rule_schemas'; +import { RuleTypeParams } from '../types'; class PatchError extends Error { public readonly statusCode: number; @@ -71,7 +72,7 @@ export const patchRules = async ({ anomalyThreshold, machineLearningJobId, actions, -}: PatchRulesOptions): Promise => { +}: PatchRulesOptions): Promise | null> => { if (rule == null) { return null; } @@ -185,10 +186,13 @@ export const patchRules = async ({ throw new PatchError(`Applying patch would create invalid rule: ${errors}`, 400); } - const update = await alertsClient.update({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const update = (await alertsClient.update({ id: rule.id, data: validated, - }); + })) as PartialAlert; if (rule.enabled && enabled === false) { await alertsClient.disable({ id: rule.id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts index e4bb65a907e2d..bc277bd2089a6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts @@ -6,6 +6,7 @@ import { SanitizedAlert } from '../../../../../alerts/common'; import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; +import { RuleTypeParams } from '../types'; import { findRules } from './find_rules'; import { isAlertType, ReadRuleOptions } from './types'; @@ -21,7 +22,7 @@ export const readRules = async ({ alertsClient, id, ruleId, -}: ReadRuleOptions): Promise => { +}: ReadRuleOptions): Promise | null> => { if (id != null) { try { const rule = await alertsClient.get({ id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts index aaeb487ad9a78..34dab20c279b4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts @@ -103,9 +103,7 @@ import { SIGNALS_ID } from '../../../../common/constants'; import { RuleTypeParams, PartialFilter } from '../types'; import { ListArrayOrUndefined, ListArray } from '../../../../common/detection_engine/schemas/types'; -export interface RuleAlertType extends Alert { - params: RuleTypeParams; -} +export type RuleAlertType = Alert; // eslint-disable-next-line @typescript-eslint/no-explicit-any export interface IRuleStatusSOAttributes extends Record { @@ -173,11 +171,15 @@ export interface Clients { alertsClient: AlertsClient; } -export const isAlertTypes = (partialAlert: PartialAlert[]): partialAlert is RuleAlertType[] => { +export const isAlertTypes = ( + partialAlert: Array> +): partialAlert is RuleAlertType[] => { return partialAlert.every((rule) => isAlertType(rule)); }; -export const isAlertType = (partialAlert: PartialAlert): partialAlert is RuleAlertType => { +export const isAlertType = ( + partialAlert: PartialAlert +): partialAlert is RuleAlertType => { return partialAlert.alertTypeId === SIGNALS_ID; }; @@ -305,7 +307,7 @@ export interface PatchRulesOptions { version: VersionOrUndefined; exceptionsList: ListArrayOrUndefined; actions: RuleAlertAction[] | undefined; - rule: SanitizedAlert | null; + rule: SanitizedAlert | null; } export interface ReadRuleOptions { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts index c63bd01cd1813..b3e0aaae715c9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts @@ -15,13 +15,14 @@ import { addTags } from './add_tags'; import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_saved_objects_client'; import { typeSpecificSnakeToCamel } from '../schemas/rule_converters'; import { InternalRuleUpdate } from '../schemas/rule_schemas'; +import { RuleTypeParams } from '../types'; export const updateRules = async ({ alertsClient, savedObjectsClient, defaultOutputIndex, ruleUpdate, -}: UpdateRulesOptions): Promise => { +}: UpdateRulesOptions): Promise | null> => { const existingRule = await readRules({ alertsClient, ruleId: ruleUpdate.rule_id, @@ -77,10 +78,13 @@ export const updateRules = async ({ notifyWhen: null, }; - const update = await alertsClient.update({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const update = (await alertsClient.update({ id: existingRule.id, data: newInternalRule, - }); + })) as PartialAlert; if (existingRule.enabled && enabled === false) { await alertsClient.disable({ id: existingRule.id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 9a40573095a1a..9c2ea0945297e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -49,7 +49,10 @@ jest.mock('./find_ml_signals'); jest.mock('./bulk_create_ml_signals'); jest.mock('../../../../common/detection_engine/parse_schedule_dates'); -const getPayload = (ruleAlert: RuleAlertType, services: AlertServicesMock) => ({ +const getPayload = ( + ruleAlert: RuleAlertType, + services: AlertServicesMock +): RuleExecutorOptions => ({ alertId: ruleAlert.id, services, params: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 7fd99a17598ae..17267b1f60f24 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -68,6 +68,7 @@ import { getIndexVersion } from '../routes/index/get_index_version'; import { MIN_EQL_RULE_INDEX_VERSION } from '../routes/index/get_signals_template'; import { filterEventsAgainstList } from './filters/filter_events_against_list'; import { isOutdated } from '../migrations/helpers'; +import { RuleTypeParams } from '../types'; export const signalRulesAlertType = ({ logger, @@ -88,7 +89,12 @@ export const signalRulesAlertType = ({ actionGroups: siemRuleActionGroups, defaultActionGroupId: 'default', validate: { - params: signalParamsSchema(), + /** + * TODO: Add support for Schema + */ + params: (signalParamsSchema() as unknown) as { + validate: (object: unknown) => RuleTypeParams; + }, }, producer: SERVER_APP_ID, minimumLicenseRequired: 'basic', 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 38a8ebfd06396..62339f50d939c 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 @@ -10,7 +10,6 @@ import { Status } from '../../../../common/detection_engine/schemas/common/schem import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; import { AlertType, - AlertTypeParams, AlertTypeState, AlertInstanceState, AlertInstanceContext, @@ -131,24 +130,27 @@ export type BaseSignalHit = BaseHit; export type EqlSignalSearchResponse = EqlSearchResponse; -export type RuleExecutorOptions = Omit & { - params: RuleTypeParams; -}; +export type RuleExecutorOptions = AlertExecutorOptions< + RuleTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; // This returns true because by default a RuleAlertTypeDefinition is an AlertType // since we are only increasing the strictness of params. export const isAlertExecutor = ( obj: SignalRuleAlertTypeDefinition -): obj is AlertType => { +): obj is AlertType => { return true; }; -export type SignalRuleAlertTypeDefinition = Omit< - AlertType, - 'executor' -> & { - executor: ({ services, params, state }: RuleExecutorOptions) => Promise; -}; +export type SignalRuleAlertTypeDefinition = AlertType< + RuleTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; export interface Ancestor { rule?: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts index 57535178c5280..90f2ce638855b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts @@ -51,10 +51,11 @@ import { import { LegacyCallAPIOptions } from '../../../../../../src/core/server'; import { Filter } from '../../../../../../src/plugins/data/server'; import { ListArrayOrUndefined } from '../../../common/detection_engine/schemas/types'; +import { AlertTypeParams } from '../../../../alerts/common'; export type PartialFilter = Partial; -export interface RuleTypeParams { +export interface RuleTypeParams extends AlertTypeParams { anomalyThreshold: AnomalyThresholdOrUndefined; author: AuthorOrUndefined; buildingBlockType: BuildingBlockTypeOrUndefined; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts index 89252f7c90104..d1f64c9298f15 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; import { Query } from '../../../../../../src/plugins/data/common'; -export interface GeoContainmentAlertParams { +export interface GeoContainmentAlertParams extends AlertTypeParams { index: string; indexId: string; geoField: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts index 5ac9c7fd29317..3f487135f0474 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; import { Query } from '../../../../../../src/plugins/data/common'; export enum TrackingEvent { @@ -12,7 +13,7 @@ export enum TrackingEvent { crossed = 'crossed', } -export interface GeoThresholdAlertParams { +export interface GeoThresholdAlertParams extends AlertTypeParams { index: string; indexId: string; geoField: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts index 356b0fbbc0845..4868b92feaeb9 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; + export interface Comparator { text: string; value: string; @@ -24,7 +26,7 @@ export interface GroupByType { validNormalizedTypes: string[]; } -export interface IndexThresholdAlertParams { +export interface IndexThresholdAlertParams extends AlertTypeParams { index: string[]; timeField?: string; aggType: string; 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 a2b8d7eac7c5e..31c2db81840ee 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 @@ -14,6 +14,7 @@ import { AlertTypeState, AlertInstanceState, AlertInstanceContext, + AlertTypeParams, } from '../../../../alerts/server'; import { Query } from '../../../../../../src/plugins/data/common/query'; @@ -101,7 +102,7 @@ export const ParamsSchema = schema.object({ boundaryIndexQuery: schema.maybe(schema.any({})), }); -export interface GeoContainmentParams { +export interface GeoContainmentParams extends AlertTypeParams { index: string; indexId: string; geoField: string; @@ -116,10 +117,34 @@ export interface GeoContainmentParams { indexQuery?: Query; boundaryIndexQuery?: Query; } +export interface GeoContainmentState extends AlertTypeState { + shapesFilters: Record; + shapesIdsNamesMap: Record; +} +export interface GeoContainmentInstanceState extends AlertInstanceState { + location: number[]; + shapeLocationId: string; + dateInShape: string | null; + docId: string; +} +export interface GeoContainmentInstanceContext extends AlertInstanceContext { + entityId: string; + entityDateTime: string | null; + entityDocumentId: string; + detectionDateTime: string; + entityLocation: string; + containingBoundaryId: string; + containingBoundaryName: unknown; +} + +export type GeoContainmentAlertType = AlertType< + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext +>; -export function getAlertType( - logger: Logger -): AlertType { +export function getAlertType(logger: Logger): GeoContainmentAlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoContainment.alertTypeTitle', { defaultMessage: 'Geo tracking containment', }); 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 ed951f340f8ed..612eff3014985 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 @@ -8,15 +8,16 @@ import _ from 'lodash'; import { SearchResponse } from 'elasticsearch'; import { Logger } from 'src/core/server'; import { executeEsQueryFactory, getShapesFilters, OTHER_CATEGORY } from './es_query_builder'; -import { AlertServices, AlertTypeState } from '../../../../alerts/server'; -import { ActionGroupId, GEO_CONTAINMENT_ID, GeoContainmentParams } from './alert_type'; +import { AlertServices } from '../../../../alerts/server'; +import { + ActionGroupId, + GEO_CONTAINMENT_ID, + GeoContainmentInstanceState, + GeoContainmentAlertType, + GeoContainmentInstanceContext, +} from './alert_type'; -export interface LatestEntityLocation { - location: number[]; - shapeLocationId: string; - dateInShape: string | null; - docId: string; -} +export type LatestEntityLocation = GeoContainmentInstanceState; // Flatten agg results and get latest locations for each entity export function transformResults( @@ -97,9 +98,10 @@ function getOffsetTime(delayOffsetWithUnits: string, oldTime: Date): Date { export function getActiveEntriesAndGenerateAlerts( prevLocationMap: Record, currLocationMap: Map, - alertInstanceFactory: ( - x: string - ) => { scheduleActions: (x: string, y: Record) => void }, + alertInstanceFactory: AlertServices< + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >['alertInstanceFactory'], shapesIdsNamesMap: Record, currIntervalEndTime: Date ) { @@ -127,23 +129,8 @@ export function getActiveEntriesAndGenerateAlerts( }); return allActiveEntriesMap; } - -export const getGeoContainmentExecutor = (log: Logger) => - async function ({ - previousStartedAt, - startedAt, - services, - params, - alertId, - state, - }: { - previousStartedAt: Date | null; - startedAt: Date; - services: AlertServices; - params: GeoContainmentParams; - alertId: string; - state: AlertTypeState; - }): Promise { +export const getGeoContainmentExecutor = (log: Logger): GeoContainmentAlertType['executor'] => + async function ({ previousStartedAt, startedAt, services, params, alertId, state }) { const { shapesFilters, shapesIdsNamesMap } = state.shapesFilters ? state : await getShapesFilters( 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 02116d0701bfa..86e9e4fa3d672 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 @@ -6,7 +6,13 @@ import { Logger } from 'src/core/server'; import { AlertingSetup } from '../../types'; -import { GeoContainmentParams, getAlertType } from './alert_type'; +import { + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext, + getAlertType, +} from './alert_type'; interface RegisterParams { logger: Logger; @@ -15,5 +21,10 @@ interface RegisterParams { export function register(params: RegisterParams) { const { logger, alerts } = params; - alerts.registerType(getAlertType(logger)); + alerts.registerType< + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >(getAlertType(logger)); } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts index 885081e859dd7..26b51060c2e73 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts @@ -10,6 +10,8 @@ import sampleJsonResponseWithNesting from './es_sample_response_with_nesting.jso import { getActiveEntriesAndGenerateAlerts, transformResults } from '../geo_containment'; import { SearchResponse } from 'elasticsearch'; import { OTHER_CATEGORY } from '../es_query_builder'; +import { alertsMock } from '../../../../../alerts/server/mocks'; +import { GeoContainmentInstanceContext, GeoContainmentInstanceState } from '../alert_type'; describe('geo_containment', () => { describe('transformResults', () => { @@ -191,8 +193,12 @@ describe('geo_containment', () => { const emptyShapesIdsNamesMap = {}; const alertInstanceFactory = (instanceId: string) => { - return { - scheduleActions: (actionGroupId: string, context: Record) => { + const alertInstance = alertsMock.createAlertInstanceFactory< + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >(); + alertInstance.scheduleActions.mockImplementation( + (actionGroupId: string, context?: GeoContainmentInstanceContext) => { const contextKeys = Object.keys(expectedContext[0].context); const contextSubset = _.pickBy(context, (v, k) => contextKeys.includes(k)); testAlertActionArr.push({ @@ -200,9 +206,12 @@ describe('geo_containment', () => { instanceId, context: contextSubset, }); - }, - }; + return alertInstance; + } + ); + return alertInstance; }; + const currentDateTime = new Date(); it('should use currently active entities if no older entity entries', () => { 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 1a6461c2e8398..20c74188f3a06 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 @@ -14,6 +14,7 @@ import { AlertTypeState, AlertInstanceState, AlertInstanceContext, + AlertTypeParams, } from '../../../../alerts/server'; import { Query } from '../../../../../../src/plugins/data/common/query'; @@ -160,7 +161,7 @@ export const ParamsSchema = schema.object({ boundaryIndexQuery: schema.maybe(schema.any({})), }); -export interface GeoThresholdParams { +export interface GeoThresholdParams extends AlertTypeParams { index: string; indexId: string; geoField: string; @@ -176,10 +177,41 @@ export interface GeoThresholdParams { indexQuery?: Query; boundaryIndexQuery?: Query; } +export interface GeoThresholdState extends AlertTypeState { + shapesFilters: Record; + shapesIdsNamesMap: Record; + prevLocationArr: GeoThresholdInstanceState[]; +} +export interface GeoThresholdInstanceState extends AlertInstanceState { + location: number[]; + shapeLocationId: string; + entityName: string; + dateInShape: string | null; + docId: string; +} +export interface GeoThresholdInstanceContext extends AlertInstanceContext { + entityId: string; + timeOfDetection: number; + crossingLine: string; + toEntityLocation: string; + toEntityDateTime: string | null; + toEntityDocumentId: string; + toBoundaryId: string; + toBoundaryName: unknown; + fromEntityLocation: string; + fromEntityDateTime: string | null; + fromEntityDocumentId: string; + fromBoundaryId: string; + fromBoundaryName: unknown; +} -export function getAlertType( - logger: Logger -): AlertType { +export type GeoThresholdAlertType = AlertType< + GeoThresholdParams, + GeoThresholdState, + GeoThresholdInstanceState, + GeoThresholdInstanceContext +>; +export function getAlertType(logger: Logger): GeoThresholdAlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoThreshold.alertTypeTitle', { defaultMessage: 'Geo tracking threshold', }); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts index 5cb4156e84623..a2375537ae6e5 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts @@ -8,16 +8,14 @@ import _ from 'lodash'; import { SearchResponse } from 'elasticsearch'; import { Logger } from 'src/core/server'; import { executeEsQueryFactory, getShapesFilters, OTHER_CATEGORY } from './es_query_builder'; -import { AlertServices, AlertTypeState } from '../../../../alerts/server'; -import { ActionGroupId, GEO_THRESHOLD_ID, GeoThresholdParams } from './alert_type'; +import { + ActionGroupId, + GEO_THRESHOLD_ID, + GeoThresholdAlertType, + GeoThresholdInstanceState, +} from './alert_type'; -interface LatestEntityLocation { - location: number[]; - shapeLocationId: string; - entityName: string; - dateInShape: string | null; - docId: string; -} +export type LatestEntityLocation = GeoThresholdInstanceState; // Flatten agg results and get latest locations for each entity export function transformResults( @@ -172,22 +170,8 @@ function getOffsetTime(delayOffsetWithUnits: string, oldTime: Date): Date { return adjustedDate; } -export const getGeoThresholdExecutor = (log: Logger) => - async function ({ - previousStartedAt, - startedAt, - services, - params, - alertId, - state, - }: { - previousStartedAt: Date | null; - startedAt: Date; - services: AlertServices; - params: GeoThresholdParams; - alertId: string; - state: AlertTypeState; - }): Promise { +export const getGeoThresholdExecutor = (log: Logger): GeoThresholdAlertType['executor'] => + async function ({ previousStartedAt, startedAt, services, params, alertId, state }) { const { shapesFilters, shapesIdsNamesMap } = state.shapesFilters ? state : await getShapesFilters( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts index 538c6be89ab4b..09c9e5b42f022 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert, AlertType } from '../../types'; +import { Alert, AlertType, AlertUpdates } from '../../types'; import { httpServiceMock } from '../../../../../../src/core/public/mocks'; import { createAlert, @@ -538,7 +538,7 @@ describe('deleteAlerts', () => { describe('createAlert', () => { test('should call create alert API', async () => { - const alertToCreate = { + const alertToCreate: AlertUpdates = { name: 'test', consumer: 'alerts', tags: ['foo'], @@ -553,10 +553,13 @@ describe('createAlert', () => { notifyWhen: 'onActionGroupChange' as AlertNotifyWhenType, createdAt: new Date('1970-01-01T00:00:00.000Z'), updatedAt: new Date('1970-01-01T00:00:00.000Z'), - apiKey: null, apiKeyOwner: null, + createdBy: null, + updatedBy: null, + muteAll: false, + mutedInstanceIds: [], }; - const resolvedValue: Alert = { + const resolvedValue = { ...alertToCreate, id: '123', createdBy: null, diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index f2c8957400fa5..05bfba27420c7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -35,12 +35,12 @@ import { AlertEditProps } from './application/sections/alert_form/alert_edit'; export interface TriggersAndActionsUIPublicPluginSetup { actionTypeRegistry: TypeRegistry; - alertTypeRegistry: TypeRegistry; + alertTypeRegistry: TypeRegistry>; } export interface TriggersAndActionsUIPublicPluginStart { actionTypeRegistry: TypeRegistry; - alertTypeRegistry: TypeRegistry; + alertTypeRegistry: TypeRegistry>; getAddConnectorFlyout: ( props: Omit ) => ReactElement; diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index cd1ebe47a8c22..b8c764817c72f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -8,12 +8,12 @@ import type { DocLinksStart } from 'kibana/public'; import { ComponentType } from 'react'; import { ChartsPluginSetup } from 'src/plugins/charts/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { ActionGroup, AlertActionParam } from '../../alerts/common'; +import { ActionGroup, AlertActionParam, AlertTypeParams } from '../../alerts/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; import { AlertType as CommonAlertType } from '../../alerts/common'; import { - SanitizedAlert as Alert, + SanitizedAlert, AlertAction, AlertAggregations, AlertTaskState, @@ -23,6 +23,11 @@ import { AlertingFrameworkHealth, AlertNotifyWhenType, } from '../../alerts/common'; + +// In Triggers and Actions we treat all `Alert`s as `SanitizedAlert` +// so the `Params` is a black-box of Record +type Alert = SanitizedAlert; + export { Alert, AlertAction, @@ -169,14 +174,14 @@ export interface AlertTableItem extends Alert { } export interface AlertTypeParamsExpressionProps< - AlertParamsType = unknown, + Params extends AlertTypeParams = AlertTypeParams, MetaData = Record > { - alertParams: AlertParamsType; + alertParams: Params; alertInterval: string; alertThrottle: string; setAlertParams: (property: string, value: any) => void; - setAlertProperty: (key: Key, value: Alert[Key] | null) => void; + setAlertProperty: (key: Key, value: any) => void; errors: IErrorObject; defaultActionGroupId: string; actionGroups: ActionGroup[]; @@ -185,16 +190,16 @@ export interface AlertTypeParamsExpressionProps< data: DataPublicPluginStart; } -export interface AlertTypeModel { +export interface AlertTypeModel { id: string; name: string | JSX.Element; description: string; iconClass: string; documentationUrl: string | ((docLinks: DocLinksStart) => string) | null; - validate: (alertParams: AlertParamsType) => ValidationResult; + validate: (alertParams: Params) => ValidationResult; alertParamsExpression: | React.FunctionComponent - | React.LazyExoticComponent>>; + | React.LazyExoticComponent>>; requiresAppContext: boolean; defaultActionMessage?: string; } diff --git a/x-pack/plugins/uptime/public/state/actions/types.ts b/x-pack/plugins/uptime/public/state/actions/types.ts index 8d87ae5d52cb2..322f1eb0136b4 100644 --- a/x-pack/plugins/uptime/public/state/actions/types.ts +++ b/x-pack/plugins/uptime/public/state/actions/types.ts @@ -6,7 +6,8 @@ import { Action } from 'redux-actions'; import { IHttpFetchError } from 'src/core/public'; -import { Alert } from '../../../../triggers_actions_ui/public'; +import { Alert } from '../../../../alerts/common'; +import { UptimeAlertTypeParams } from '../alerts/alerts'; export interface AsyncAction { get: (payload: Payload) => Action; @@ -59,5 +60,5 @@ export interface AlertsResult { page: number; perPage: number; total: number; - data: Alert[]; + data: Array>; } diff --git a/x-pack/plugins/uptime/public/state/alerts/alerts.ts b/x-pack/plugins/uptime/public/state/alerts/alerts.ts index aeb81bb413aa7..43186e0ef5422 100644 --- a/x-pack/plugins/uptime/public/state/alerts/alerts.ts +++ b/x-pack/plugins/uptime/public/state/alerts/alerts.ts @@ -20,10 +20,8 @@ import { fetchMonitorAlertRecords, NewAlertParams, } from '../api/alerts'; -import { - ActionConnector as RawActionConnector, - Alert, -} from '../../../../triggers_actions_ui/public'; +import { ActionConnector as RawActionConnector } from '../../../../triggers_actions_ui/public'; +import { Alert } from '../../../../alerts/common'; import { kibanaService } from '../kibana_service'; import { monitorIdSelector } from '../selectors'; import { AlertsResult, MonitorIdParam } from '../actions/types'; @@ -31,13 +29,22 @@ import { simpleAlertEnabled } from '../../lib/alert_types/alert_messages'; export type ActionConnector = Omit; -export const createAlertAction = createAsyncAction('CREATE ALERT'); +/** + * TODO: Use actual AlertType Params type that's specific to Uptime instead of `any` + */ +export type UptimeAlertTypeParams = Record; + +export const createAlertAction = createAsyncAction< + NewAlertParams, + Alert | null +>('CREATE ALERT'); export const getConnectorsAction = createAsyncAction<{}, ActionConnector[]>('GET CONNECTORS'); export const getMonitorAlertsAction = createAsyncAction<{}, AlertsResult | null>('GET ALERTS'); -export const getAnomalyAlertAction = createAsyncAction( - 'GET EXISTING ALERTS' -); +export const getAnomalyAlertAction = createAsyncAction< + MonitorIdParam, + Alert +>('GET EXISTING ALERTS'); export const deleteAlertAction = createAsyncAction<{ alertId: string }, string | null>( 'DELETE ALERTS' ); @@ -47,9 +54,9 @@ export const deleteAnomalyAlertAction = createAsyncAction<{ alertId: string }, a interface AlertState { connectors: AsyncInitState; - newAlert: AsyncInitState; + newAlert: AsyncInitState>; alerts: AsyncInitState; - anomalyAlert: AsyncInitState; + anomalyAlert: AsyncInitState>; alertDeletion: AsyncInitState; anomalyAlertDeletion: AsyncInitState; } diff --git a/x-pack/plugins/uptime/public/state/api/alerts.ts b/x-pack/plugins/uptime/public/state/api/alerts.ts index da86da12a70ca..9d4dd3a1253c3 100644 --- a/x-pack/plugins/uptime/public/state/api/alerts.ts +++ b/x-pack/plugins/uptime/public/state/api/alerts.ts @@ -9,9 +9,10 @@ import { apiService } from './utils'; import { ActionConnector } from '../alerts/alerts'; import { AlertsResult, MonitorIdParam } from '../actions/types'; -import { Alert, AlertAction } from '../../../../triggers_actions_ui/public'; +import { AlertAction } from '../../../../triggers_actions_ui/public'; import { API_URLS } from '../../../common/constants'; import { MonitorStatusTranslations } from '../../../common/translations'; +import { Alert, AlertTypeParams } from '../../../../alerts/common'; const { MONITOR_STATUS } = ACTION_GROUP_DEFINITIONS; @@ -21,7 +22,7 @@ export const fetchConnectors = async () => { return await apiService.get(API_URLS.ALERT_ACTIONS); }; -export interface NewAlertParams { +export interface NewAlertParams extends AlertTypeParams { monitorId: string; monitorName?: string; defaultActions: ActionConnector[]; @@ -80,7 +81,9 @@ export const fetchMonitorAlertRecords = async (): Promise => { return await apiService.get(API_URLS.ALERTS_FIND, data); }; -export const fetchAlertRecords = async ({ monitorId }: MonitorIdParam): Promise => { +export const fetchAlertRecords = async ({ + monitorId, +}: MonitorIdParam): Promise> => { const data = { page: 1, per_page: 500, @@ -90,7 +93,7 @@ export const fetchAlertRecords = async ({ monitorId }: MonitorIdParam): Promise< sort_order: 'asc', }; const alerts = await apiService.get(API_URLS.ALERTS_FIND, data); - return alerts.data.find((alert: Alert) => alert.params.monitorId === monitorId); + return alerts.data.find((alert: Alert) => alert.params.monitorId === monitorId); }; export const disableAlertById = async ({ alertId }: { alertId: string }) => { diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts index 65f9abfd2e60d..d143e33fb8e96 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/types.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts @@ -6,16 +6,17 @@ import { UptimeCorePlugins, UptimeCoreSetup } from '../adapters'; import { UMServerLibs } from '../lib'; -import { - AlertType, - AlertTypeParams, - AlertTypeState, - AlertInstanceState, - AlertInstanceContext, -} from '../../../../alerts/server'; +import { AlertType, AlertInstanceState, AlertInstanceContext } from '../../../../alerts/server'; +export type UptimeAlertTypeParam = Record; +export type UptimeAlertTypeState = Record; export type UptimeAlertTypeFactory = ( server: UptimeCoreSetup, libs: UMServerLibs, plugins: UptimeCorePlugins -) => AlertType; +) => AlertType< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; 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 b1f1edd752c1b..a4a2f2c64db1b 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 @@ -7,37 +7,44 @@ import { SavedObjectsClientContract } from 'kibana/server'; import { AlertExecutorOptions, - AlertType, - AlertTypeParams, - AlertTypeState, AlertInstanceState, AlertInstanceContext, } from '../../../../alerts/server'; import { savedObjectsAdapter } from '../saved_objects'; import { DynamicSettings } from '../../../common/runtime_types'; import { createUptimeESClient, UptimeESClient } from '../lib'; +import { UptimeAlertTypeFactory, UptimeAlertTypeParam, UptimeAlertTypeState } from './types'; export interface UptimeAlertType - extends Omit< - AlertType, - 'executor' | 'producer' - > { + extends Omit, 'executor' | 'producer'> { executor: ({ options, uptimeEsClient, dynamicSettings, }: { - options: AlertExecutorOptions; + options: AlertExecutorOptions< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext + >; uptimeEsClient: UptimeESClient; dynamicSettings: DynamicSettings; savedObjectsClient: SavedObjectsClientContract; - }) => Promise; + }) => Promise; } export const uptimeAlertWrapper = (uptimeAlert: UptimeAlertType) => ({ ...uptimeAlert, producer: 'uptime', - executor: async (options: AlertExecutorOptions) => { + executor: async ( + options: AlertExecutorOptions< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext + > + ) => { const { services: { scopedClusterClient: esClient, savedObjectsClient }, } = options; From 5ddcf7a67d2501a30963c18a3432e3bdd15b433b Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 21 Dec 2020 11:33:44 +0000 Subject: [PATCH 03/13] fixed typing in tests --- .../examples/alerting_example/common/constants.ts | 4 +++- .../public/components/view_astros_alert.tsx | 4 ++-- .../alerting_example/server/alert_types/astros.ts | 6 +++++- .../alerts/ccr_read_exceptions_alert/index.tsx | 5 +++-- .../fixtures/plugins/alerts/server/alert_types.ts | 14 ++++++++------ .../fixtures/plugins/alerts/server/plugin.ts | 11 +++++++++-- 6 files changed, 30 insertions(+), 14 deletions(-) diff --git a/x-pack/examples/alerting_example/common/constants.ts b/x-pack/examples/alerting_example/common/constants.ts index 40cc298db795a..8e4ea4faf014c 100644 --- a/x-pack/examples/alerting_example/common/constants.ts +++ b/x-pack/examples/alerting_example/common/constants.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../plugins/alerts/common'; + export const ALERTING_EXAMPLE_APP_ID = 'AlertingExample'; // always firing export const DEFAULT_INSTANCES_TO_GENERATE = 5; -export interface AlwaysFiringParams { +export interface AlwaysFiringParams extends AlertTypeParams { instances?: number; thresholds?: { small?: number; diff --git a/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx b/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx index e4687c75fa0b7..eb682a86f5ff6 100644 --- a/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx +++ b/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx @@ -23,7 +23,7 @@ import { withRouter, RouteComponentProps } from 'react-router-dom'; import { CoreStart } from 'kibana/public'; import { isEmpty } from 'lodash'; import { Alert, AlertTaskState, BASE_ALERT_API_PATH } from '../../../../plugins/alerts/common'; -import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; +import { ALERTING_EXAMPLE_APP_ID, AlwaysFiringParams } from '../../common/constants'; type Props = RouteComponentProps & { http: CoreStart['http']; @@ -34,7 +34,7 @@ function hasCraft(state: any): state is { craft: string } { return state && state.craft; } export const ViewPeopleInSpaceAlertPage = withRouter(({ http, id }: Props) => { - const [alert, setAlert] = useState(null); + const [alert, setAlert] = useState | null>(null); const [alertState, setAlertState] = useState(null); useEffect(() => { 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 27a8bfc7a53a3..22c2f25c410cd 100644 --- a/x-pack/examples/alerting_example/server/alert_types/astros.ts +++ b/x-pack/examples/alerting_example/server/alert_types/astros.ts @@ -38,7 +38,11 @@ function getCraftFilter(craft: string) { craft === Craft.OuterSpace ? true : craft === person.craft; } -export const alertType: AlertType = { +export const alertType: AlertType< + { outerSpaceCapacity: number; craft: string; op: string }, + { peopleInSpace: number }, + { craft: string } +> = { id: 'example.people-in-space', name: 'People In Space Right Now', actionGroups: [{ id: 'default', name: 'default' }], diff --git a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx index 4d22d422ecda6..6d7751d91b761 100644 --- a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx @@ -9,8 +9,9 @@ import { i18n } from '@kbn/i18n'; import { Expression, Props } from '../components/duration/expression'; import { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public'; import { ALERT_CCR_READ_EXCEPTIONS, ALERT_DETAILS } from '../../../common/constants'; +import { AlertTypeParams } from '../../../../alerts/common'; -interface ValidateOptions { +interface ValidateOptions extends AlertTypeParams { duration: string; } @@ -30,7 +31,7 @@ const validate = (inputValues: ValidateOptions): ValidationResult => { return validationResult; }; -export function createCCRReadExceptionsAlertType(): AlertTypeModel { +export function createCCRReadExceptionsAlertType(): AlertTypeModel { return { id: ALERT_CCR_READ_EXCEPTIONS, description: ALERT_DETAILS[ALERT_CCR_READ_EXCEPTIONS].description, 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 b4ee273e57d61..30c19f735b75d 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 @@ -13,6 +13,8 @@ import { AlertType, AlertInstanceState, AlertInstanceContext, + AlertTypeState, + AlertTypeParams, } from '../../../../../../../plugins/alerts/server'; export const EscapableStrings = { @@ -50,7 +52,7 @@ function getAlwaysFiringAlertType() { groupsToScheduleActionsInSeries: schema.maybe(schema.arrayOf(schema.nullable(schema.string()))), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { groupInSeriesIndex?: number; } interface InstanceState extends AlertInstanceState { @@ -59,7 +61,7 @@ function getAlwaysFiringAlertType() { interface InstanceContext extends AlertInstanceContext { instanceContextValue: boolean; } - const result: AlertType = { + const result: AlertType = { id: 'test.always-firing', name: 'Test: Always Firing', actionGroups: [ @@ -141,7 +143,7 @@ async function alwaysFiringExecutor(alertExecutorOptions: any) { } function getCumulativeFiringAlertType() { - interface State { + interface State extends AlertTypeState { runCount?: number; } interface InstanceState extends AlertInstanceState { @@ -175,7 +177,7 @@ function getCumulativeFiringAlertType() { }; }, }; - return result as AlertType; + return result; } function getNeverFiringAlertType() { @@ -184,7 +186,7 @@ function getNeverFiringAlertType() { reference: schema.string(), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { globalStateValue: boolean; } const result: AlertType = { @@ -385,7 +387,7 @@ function getPatternFiringAlertType() { reference: schema.maybe(schema.string()), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { patternIndex?: number; } const result: AlertType = { 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 f6cbc52e7a421..cf09286fe1ba6 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 @@ -27,7 +27,14 @@ export const noopAlertType: AlertType = { producer: 'alerts', }; -export const alwaysFiringAlertType: AlertType = { +export const alwaysFiringAlertType: AlertType< + { instances: Array<{ id: string; state: any }> }, + { + globalStateValue: boolean; + groupInSeriesIndex: number; + }, + { instanceStateValue: boolean; globalStateValue: boolean; groupInSeriesIndex: number } +> = { id: 'test.always-firing', name: 'Always Firing', actionGroups: [ @@ -37,7 +44,7 @@ export const alwaysFiringAlertType: AlertType = { defaultActionGroupId: 'default', producer: 'alerts', minimumLicenseRequired: 'basic', - async executor(alertExecutorOptions: any) { + async executor(alertExecutorOptions) { const { services, state, params } = alertExecutorOptions; (params.instances || []).forEach((instance: { id: string; state: any }) => { From 93f10cb2280d23680fe7ba28aa366ce6592a0480 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 21 Dec 2020 11:51:09 +0000 Subject: [PATCH 04/13] removed dead code --- x-pack/plugins/alerts/server/mocks.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/x-pack/plugins/alerts/server/mocks.ts b/x-pack/plugins/alerts/server/mocks.ts index 679ae17c6fdd7..0f042b7a81d6c 100644 --- a/x-pack/plugins/alerts/server/mocks.ts +++ b/x-pack/plugins/alerts/server/mocks.ts @@ -76,15 +76,6 @@ const createAlertServicesMock = < }; }; export type AlertServicesMock = ReturnType; -// export type AlertServicesMock< -// InstanceState extends AlertInstanceState = AlertInstanceState, -// InstanceContext extends AlertInstanceContext = AlertInstanceContext -// > = { -// alertInstanceFactory: jest.Mock< -// jest.Mocked>, -// [string] -// >; -// } & Omit, 'alertInstanceFactory'>; export const alertsMock = { createAlertInstanceFactory: createAlertInstanceFactoryMock, From f4c543a68277abfa659fa263bac2d81f5b6ca2a4 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 21 Dec 2020 12:18:24 +0000 Subject: [PATCH 05/13] added TODOs --- .../inventory_metric_threshold_executor.ts | 3 +++ .../register_inventory_metric_threshold_alert_type.ts | 3 +++ .../metric_threshold/metric_threshold_executor.test.ts | 3 +++ .../register_metric_threshold_alert_type.ts | 6 ++++++ .../lib/detection_engine/signals/signal_params_schema.ts | 2 +- .../lib/detection_engine/signals/signal_rule_alert_type.ts | 6 +++++- 6 files changed, 21 insertions(+), 2 deletions(-) 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 b389666889dc7..54cf8658a3f0d 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 @@ -40,6 +40,9 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = services, params, }: AlertExecutorOptions< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ Record, Record, AlertInstanceState, 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 2fd2692747476..a2e8eff34ef98 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 @@ -43,6 +43,9 @@ const condition = schema.object({ export const registerMetricInventoryThresholdAlertType = ( libs: InfraBackendLibs ): AlertType< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ Record, Record, AlertInstanceState, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index cada17caf9656..6c9fac9d1133c 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -51,6 +51,9 @@ describe('The metric threshold alert type', () => { }, ], }, + /** + * TODO: Remove this use of `as` by utilizing a proper type + */ } as unknown) as MetricThresholdAlertExecutorOptions); test('alerts as expected with the > comparator', async () => { await execute(Comparator.GT, [0.75]); 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 e57ef26ba22db..000c89f5899ef 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 @@ -27,12 +27,18 @@ import { } from '../common/messages'; export type MetricThresholdAlertType = AlertType< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ Record, Record, AlertInstanceState, AlertInstanceContext >; export type MetricThresholdAlertExecutorOptions = AlertExecutorOptions< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ Record, Record, AlertInstanceState, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts index 50e740e81830f..aede91c5af143 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts @@ -8,7 +8,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { DEFAULT_MAX_SIGNALS } from '../../../../common/constants'; -const signalSchema = schema.object({ +export const signalSchema = schema.object({ anomalyThreshold: schema.maybe(schema.number()), author: schema.arrayOf(schema.string(), { defaultValue: [] }), buildingBlockType: schema.nullable(schema.string()), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 746720fdb5f8f..476b9aa56f572 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -88,7 +88,11 @@ export const signalRulesAlertType = ({ defaultActionGroupId: 'default', validate: { /** - * TODO: Add support for Schema + * TODO: Fix typing inconsistancy between `RuleTypeParams` and `CreateRulesOptions` + * Once that's done, you should be able to do: + * ``` + * params: signalParamsSchema(), + * ``` */ params: (signalParamsSchema() as unknown) as { validate: (object: unknown) => RuleTypeParams; From aa95e0e934d8be10fed0194deb881c02d96f2e2a Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 21 Dec 2020 12:23:47 +0000 Subject: [PATCH 06/13] mixed test --- .../public/application/lib/alert_api.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts index 09c9e5b42f022..ea654bb21e88b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts @@ -579,7 +579,7 @@ describe('createAlert', () => { Array [ "/api/alerts/alert", Object { - "body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerts\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"notifyWhen\\":\\"onActionGroupChange\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKey\\":null,\\"apiKeyOwner\\":null}", + "body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerts\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"notifyWhen\\":\\"onActionGroupChange\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKeyOwner\\":null,\\"createdBy\\":null,\\"updatedBy\\":null,\\"muteAll\\":false,\\"mutedInstanceIds\\":[]}", }, ] `); From dd833fcb82ccae7e1e66f1044c7cc62bccc715df Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 21 Dec 2020 12:38:41 +0000 Subject: [PATCH 07/13] corrected AlertTypeModel typing --- .../public/alert_types/threshold/expression.tsx | 12 ++++++++---- x-pack/plugins/triggers_actions_ui/public/types.ts | 7 +++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx index 3c84f2a5d4f9c..12e021958f497 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx @@ -71,6 +71,10 @@ interface KibanaDeps { http: HttpSetup; } +function isString(value: unknown): value is string { + return typeof value === 'string'; +} + export const IndexThresholdAlertTypeExpression: React.FunctionComponent< AlertTypeParamsExpressionProps > = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, charts, data }) => { @@ -190,10 +194,10 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< }; })} onChange={async (selected: EuiComboBoxOptionOption[]) => { - setAlertParams( - 'index', - selected.map((aSelected) => aSelected.value) - ); + const indicies: string[] = selected + .map((aSelected) => aSelected.value) + .filter(isString); + setAlertParams('index', indicies); const indices = selected.map((s) => s.value as string); // reset time field and expression fields if indices are deleted diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 705d8a9f0a593..e332263cbd9ce 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -180,8 +180,11 @@ export interface AlertTypeParamsExpressionProps< alertParams: Params; alertInterval: string; alertThrottle: string; - setAlertParams: (property: string, value: any) => void; - setAlertProperty: (key: Key, value: any) => void; + setAlertParams: (property: Key, value: Params[Key] | undefined) => void; + setAlertProperty: ( + key: Key, + value: SanitizedAlert[Key] | null + ) => void; errors: IErrorObject; defaultActionGroupId: string; actionGroups: ActionGroup[]; From 10711133551b0edb7e51ebf669bcf108cdb118ee Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 21 Dec 2020 12:45:45 +0000 Subject: [PATCH 08/13] renamed generics --- x-pack/plugins/triggers_actions_ui/public/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index e332263cbd9ce..43780b324aa56 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -181,9 +181,9 @@ export interface AlertTypeParamsExpressionProps< alertInterval: string; alertThrottle: string; setAlertParams: (property: Key, value: Params[Key] | undefined) => void; - setAlertProperty: ( - key: Key, - value: SanitizedAlert[Key] | null + setAlertProperty: ( + key: Prop, + value: SanitizedAlert[Prop] | null ) => void; errors: IErrorObject; defaultActionGroupId: string; From b79eb9672f7f75264837de478bdebdd29615f596 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Tue, 22 Dec 2020 19:57:29 +0000 Subject: [PATCH 09/13] typed action groups --- x-pack/plugins/alerts/common/alert_type.ts | 22 +- .../alerts/common/builtin_action_groups.ts | 13 +- .../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 | 92 ++++++-- .../server/task_runner/task_runner_factory.ts | 40 +++- x-pack/plugins/alerts/server/types.ts | 29 ++- 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 | 9 +- .../alert_types/geo_containment/alert_type.ts | 3 +- .../geo_containment/geo_containment.ts | 3 +- .../alert_types/geo_containment/index.ts | 4 +- .../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 +- 61 files changed, 896 insertions(+), 293 deletions(-) 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..ac1e83e4244d1 100644 --- a/x-pack/plugins/alerts/common/builtin_action_groups.ts +++ b/x-pack/plugins/alerts/common/builtin_action_groups.ts @@ -6,13 +6,18 @@ 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 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..719b2f0b17dc6 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, @@ -64,16 +64,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 +160,14 @@ export class TaskRunner< actions: Alert['actions'], alertParams: Params ) { - return createExecutionHandler({ + return createExecutionHandler< + Params, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + >({ alertId, alertName, tags, @@ -163,7 +186,7 @@ export class TaskRunner< async executeAlertInstance( alertInstanceId: string, alertInstance: AlertInstance, - executionHandler: ReturnType + executionHandler: ExecutionHandler ) { const { actionGroup, @@ -180,7 +203,7 @@ export class TaskRunner< services: Services, alert: SanitizedAlert, params: Params, - executionHandler: ReturnType, + executionHandler: ExecutionHandler, spaceId: string, event: Event ): Promise { @@ -218,9 +241,11 @@ export class TaskRunner< alertId, services: { ...services, - alertInstanceFactory: createAlertInstanceFactory( - alertInstances - ), + alertInstanceFactory: createAlertInstanceFactory< + InstanceState, + InstanceContext, + ActionGroupIds + >(alertInstances), }, params, state: alertTypeState as State, @@ -278,7 +303,7 @@ export class TaskRunner< if (!muteAll) { const mutedInstanceIdsSet = new Set(mutedInstanceIds); - scheduleActionsForRecoveredInstances({ + scheduleActionsForRecoveredInstances({ recoveryActionGroup: this.alertType.recoveryActionGroup, recoveredAlertInstances, executionHandler, @@ -615,20 +640,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 +695,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 027f875e2d08d..429493f3d3f17 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -55,21 +55,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; @@ -89,9 +93,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 { @@ -101,17 +106,19 @@ 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; 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 dccab5168fb60..5536ed7f46f10 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: Criteria) => { // 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 4703371f5e0de..79f765d368084 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 7141b61a23e6e..02fe598e7afb6 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 23ef9fcea8e53..5d0fb108c462f 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 960693bc703d6..7e6c3533fb1c4 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 18f6e8d127b1b..b3f5e74e18a37 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'; @@ -140,7 +145,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 85dcf1125becd..c89b58d14286e 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 @@ -141,7 +141,8 @@ export type GeoContainmentAlertType = AlertType< GeoContainmentParams, GeoContainmentState, GeoContainmentInstanceState, - GeoContainmentInstanceContext + GeoContainmentInstanceContext, + typeof ActionGroupId >; export function getAlertType(logger: Logger): GeoContainmentAlertType { 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..395afab6d51fd 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,7 @@ import { GeoContainmentInstanceState, GeoContainmentInstanceContext, getAlertType, + ActionGroupId, } from './alert_type'; interface RegisterParams { @@ -25,6 +26,7 @@ export function register(params: RegisterParams) { GeoContainmentParams, GeoContainmentState, GeoContainmentInstanceState, - GeoContainmentInstanceContext + GeoContainmentInstanceContext, + typeof ActionGroupId >(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 43780b324aa56..80419e280c636 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -146,9 +146,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' @@ -175,7 +177,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; @@ -187,7 +190,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 { From 020669065a409035321a7e0a915e0d6eeb322c5f Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 30 Dec 2020 10:20:54 +0000 Subject: [PATCH 10/13] fixed typing in test and example plugins --- .../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 ++- .../plugins/alerts/server/alert_types.ts | 32 +++++++++++-------- .../alerts_restricted/server/alert_types.ts | 10 +++--- .../fixtures/plugins/alerts/server/plugin.ts | 8 +++-- 7 files changed, 47 insertions(+), 31 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/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: [ From 582b60d39be4b356a9952612611c6b0b9a78ed44 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 30 Dec 2020 10:55:09 +0000 Subject: [PATCH 11/13] added typing to docs --- x-pack/plugins/alerts/README.md | 41 ++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) 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. From 0c8b26a9fd97f94510117116a402904da98a3d4f Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 30 Dec 2020 11:26:47 +0000 Subject: [PATCH 12/13] ensure reserved types arent in aciton groups in executor --- .../plugins/alerts/common/builtin_action_groups.ts | 6 +++++- x-pack/plugins/alerts/server/types.ts | 13 ++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/alerts/common/builtin_action_groups.ts b/x-pack/plugins/alerts/common/builtin_action_groups.ts index ac1e83e4244d1..552250d6e71ce 100644 --- a/x-pack/plugins/alerts/common/builtin_action_groups.ts +++ b/x-pack/plugins/alerts/common/builtin_action_groups.ts @@ -16,8 +16,12 @@ export const RecoveredActionGroup: Readonly> = Object.f }), }); +export type ReservedActionGroups = + | RecoveryActionGroupId + | RecoveredActionGroupId; + export function getBuiltinActionGroups( customRecoveryGroup?: ActionGroup -): [ActionGroup] { +): [ActionGroup>] { return [customRecoveryGroup ?? RecoveredActionGroup]; } diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 429493f3d3f17..f56c0749bd7bb 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, + ReservedActionGroups, } from '../common'; import { LicenseType } from '../../licensing/server'; @@ -118,7 +119,17 @@ export interface AlertType< actionGroups: Array>; defaultActionGroupId: ActionGroup['id']; recoveryActionGroup?: ActionGroup; - executor: ExecutorType; + executor: ExecutorType< + Params, + State, + InstanceState, + InstanceContext, + /** + * Ensure that the reserved ActionGroups (such as `Recovered`) are not + * available for scheduling in the Executor + */ + ActionGroupIds extends ReservedActionGroups ? never : ActionGroupIds + >; producer: string; actionVariables?: { context?: ActionVariable[]; From a9f5f503a1d349702297aff8d86db438be8b6599 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Wed, 30 Dec 2020 13:31:56 +0000 Subject: [PATCH 13/13] fixed task runner typing --- x-pack/plugins/alerts/common/builtin_action_groups.ts | 5 +++++ x-pack/plugins/alerts/server/task_runner/task_runner.ts | 3 ++- x-pack/plugins/alerts/server/types.ts | 4 ++-- .../server/lib/detection_engine/signals/utils.ts | 5 ++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/alerts/common/builtin_action_groups.ts b/x-pack/plugins/alerts/common/builtin_action_groups.ts index 552250d6e71ce..f2b7ec855b86e 100644 --- a/x-pack/plugins/alerts/common/builtin_action_groups.ts +++ b/x-pack/plugins/alerts/common/builtin_action_groups.ts @@ -20,6 +20,11 @@ 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>] { 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 719b2f0b17dc6..12f7c33ae5052 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -44,6 +44,7 @@ import { AlertTypeState, AlertInstanceState, AlertInstanceContext, + WithoutReservedActionGroups, } from '../../common'; import { NormalizedAlertType } from '../alert_type_registry'; @@ -244,7 +245,7 @@ export class TaskRunner< alertInstanceFactory: createAlertInstanceFactory< InstanceState, InstanceContext, - ActionGroupIds + WithoutReservedActionGroups >(alertInstances), }, params, diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index de12df40010a8..39c52d9653aaa 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -29,7 +29,7 @@ import { AlertExecutionStatusErrorReasons, AlertsHealth, AlertNotifyWhenType, - ReservedActionGroups, + WithoutReservedActionGroups, } from '../common'; import { LicenseType } from '../../licensing/server'; @@ -131,7 +131,7 @@ export interface AlertType< * Ensure that the reserved ActionGroups (such as `Recovered`) are not * available for scheduling in the Executor */ - ActionGroupIds extends ReservedActionGroups ? never : ActionGroupIds + WithoutReservedActionGroups >; producer: string; actionVariables?: { 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 5aad44fadca8f..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 @@ -57,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',