From 646a41fdc2f8d54420fda961ba1b088ab64d836b Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 8 Jun 2021 14:16:44 -0400 Subject: [PATCH 01/20] Adding function hooks into rule type definition and call extract fn on rule create --- .../server/alerts_client/alerts_client.ts | 15 +++++++++++--- x-pack/plugins/alerting/server/types.ts | 20 ++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index c81fa7927ef7d..8fe466491ae8f 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -282,11 +282,20 @@ export class AlertsClient { throw Boom.badRequest(`Error creating rule: could not create API key - ${error.message}`); } + // Validate actions and create a saved object reference for each action await this.validateActions(alertType, data.actions); + const { references: actionReferences, actions } = await this.denormalizeActions(data.actions); - const createTime = Date.now(); - const { references, actions } = await this.denormalizeActions(data.actions); + // Extracts any references using configured reference extractor if available + const extractedRefsAndParams = alertType?.references?.extractReferences + ? alertType.references.extractReferences(validatedAlertTypeParams) + : null; + const extractedReferences = extractedRefsAndParams?.references ?? []; + const ruleParams = extractedRefsAndParams?.params ?? validatedAlertTypeParams; + + const references = [...actionReferences, ...extractedReferences]; + const createTime = Date.now(); const notifyWhen = getAlertNotifyWhenType(data.notifyWhen, data.throttle); const rawAlert: RawAlert = { @@ -297,7 +306,7 @@ export class AlertsClient { updatedBy: username, createdAt: new Date(createTime).toISOString(), updatedAt: new Date(createTime).toISOString(), - params: validatedAlertTypeParams as RawAlert['params'], + params: ruleParams as RawAlert['params'], muteAll: false, mutedInstanceIds: [], notifyWhen, diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index f8846035e6b02..898028549b1e0 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { IRouter, RequestHandlerContext } from 'src/core/server'; +import type { IRouter, RequestHandlerContext, SavedObjectReference } from 'src/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { PublicAlertInstance } from './alert_instance'; import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry'; @@ -99,6 +99,20 @@ export interface AlertExecutorOptions< updatedBy: string | null; } +export interface ExtractedReferencesAndParams { + references: SavedObjectReference[]; + params: Params; +} + +export type ExtractReferencesType = ( + params: Params +) => ExtractedReferencesAndParams; + +export type InjectReferencesType = ( + params: Params, + references: SavedObjectReference[] +) => Params; + export type ExecutorType< Params extends AlertTypeParams = never, State extends AlertTypeState = never, @@ -125,6 +139,10 @@ export interface AlertType< validate?: { params?: AlertTypeParamsValidator; }; + references?: { + extractReferences: ExtractReferencesType; + injectReferences: InjectReferencesType; + }; actionGroups: Array>; defaultActionGroupId: ActionGroup['id']; recoveryActionGroup?: ActionGroup; From 9cb4e3b40db4799efed5f69a64f2d1fa2c8314ea Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Wed, 9 Jun 2021 15:46:47 -0400 Subject: [PATCH 02/20] Adding hooks for extracting and injecting saved object references. Adding extractReferences to create and update workflow --- .../server/alerts_client/alerts_client.ts | 92 ++++++-- .../server/alerts_client/tests/create.test.ts | 149 +++++++++++++ .../server/alerts_client/tests/update.test.ts | 198 ++++++++++++++++++ x-pack/plugins/alerting/server/types.ts | 19 +- 4 files changed, 430 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index 8fe466491ae8f..2382c2397d902 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -183,6 +183,8 @@ export interface GetAlertInstanceSummaryParams { dateStart?: string; } +const reservedSavedObjectReferenceNamePrefix = 'action_'; + const alertingAuthorizationFilterOpts: AlertingAuthorizationFilterOpts = { type: AlertingAuthorizationFilterType.KQL, fieldNames: { ruleTypeId: 'alert.attributes.alertTypeId', consumer: 'alert.attributes.consumer' }, @@ -282,18 +284,15 @@ export class AlertsClient { throw Boom.badRequest(`Error creating rule: could not create API key - ${error.message}`); } - // Validate actions and create a saved object reference for each action + // Validate actions await this.validateActions(alertType, data.actions); - const { references: actionReferences, actions } = await this.denormalizeActions(data.actions); - - // Extracts any references using configured reference extractor if available - const extractedRefsAndParams = alertType?.references?.extractReferences - ? alertType.references.extractReferences(validatedAlertTypeParams) - : null; - const extractedReferences = extractedRefsAndParams?.references ?? []; - const ruleParams = extractedRefsAndParams?.params ?? validatedAlertTypeParams; - const references = [...actionReferences, ...extractedReferences]; + // Extract saved object references for this rule + const { references, params: updatedParams, actions } = await this.extractReferences( + alertType, + data.actions, + validatedAlertTypeParams + ); const createTime = Date.now(); const notifyWhen = getAlertNotifyWhenType(data.notifyWhen, data.throttle); @@ -306,7 +305,7 @@ export class AlertsClient { updatedBy: username, createdAt: new Date(createTime).toISOString(), updatedAt: new Date(createTime).toISOString(), - params: ruleParams as RawAlert['params'], + params: updatedParams as RawAlert['params'], muteAll: false, mutedInstanceIds: [], notifyWhen, @@ -769,7 +768,13 @@ export class AlertsClient { ); await this.validateActions(alertType, data.actions); - const { actions, references } = await this.denormalizeActions(data.actions); + // Extract saved object references for this rule + const { references, params: updatedParams, actions } = await this.extractReferences( + alertType, + data.actions, + validatedAlertTypeParams + ); + const username = await this.getUserName(); let createdAPIKey = null; @@ -789,7 +794,7 @@ export class AlertsClient { ...attributes, ...data, ...apiKeyAttributes, - params: validatedAlertTypeParams as RawAlert['params'], + params: updatedParams as RawAlert['params'], actions, notifyWhen, updatedBy: username, @@ -1534,6 +1539,65 @@ export class AlertsClient { } } + private async extractReferences( + ruleType: UntypedNormalizedAlertType, + ruleActions: NormalizedAlertAction[], + ruleParams: Params + ): Promise<{ + actions: RawAlert['actions']; + params: Params; + references: SavedObjectReference[]; + }> { + const { references: actionReferences, actions } = await this.denormalizeActions(ruleActions); + + // Extracts any references using configured reference extractor if available + const extractedRefsAndParams = ruleType?.useSavedObjectReferences?.extractReferences + ? ruleType.useSavedObjectReferences.extractReferences(ruleParams) + : null; + const extractedReferences = extractedRefsAndParams?.references ?? []; + const params = (extractedRefsAndParams?.params as Params) ?? ruleParams; + + // Validate that extract references don't use prefix reserved for actions + const referencesUsingReservedPrefix = extractedReferences.filter( + (reference: SavedObjectReference) => + reference.name.startsWith(reservedSavedObjectReferenceNamePrefix) + ); + + if (referencesUsingReservedPrefix.length > 0) { + throw Boom.badRequest( + `Error creating rule: extracted saved object reference names are cannot start with ${reservedSavedObjectReferenceNamePrefix}` + ); + } + + const references = [...actionReferences, ...extractedReferences]; + return { + actions, + params, + references, + }; + } + + private injectReferences( + ruleId: string, + ruleType: UntypedNormalizedAlertType, + ruleActions: RawAlert['actions'], + ruleParams: Params, + references: SavedObjectReference[] + ): { + actions: Alert['actions']; + params: Params; + } { + const actions = this.injectReferencesIntoActions(ruleId, ruleActions, references); + const params = ruleType?.useSavedObjectReferences?.injectReferences + ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) + : ruleParams; + + return { + actions, + params, + }; + } + private async denormalizeActions( alertActions: NormalizedAlertAction[] ): Promise<{ actions: RawAlert['actions']; references: SavedObjectReference[] }> { @@ -1551,7 +1615,7 @@ export class AlertsClient { alertActions.forEach(({ id, ...alertAction }, i) => { const actionResultValue = actionResults.find((action) => action.id === id); if (actionResultValue) { - const actionRef = `action_${i}`; + const actionRef = `${reservedSavedObjectReferenceNamePrefix}${i}`; references.push({ id, name: actionRef, diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts index a2d5a5e0386c4..8587316500802 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts @@ -801,6 +801,111 @@ describe('create()', () => { expect(taskManager.schedule).toHaveBeenCalledTimes(0); }); + test('should call useSavedObjectReferences.extractReferences and useSavedObjectReferences.injectReferences if defined for rule type', async () => { + const ruleParams = { + bar: true, + parameterThatIsSavedObjectId: '9', + }; + const extractReferencesFn = jest.fn().mockReturnValue({ + params: { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + references: [ + { + name: 'soRef_0', + type: 'someSavedObjecType', + id: '9', + }, + ], + }); + const injectReferencesFn = jest.fn(); + alertTypeRegistry.get.mockImplementation(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: extractReferencesFn, + injectReferences: injectReferencesFn, + }, + })); + const data = getMockData({ + params: ruleParams, + }); + taskManager.schedule.mockResolvedValueOnce({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + scheduledTaskId: 'task-123', + }, + references: [], + }); + const result = await alertsClient.create({ data }); + + expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'alert', + { + actions: [ + { actionRef: 'action_0', actionTypeId: 'test', group: 'default', params: { foo: true } }, + ], + alertTypeId: '123', + apiKey: null, + apiKeyOwner: null, + consumer: 'bar', + createdAt: '2019-02-12T21:01:22.479Z', + createdBy: 'elastic', + enabled: true, + executionStatus: { + error: null, + lastExecutionDate: '2019-02-12T21:01:22.479Z', + status: 'pending', + }, + meta: { versionApiKeyLastmodified: 'v7.10.0' }, + muteAll: false, + mutedInstanceIds: [], + name: 'abc', + notifyWhen: 'onActiveAlert', + params: { bar: true, parameterThatIsSavedObjectRef: 'soRef_0' }, + schedule: { interval: '10s' }, + tags: ['foo'], + throttle: null, + updatedAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + }, + { + id: 'mock-saved-object-id', + references: [ + { id: '1', name: 'action_0', type: 'action' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjecType' }, + ], + } + ); + + // TODO + // expect(injectReferencesFn).toHaveBeenCalledWith(); + // expect(result).toEqual(); + }); + test('should trim alert name when creating API key', async () => { const data = getMockData({ name: ' my alert name ' }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ @@ -1301,6 +1406,50 @@ describe('create()', () => { ); }); + test('throws error if extracted references uses reserved name prefix', async () => { + const ruleParams = { + bar: true, + parameterThatIsSavedObjectId: '9', + }; + const extractReferencesFn = jest.fn().mockReturnValue({ + params: { + bar: true, + parameterThatIsSavedObjectRef: 'action_0', + }, + references: [ + { + name: 'action_0', + type: 'someSavedObjecType', + id: '9', + }, + ], + }); + alertTypeRegistry.get.mockImplementation(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: extractReferencesFn, + injectReferences: jest.fn(), + }, + })); + const data = getMockData({ + params: ruleParams, + }); + await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error creating rule: extracted saved object reference names are cannot start with action_"` + ); + + expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); + test('throws error if loading actions fails', async () => { const data = getMockData(); // Reset from default behaviour diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts index c743312ef2c4b..469c704bb1c79 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts @@ -24,6 +24,12 @@ import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { auditServiceMock } from '../../../../security/server/audit/index.mock'; import { getBeforeSetup, setGlobalDate } from './lib'; +jest.mock('../../../../../../src/core/server/saved_objects/service/lib/utils', () => ({ + SavedObjectsUtils: { + generateId: () => 'mock-saved-object-id', + }, +})); + const taskManager = taskManagerMock.createStart(); const alertTypeRegistry = alertTypeRegistryMock.create(); const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); @@ -400,6 +406,137 @@ describe('update()', () => { expect(actionsClient.isActionTypeEnabled).toHaveBeenCalledWith('test2', { notifyUsage: true }); }); + test('should call useSavedObjectReferences.extractReferences and useSavedObjectReferences.injectReferences if defined for rule type', async () => { + const ruleParams = { + bar: true, + parameterThatIsSavedObjectId: '9', + }; + const extractReferencesFn = jest.fn().mockReturnValue({ + params: { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + references: [ + { + name: 'soRef_0', + type: 'someSavedObjecType', + id: '9', + }, + ], + }); + const injectReferencesFn = jest.fn(); + alertTypeRegistry.get.mockImplementation(() => ({ + id: 'myType', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: extractReferencesFn, + injectReferences: injectReferencesFn, + }, + })); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: true, + schedule: { interval: '10s' }, + params: { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + notifyWhen: 'onActiveAlert', + scheduledTaskId: 'task-123', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'soRef_0', + type: 'someSavedObjecType', + id: '9', + }, + ], + }); + const result = await alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: ruleParams, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }); + + expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'alert', + { + actions: [ + { actionRef: 'action_0', actionTypeId: 'test', group: 'default', params: { foo: true } }, + ], + alertTypeId: 'myType', + apiKey: null, + apiKeyOwner: null, + consumer: 'myApp', + enabled: true, + meta: { versionApiKeyLastmodified: 'v7.10.0' }, + name: 'abc', + notifyWhen: 'onActiveAlert', + params: { bar: true, parameterThatIsSavedObjectRef: 'soRef_0' }, + schedule: { interval: '10s' }, + scheduledTaskId: 'task-123', + tags: ['foo'], + throttle: null, + updatedAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + }, + { + id: '1', + overwrite: true, + references: [ + { id: '1', name: 'action_0', type: 'action' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjecType' }, + ], + version: '123', + } + ); + + // TODO + // expect(injectReferencesFn).toHaveBeenCalledWith(); + // expect(result).toEqual(); + }); + it('calls the createApiKey function', async () => { alertsClientParams.createAPIKey.mockResolvedValueOnce({ apiKeysEnabled: true, @@ -1082,6 +1219,67 @@ describe('update()', () => { ).toBe('234'); }); + test('throws error if extracted references uses reserved name prefix', async () => { + const ruleParams = { + bar: true, + parameterThatIsSavedObjectId: '9', + }; + const extractReferencesFn = jest.fn().mockReturnValue({ + params: { + bar: true, + parameterThatIsSavedObjectRef: 'action_0', + }, + references: [ + { + name: 'action_0', + type: 'someSavedObjecType', + id: '9', + }, + ], + }); + alertTypeRegistry.get.mockImplementation(() => ({ + id: 'myType', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: extractReferencesFn, + injectReferences: jest.fn(), + }, + })); + await expect( + alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: ruleParams, + throttle: null, + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error creating rule: extracted saved object reference names are cannot start with action_"` + ); + + expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + }); + describe('updating an alert schedule', () => { function mockApiCalls( alertId: string, diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 898028549b1e0..0e4bd6e100815 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -99,20 +99,11 @@ export interface AlertExecutorOptions< updatedBy: string | null; } -export interface ExtractedReferencesAndParams { +export interface RuleParamsAndRefs { references: SavedObjectReference[]; params: Params; } -export type ExtractReferencesType = ( - params: Params -) => ExtractedReferencesAndParams; - -export type InjectReferencesType = ( - params: Params, - references: SavedObjectReference[] -) => Params; - export type ExecutorType< Params extends AlertTypeParams = never, State extends AlertTypeState = never, @@ -139,10 +130,6 @@ export interface AlertType< validate?: { params?: AlertTypeParamsValidator; }; - references?: { - extractReferences: ExtractReferencesType; - injectReferences: InjectReferencesType; - }; actionGroups: Array>; defaultActionGroupId: ActionGroup['id']; recoveryActionGroup?: ActionGroup; @@ -164,6 +151,10 @@ export interface AlertType< params?: ActionVariable[]; }; minimumLicenseRequired: LicenseType; + useSavedObjectReferences?: { + extractReferences: (params: Params) => RuleParamsAndRefs; + injectReferences: (params: Params, references: SavedObjectReference[]) => Params; + }; } export type UntypedAlertType = AlertType< From e1663439a18d1327183fbfebb204c8dbd40fc571 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 10 Jun 2021 09:25:53 -0400 Subject: [PATCH 03/20] Adding type template for extracted params --- .../server/alert_types/always_firing.ts | 1 + .../server/alert_types/astros.ts | 1 + x-pack/plugins/alerting/README.md | 4 ++ .../server/alert_type_registry.test.ts | 17 +++---- .../alerting/server/alert_type_registry.ts | 22 ++++++++- x-pack/plugins/alerting/server/index.ts | 1 + .../alerting/server/lib/license_state.test.ts | 4 +- .../alerting/server/lib/license_state.ts | 2 + x-pack/plugins/alerting/server/plugin.test.ts | 2 +- x-pack/plugins/alerting/server/plugin.ts | 4 ++ .../create_execution_handler.test.ts | 2 + .../task_runner/create_execution_handler.ts | 4 ++ .../server/task_runner/task_runner.ts | 4 ++ .../server/task_runner/task_runner_factory.ts | 2 + x-pack/plugins/alerting/server/types.ts | 5 +- ...r_inventory_metric_threshold_alert_type.ts | 1 + .../register_metric_anomaly_alert_type.ts | 1 + .../register_metric_threshold_alert_type.ts | 1 + .../monitoring/server/alerts/base_alert.ts | 2 +- x-pack/plugins/rule_registry/server/types.ts | 20 +++++++- .../detection_engine/notifications/types.ts | 17 ++++++- .../server/alert_types/es_query/alert_type.ts | 9 +++- .../alert_types/geo_containment/alert_type.ts | 1 + .../alert_types/index_threshold/alert_type.ts | 48 +++++++++++++++++-- .../index_threshold/alert_type_params.ts | 1 + x-pack/plugins/stack_alerts/server/types.ts | 1 + .../server/lib/alerts/status_check.test.ts | 1 + .../plugins/uptime/server/lib/alerts/types.ts | 1 + .../plugins/alerts/server/alert_types.ts | 25 +++++----- .../alerts_restricted/server/alert_types.ts | 4 +- .../fixtures/plugins/alerts/server/plugin.ts | 5 +- 31 files changed, 173 insertions(+), 40 deletions(-) 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 6e9ec0d367c9a..01f7de3514197 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 @@ -39,6 +39,7 @@ function getTShirtSizeByIdAndThreshold( export const alertType: AlertType< AlwaysFiringParams, + never, { count?: number }, { triggerdOnCycle: number }, never, 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 45ea6b48bf6f4..f157e9353eefd 100644 --- a/x-pack/examples/alerting_example/server/alert_types/astros.ts +++ b/x-pack/examples/alerting_example/server/alert_types/astros.ts @@ -41,6 +41,7 @@ function getCraftFilter(craft: string) { export const alertType: AlertType< { outerSpaceCapacity: number; craft: string; op: string }, + never, { peopleInSpace: number }, { craft: string }, never, diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index cb43e53408090..74dc3180e8426 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -228,6 +228,7 @@ type MyRuleTypeActionGroups = 'default' | 'warning'; const myRuleType: AlertType< MyRuleTypeParams, + MyRuleTypeExtractedParams, MyRuleTypeState, MyRuleTypeAlertState, MyRuleTypeAlertContext, @@ -318,6 +319,9 @@ const myRuleType: AlertType< }; }, producer: 'alerting', + useSavedObjectReferences?: { + + } }; server.newPlatform.setup.plugins.alerting.registerType(myRuleType); diff --git a/x-pack/plugins/alerting/server/alert_type_registry.test.ts b/x-pack/plugins/alerting/server/alert_type_registry.test.ts index 7f34760c73199..ab494a2069287 100644 --- a/x-pack/plugins/alerting/server/alert_type_registry.test.ts +++ b/x-pack/plugins/alerting/server/alert_type_registry.test.ts @@ -56,7 +56,7 @@ describe('has()', () => { describe('register()', () => { test('throws if AlertType Id contains invalid characters', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -88,7 +88,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: [ @@ -110,7 +110,7 @@ describe('register()', () => { }); test('throws if AlertType action groups contains reserved group id', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -142,7 +142,7 @@ describe('register()', () => { }); test('allows an AlertType to specify a custom recovery group', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -182,6 +182,7 @@ describe('register()', () => { never, never, never, + never, 'default' | 'backToAwesome', 'backToAwesome' > = { @@ -216,7 +217,7 @@ describe('register()', () => { }); test('registers the executor with the task manager', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -246,7 +247,7 @@ describe('register()', () => { }); test('shallow clones the given alert type', () => { - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -491,8 +492,8 @@ function alertTypeWithVariables( id: ActionGroupIds, context: string, state: string -): AlertType { - const baseAlert: AlertType = { +): AlertType { + const baseAlert: AlertType = { id, name: `${id}-name`, actionGroups: [], diff --git a/x-pack/plugins/alerting/server/alert_type_registry.ts b/x-pack/plugins/alerting/server/alert_type_registry.ts index 21feb76926791..f499700b09d84 100644 --- a/x-pack/plugins/alerting/server/alert_type_registry.ts +++ b/x-pack/plugins/alerting/server/alert_type_registry.ts @@ -73,6 +73,7 @@ const alertIdSchema = schema.string({ export type NormalizedAlertType< Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -81,13 +82,22 @@ export type NormalizedAlertType< > = { actionGroups: Array>; } & Omit< - AlertType, + AlertType< + Params, + ExtractedParams, + State, + InstanceState, + InstanceContext, + ActionGroupIds, + RecoveryActionGroupId + >, 'recoveryActionGroup' | 'actionGroups' > & Pick< Required< AlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -99,6 +109,7 @@ export type NormalizedAlertType< >; export type UntypedNormalizedAlertType = NormalizedAlertType< + AlertTypeParams, AlertTypeParams, AlertTypeState, AlertInstanceState, @@ -131,6 +142,7 @@ export class AlertTypeRegistry { public register< Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -139,6 +151,7 @@ export class AlertTypeRegistry { >( alertType: AlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -160,6 +173,7 @@ export class AlertTypeRegistry { const normalizedAlertType = augmentActionGroupsWithReserved< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -197,6 +211,7 @@ export class AlertTypeRegistry { public get< Params extends AlertTypeParams = AlertTypeParams, + ExtractedParams extends AlertTypeParams = AlertTypeParams, State extends AlertTypeState = AlertTypeState, InstanceState extends AlertInstanceState = AlertInstanceState, InstanceContext extends AlertInstanceContext = AlertInstanceContext, @@ -206,6 +221,7 @@ export class AlertTypeRegistry { id: string ): NormalizedAlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -229,6 +245,7 @@ export class AlertTypeRegistry { */ return (this.alertTypes.get(id)! as unknown) as NormalizedAlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -281,6 +298,7 @@ function normalizedActionVariables(actionVariables: AlertType['actionVariables'] function augmentActionGroupsWithReserved< Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -289,6 +307,7 @@ function augmentActionGroupsWithReserved< >( alertType: AlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -297,6 +316,7 @@ function augmentActionGroupsWithReserved< > ): NormalizedAlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index 72e3325107f31..4c759434ef1ed 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -28,6 +28,7 @@ export type { AlertInstanceState, AlertInstanceContext, AlertingApiRequestHandlerContext, + RuleParamsAndRefs, } from './types'; export { PluginSetupContract, PluginStartContract } from './plugin'; export { FindResult } from './alerts_client'; diff --git a/x-pack/plugins/alerting/server/lib/license_state.test.ts b/x-pack/plugins/alerting/server/lib/license_state.test.ts index a1c326656f735..b4e9b26549481 100644 --- a/x-pack/plugins/alerting/server/lib/license_state.test.ts +++ b/x-pack/plugins/alerting/server/lib/license_state.test.ts @@ -57,7 +57,7 @@ describe('getLicenseCheckForAlertType', () => { let license: Subject; let licenseState: ILicenseState; const mockNotifyUsage = jest.fn(); - const alertType: AlertType = { + const alertType: AlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -191,7 +191,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/alerting/server/lib/license_state.ts b/x-pack/plugins/alerting/server/lib/license_state.ts index dc5d9d278b6b5..837fecde11659 100644 --- a/x-pack/plugins/alerting/server/lib/license_state.ts +++ b/x-pack/plugins/alerting/server/lib/license_state.ts @@ -140,6 +140,7 @@ export class LicenseState { public ensureLicenseForAlertType< Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -148,6 +149,7 @@ export class LicenseState { >( alertType: AlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, diff --git a/x-pack/plugins/alerting/server/plugin.test.ts b/x-pack/plugins/alerting/server/plugin.test.ts index 4e9249944a6bf..0894558ee5668 100644 --- a/x-pack/plugins/alerting/server/plugin.test.ts +++ b/x-pack/plugins/alerting/server/plugin.test.ts @@ -129,7 +129,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/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 769243b8feaf6..1ba12f69285e0 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -85,6 +85,7 @@ export const LEGACY_EVENT_LOG_ACTIONS = { export interface PluginSetupContract { registerType< Params extends AlertTypeParams = AlertTypeParams, + ExtractedParams extends AlertTypeParams = AlertTypeParams, State extends AlertTypeState = AlertTypeState, InstanceState extends AlertInstanceState = AlertInstanceState, InstanceContext extends AlertInstanceContext = AlertInstanceContext, @@ -93,6 +94,7 @@ export interface PluginSetupContract { >( alertType: AlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -264,6 +266,7 @@ export class AlertingPlugin { return { registerType< Params extends AlertTypeParams = AlertTypeParams, + ExtractedParams extends AlertTypeParams = AlertTypeParams, State extends AlertTypeState = AlertTypeState, InstanceState extends AlertInstanceState = AlertInstanceState, InstanceContext extends AlertInstanceContext = AlertInstanceContext, @@ -272,6 +275,7 @@ export class AlertingPlugin { >( alertType: AlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index 5ab25fbfa39e7..adad5ec9ab5ca 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -29,6 +29,7 @@ jest.mock('./inject_action_params', () => ({ })); const alertType: NormalizedAlertType< + AlertTypeParams, AlertTypeParams, AlertTypeState, AlertInstanceState, @@ -58,6 +59,7 @@ const mockActionsPlugin = actionsMock.createStart(); const mockEventLogger = eventLoggerMock.create(); const createExecutionHandlerParams: jest.Mocked< CreateExecutionHandlerOptions< + AlertTypeParams, AlertTypeParams, AlertTypeState, AlertInstanceState, diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index ef93179bdaba1..c51ba5fd38e5b 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -26,6 +26,7 @@ import { NormalizedAlertType } from '../alert_type_registry'; export interface CreateExecutionHandlerOptions< Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -42,6 +43,7 @@ export interface CreateExecutionHandlerOptions< kibanaBaseUrl: string | undefined; alertType: NormalizedAlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -68,6 +70,7 @@ export type ExecutionHandler = ( export function createExecutionHandler< Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -89,6 +92,7 @@ export function createExecutionHandler< alertParams, }: CreateExecutionHandlerOptions< Params, + ExtractedParams, State, InstanceState, InstanceContext, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index fd82b38b493d7..8afc5fbe0bdbb 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -67,6 +67,7 @@ interface AlertTaskInstance extends ConcreteTaskInstance { export class TaskRunner< Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -78,6 +79,7 @@ export class TaskRunner< private taskInstance: AlertTaskInstance; private alertType: NormalizedAlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -89,6 +91,7 @@ export class TaskRunner< constructor( alertType: NormalizedAlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, @@ -168,6 +171,7 @@ export class TaskRunner< ) { return createExecutionHandler< Params, + ExtractedParams, State, InstanceState, InstanceContext, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index a023776134e9c..adfd4c9107c33 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -57,6 +57,7 @@ export class TaskRunnerFactory { public create< Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams, State extends AlertTypeState, InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -65,6 +66,7 @@ export class TaskRunnerFactory { >( alertType: NormalizedAlertType< Params, + ExtractedParams, State, InstanceState, InstanceContext, diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 0e4bd6e100815..b73a78b089c9d 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -119,6 +119,7 @@ export interface AlertTypeParamsValidator { } export interface AlertType< Params extends AlertTypeParams = never, + ExtractedParams extends AlertTypeParams = never, State extends AlertTypeState = never, InstanceState extends AlertInstanceState = never, InstanceContext extends AlertInstanceContext = never, @@ -152,8 +153,8 @@ export interface AlertType< }; minimumLicenseRequired: LicenseType; useSavedObjectReferences?: { - extractReferences: (params: Params) => RuleParamsAndRefs; - injectReferences: (params: Params, references: SavedObjectReference[]) => Params; + extractReferences: (params: Params) => RuleParamsAndRefs; + injectReferences: (params: ExtractedParams, references: SavedObjectReference[]) => Params; }; } 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 81204c7b71a68..912ea716f9e64 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 @@ -62,6 +62,7 @@ export const registerMetricInventoryThresholdAlertType = ( * TODO: Remove this use of `any` by utilizing a proper type */ Record, + never, // Only use if defining useSavedObjectReferences hook Record, AlertInstanceState, AlertInstanceContext, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_anomaly/register_metric_anomaly_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_anomaly/register_metric_anomaly_alert_type.ts index 2adf37c60cf3a..b15bbcce5a487 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_anomaly/register_metric_anomaly_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_anomaly/register_metric_anomaly_alert_type.ts @@ -34,6 +34,7 @@ export const registerMetricAnomalyAlertType = ( * TODO: Remove this use of `any` by utilizing a proper type */ Record, + never, // Only use if defining useSavedObjectReferences hook Record, AlertInstanceState, AlertInstanceContext, 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 7dbae03f20fbd..8b47620468eaf 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 @@ -38,6 +38,7 @@ export type MetricThresholdAlertType = AlertType< * TODO: Remove this use of `any` by utilizing a proper type */ Record, + never, // Only use if defining useSavedObjectReferences hook Record, AlertInstanceState, AlertInstanceContext, diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index bb80d84210a48..6b830e8dc3f6d 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -81,7 +81,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, diff --git a/x-pack/plugins/rule_registry/server/types.ts b/x-pack/plugins/rule_registry/server/types.ts index 959c05fd1334e..8d6ab51bb3601 100644 --- a/x-pack/plugins/rule_registry/server/types.ts +++ b/x-pack/plugins/rule_registry/server/types.ts @@ -16,7 +16,15 @@ import { AlertType } from '../../alerting/server'; type SimpleAlertType< TParams extends AlertTypeParams = {}, TAlertInstanceContext extends AlertInstanceContext = {} -> = AlertType; +> = AlertType< + TParams, + TParams, + AlertTypeState, + AlertInstanceState, + TAlertInstanceContext, + string, + string +>; export type AlertTypeExecutor< TParams extends AlertTypeParams = {}, @@ -33,7 +41,15 @@ export type AlertTypeWithExecutor< TAlertInstanceContext extends AlertInstanceContext = {}, TServices extends Record = {} > = Omit< - AlertType, + AlertType< + TParams, + TParams, + AlertTypeState, + AlertInstanceState, + TAlertInstanceContext, + string, + string + >, 'executor' > & { executor: AlertTypeExecutor; 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 0a32e0c21a075..50ad98865544e 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 @@ -101,12 +101,25 @@ export type NotificationExecutorOptions = AlertExecutorOptions< // since we are only increasing the strictness of params. export const isNotificationAlertExecutor = ( obj: NotificationAlertTypeDefinition -): obj is AlertType => { +): obj is AlertType< + AlertTypeParams, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +> => { return true; }; export type NotificationAlertTypeDefinition = Omit< - AlertType, + AlertType< + AlertTypeParams, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + 'default' + >, 'executor' > & { executor: ({ diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts index b81bc19d5c731..af49ecb3d9bf4 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.ts @@ -27,7 +27,14 @@ export const ConditionMetAlertInstanceId = 'query matched'; export function getAlertType( logger: Logger -): AlertType { +): AlertType< + EsQueryAlertParams, + never, // Only use if defining useSavedObjectReferences hook + EsQueryAlertState, + {}, + ActionContext, + typeof ActionGroupId +> { const alertTypeName = i18n.translate('xpack.stackAlerts.esQuery.alertTypeTitle', { defaultMessage: 'Elasticsearch query', }); 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 e3d379f2869b9..23a22461c09d6 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 @@ -140,6 +140,7 @@ export interface GeoContainmentInstanceContext extends AlertInstanceContext { export type GeoContainmentAlertType = AlertType< GeoContainmentParams, + never, // Only use if defining useSavedObjectReferences hook GeoContainmentState, GeoContainmentInstanceState, GeoContainmentInstanceContext, 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 a242c1e0eb29e..68de5e0a58f95 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 @@ -6,9 +6,17 @@ */ import { i18n } from '@kbn/i18n'; -import { Logger } from 'src/core/server'; -import { AlertType, AlertExecutorOptions, StackAlertsStartDeps } from '../../types'; -import { Params, ParamsSchema } from './alert_type_params'; +import { + Logger, + // SavedObjectReference +} from 'src/core/server'; +import { + AlertType, + AlertExecutorOptions, + StackAlertsStartDeps, + // RuleParamsAndRefs, +} from '../../types'; +import { Params, ExtractedParams, ParamsSchema } from './alert_type_params'; import { ActionContext, BaseActionContext, addMessages } from './action_context'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { @@ -23,7 +31,7 @@ const ActionGroupId = 'threshold met'; export function getAlertType( logger: Logger, data: Promise -): AlertType { +): AlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.indexThreshold.alertTypeTitle', { defaultMessage: 'Index threshold', }); @@ -127,6 +135,38 @@ export function getAlertType( minimumLicenseRequired: 'basic', executor, producer: STACK_ALERTS_FEATURE_ID, + // useSavedObjectReferences: { + // extractReferences: (params: Params): RuleParamsAndRefs => { + // const { index, ...otherParams } = params; + + // let indexRef: string | string[]; + // const references = []; + // if (Array.isArray(index)) { + // const extractedIndex: string[] = []; + // index.forEach((indexId: string, idx: number) => { + // extractedIndex.push(`indexRef_${idx}`); + // references.push({ + // name: `indexRef_${idx}`, + // id: indexId, + // type: 'index-pattern', + // }); + // }); + // indexRef = extractedIndex; + // } else { + // indexRef = `indexRef_0`; + // references.push({ + // name: `indexRef_0`, + // id: index, + // type: 'index-pattern', + // }); + // } + // return { params: { ...otherParams, indexRef }, references }; + // }, + // injectReferences: (params: ExtractedParams, references: SavedObjectReference[]) => { + // const { indexRef, ...otherParams } = params; + // return { ...otherParams, index: 'hi' }; + // }, + // }, }; async function executor( diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts index d32e7890b17c6..6a81a1214d67b 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts @@ -16,6 +16,7 @@ import { // alert type parameters export type Params = TypeOf; +export type ExtractedParams = Omit & { indexRef: string | string[] }; export const ParamsSchema = schema.object( { diff --git a/x-pack/plugins/stack_alerts/server/types.ts b/x-pack/plugins/stack_alerts/server/types.ts index 9725d90138300..b78aa4e6432d5 100644 --- a/x-pack/plugins/stack_alerts/server/types.ts +++ b/x-pack/plugins/stack_alerts/server/types.ts @@ -11,6 +11,7 @@ import { PluginSetupContract as AlertingSetup } from '../../alerting/server'; export { PluginSetupContract as AlertingSetup, AlertType, + RuleParamsAndRefs, AlertExecutorOptions, } from '../../alerting/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts index 29f2f0cca82bc..20b65a20c5883 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts @@ -854,6 +854,7 @@ describe('status check alert', () => { // @ts-ignore let alert: AlertType< AlertTypeParams, + never, AlertTypeState, AlertInstanceState, AlertInstanceContext, diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts index 6f9ca42e54adc..00d233b48f362 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/types.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts @@ -17,6 +17,7 @@ export type UptimeAlertTypeFactory = ( plugins: UptimeCorePlugins ) => AlertType< UptimeAlertTypeParam, + never, // Only use if defining useSavedObjectReferences hook UptimeAlertTypeState, AlertInstanceState, AlertInstanceContext, 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 3e622b49d03df..cbe5c75812599 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 @@ -64,6 +64,7 @@ function getAlwaysFiringAlertType() { } const result: AlertType< ParamsType & AlertTypeParams, + never, // Only use if defining useSavedObjectReferences hook State, InstanceState, InstanceContext, @@ -157,7 +158,7 @@ function getCumulativeFiringAlertType() { interface InstanceState extends AlertInstanceState { instanceStateValue: boolean; } - const result: AlertType<{}, State, InstanceState, {}, 'default' | 'other'> = { + const result: AlertType<{}, {}, State, InstanceState, {}, 'default' | 'other'> = { id: 'test.cumulative-firing', name: 'Test: Cumulative Firing', actionGroups: [ @@ -197,7 +198,7 @@ function getNeverFiringAlertType() { interface State extends AlertTypeState { globalStateValue: boolean; } - const result: AlertType = { + const result: AlertType = { id: 'test.never-firing', name: 'Test: Never firing', actionGroups: [ @@ -237,7 +238,7 @@ function getFailingAlertType() { reference: schema.string(), }); type ParamsType = TypeOf; - const result: AlertType = { + const result: AlertType = { id: 'test.failing', name: 'Test: Failing', validate: { @@ -278,7 +279,7 @@ function getAuthorizationAlertType(core: CoreSetup) { reference: schema.string(), }); type ParamsType = TypeOf; - const result: AlertType = { + const result: AlertType = { id: 'test.authorization', name: 'Test: Authorization', actionGroups: [ @@ -365,7 +366,7 @@ function getValidationAlertType() { param1: schema.string(), }); type ParamsType = TypeOf; - const result: AlertType = { + const result: AlertType = { id: 'test.validation', name: 'Test: Validation', actionGroups: [ @@ -397,7 +398,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' }], @@ -461,7 +462,7 @@ export function defineAlertTypes( core: CoreSetup, { alerting }: Pick ) { - const noopAlertType: AlertType<{}, {}, {}, {}, 'default'> = { + const noopAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.noop', name: 'Test: Noop', actionGroups: [{ id: 'default', name: 'Default' }], @@ -470,7 +471,7 @@ export function defineAlertTypes( minimumLicenseRequired: 'basic', async executor() {}, }; - const goldNoopAlertType: AlertType<{}, {}, {}, {}, 'default'> = { + const goldNoopAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.gold.noop', name: 'Test: Noop', actionGroups: [{ id: 'default', name: 'Default' }], @@ -479,7 +480,7 @@ export function defineAlertTypes( minimumLicenseRequired: 'gold', async executor() {}, }; - const onlyContextVariablesAlertType: AlertType<{}, {}, {}, {}, 'default'> = { + const onlyContextVariablesAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.onlyContextVariables', name: 'Test: Only Context Variables', actionGroups: [{ id: 'default', name: 'Default' }], @@ -491,7 +492,7 @@ export function defineAlertTypes( }, async executor() {}, }; - const onlyStateVariablesAlertType: AlertType<{}, {}, {}, {}, 'default'> = { + const onlyStateVariablesAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.onlyStateVariables', name: 'Test: Only State Variables', actionGroups: [{ id: 'default', name: 'Default' }], @@ -503,7 +504,7 @@ export function defineAlertTypes( minimumLicenseRequired: 'basic', async executor() {}, }; - const throwAlertType: AlertType<{}, {}, {}, {}, 'default'> = { + const throwAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.throw', name: 'Test: Throw', actionGroups: [ @@ -519,7 +520,7 @@ export function defineAlertTypes( throw new Error('this alert is intended to fail'); }, }; - const longRunningAlertType: AlertType<{}, {}, {}, {}, 'default'> = { + 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 884af7801855f..fb4019b8c7e4a 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 @@ -13,7 +13,7 @@ export function defineAlertTypes( core: CoreSetup, { alerting }: Pick ) { - const noopRestrictedAlertType: AlertType<{}, {}, {}, {}, 'default', 'restrictedRecovered'> = { + const noopRestrictedAlertType: AlertType<{}, {}, {}, {}, {}, 'default', 'restrictedRecovered'> = { id: 'test.restricted-noop', name: 'Test: Restricted Noop', actionGroups: [{ id: 'default', name: 'Default' }], @@ -23,7 +23,7 @@ export function defineAlertTypes( recoveryActionGroup: { id: 'restrictedRecovered', name: 'Restricted Recovery' }, async executor() {}, }; - const noopUnrestrictedAlertType: AlertType<{}, {}, {}, {}, 'default'> = { + const noopUnrestrictedAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.unrestricted-noop', name: 'Test: Unrestricted Noop', actionGroups: [{ id: 'default', name: 'Default' }], 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 10a81e4309088..5858a8a0dfe0a 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 @@ -18,7 +18,7 @@ export interface AlertingExampleDeps { features: FeaturesPluginSetup; } -export const noopAlertType: AlertType<{}, {}, {}, {}, 'default'> = { +export const noopAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { id: 'test.noop', name: 'Test: Noop', actionGroups: [{ id: 'default', name: 'Default' }], @@ -30,6 +30,7 @@ export const noopAlertType: AlertType<{}, {}, {}, {}, 'default'> = { export const alwaysFiringAlertType: AlertType< { instances: Array<{ id: string; state: any }> }, + never, // Only use if defining useSavedObjectReferences hook { globalStateValue: boolean; groupInSeriesIndex: number; @@ -64,7 +65,7 @@ export const alwaysFiringAlertType: AlertType< }, }; -export const failingAlertType: AlertType = { +export const failingAlertType: AlertType = { id: 'test.failing', name: 'Test: Failing', actionGroups: [ From fbcb2cc09868674f2ea94e46e83cd224e78c2c8f Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 10 Jun 2021 09:49:47 -0400 Subject: [PATCH 04/20] Adding type template for extracted params --- .../alerting/server/alert_type_registry.ts | 1 + .../server/alerts_client/alerts_client.ts | 49 ++++++++++--------- .../server/alerts_client/tests/create.test.ts | 2 +- .../server/alerts_client/tests/update.test.ts | 2 +- .../server/task_runner/task_runner_factory.ts | 1 + 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/alerting/server/alert_type_registry.ts b/x-pack/plugins/alerting/server/alert_type_registry.ts index f499700b09d84..b0f08fa050699 100644 --- a/x-pack/plugins/alerting/server/alert_type_registry.ts +++ b/x-pack/plugins/alerting/server/alert_type_registry.ts @@ -192,6 +192,7 @@ export class AlertTypeRegistry { createTaskRunner: (context: RunContext) => this.taskRunnerFactory.create< Params, + ExtractedParams, State, InstanceState, InstanceContext, diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index 2382c2397d902..ae10897727bb9 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -1539,13 +1539,16 @@ export class AlertsClient { } } - private async extractReferences( + private async extractReferences< + Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams + >( ruleType: UntypedNormalizedAlertType, ruleActions: NormalizedAlertAction[], ruleParams: Params ): Promise<{ actions: RawAlert['actions']; - params: Params; + params: ExtractedParams; references: SavedObjectReference[]; }> { const { references: actionReferences, actions } = await this.denormalizeActions(ruleActions); @@ -1555,7 +1558,7 @@ export class AlertsClient { ? ruleType.useSavedObjectReferences.extractReferences(ruleParams) : null; const extractedReferences = extractedRefsAndParams?.references ?? []; - const params = (extractedRefsAndParams?.params as Params) ?? ruleParams; + const params = (extractedRefsAndParams?.params as ExtractedParams) ?? ruleParams; // Validate that extract references don't use prefix reserved for actions const referencesUsingReservedPrefix = extractedReferences.filter( @@ -1577,26 +1580,26 @@ export class AlertsClient { }; } - private injectReferences( - ruleId: string, - ruleType: UntypedNormalizedAlertType, - ruleActions: RawAlert['actions'], - ruleParams: Params, - references: SavedObjectReference[] - ): { - actions: Alert['actions']; - params: Params; - } { - const actions = this.injectReferencesIntoActions(ruleId, ruleActions, references); - const params = ruleType?.useSavedObjectReferences?.injectReferences - ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) - : ruleParams; - - return { - actions, - params, - }; - } + // private injectReferences( + // ruleId: string, + // ruleType: UntypedNormalizedAlertType, + // ruleActions: RawAlert['actions'], + // ruleParams: Params, + // references: SavedObjectReference[] + // ): { + // actions: Alert['actions']; + // params: Params; + // } { + // const actions = this.injectReferencesIntoActions(ruleId, ruleActions, references); + // const params = ruleType?.useSavedObjectReferences?.injectReferences + // ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) + // : ruleParams; + + // return { + // actions, + // params, + // }; + // } private async denormalizeActions( alertActions: NormalizedAlertAction[] diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts index 8587316500802..b2435e23b2461 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts @@ -859,7 +859,7 @@ describe('create()', () => { }, references: [], }); - const result = await alertsClient.create({ data }); + /* const result = */ await alertsClient.create({ data }); expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts index 469c704bb1c79..fb7646480e9eb 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts @@ -477,7 +477,7 @@ describe('update()', () => { }, ], }); - const result = await alertsClient.update({ + /* const result =*/ await alertsClient.update({ id: '1', data: { schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index adfd4c9107c33..bf91954fa964c 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -81,6 +81,7 @@ export class TaskRunnerFactory { return new TaskRunner< Params, + ExtractedParams, State, InstanceState, InstanceContext, From 6412a22e450b69a6d37d8e7a2d4ab95645033d50 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 10 Jun 2021 10:20:12 -0400 Subject: [PATCH 05/20] Adding type template for extracted params --- .../alerting/log_threshold/register_log_threshold_alert_type.ts | 1 + .../server/lib/alerts/register_anomaly_detection_alert_type.ts | 1 + .../stack_alerts/server/alert_types/geo_containment/index.ts | 1 + 3 files changed, 3 insertions(+) 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 b7c6881b20390..6371cf9cc3a59 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 @@ -90,6 +90,7 @@ export async function registerLogThresholdAlertType( alertingPlugin.registerType< AlertTypeParams, + never, // Only use if defining useSavedObjectReferences hook AlertTypeState, AlertInstanceState, AlertInstanceContext, diff --git a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts index f39b3850b71b1..b051f9983cbb1 100644 --- a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts +++ b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts @@ -46,6 +46,7 @@ export function registerAnomalyDetectionAlertType({ }: RegisterAlertParams) { alerting.registerType< MlAnomalyDetectionAlertParams, + never, // Only use if defining useSavedObjectReferences hook AlertTypeState, AlertInstanceState, AnomalyDetectionAlertContext, 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 ee0b36bcd1766..023ea168a77d2 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 @@ -26,6 +26,7 @@ export function register(params: RegisterParams) { const { logger, alerting } = params; alerting.registerType< GeoContainmentParams, + never, // Only use if defining useSavedObjectReferences hook GeoContainmentState, GeoContainmentInstanceState, GeoContainmentInstanceContext, From ae10948fd216b82d49c2556c3faca866cca92d1b Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 10 Jun 2021 11:01:37 -0400 Subject: [PATCH 06/20] Adding type template for extracted params --- .../alerting/server/alert_type_registry.ts | 1 + .../server/alerts_client/alerts_client.ts | 49 ++++++++++--------- .../server/alerts_client/tests/create.test.ts | 2 +- .../server/alerts_client/tests/update.test.ts | 2 +- .../server/task_runner/task_runner_factory.ts | 1 + .../register_log_threshold_alert_type.ts | 1 + .../register_anomaly_detection_alert_type.ts | 1 + .../lib/detection_engine/signals/types.ts | 2 + .../alert_types/geo_containment/index.ts | 1 + 9 files changed, 35 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/alerting/server/alert_type_registry.ts b/x-pack/plugins/alerting/server/alert_type_registry.ts index f499700b09d84..b0f08fa050699 100644 --- a/x-pack/plugins/alerting/server/alert_type_registry.ts +++ b/x-pack/plugins/alerting/server/alert_type_registry.ts @@ -192,6 +192,7 @@ export class AlertTypeRegistry { createTaskRunner: (context: RunContext) => this.taskRunnerFactory.create< Params, + ExtractedParams, State, InstanceState, InstanceContext, diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index 2382c2397d902..ae10897727bb9 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -1539,13 +1539,16 @@ export class AlertsClient { } } - private async extractReferences( + private async extractReferences< + Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams + >( ruleType: UntypedNormalizedAlertType, ruleActions: NormalizedAlertAction[], ruleParams: Params ): Promise<{ actions: RawAlert['actions']; - params: Params; + params: ExtractedParams; references: SavedObjectReference[]; }> { const { references: actionReferences, actions } = await this.denormalizeActions(ruleActions); @@ -1555,7 +1558,7 @@ export class AlertsClient { ? ruleType.useSavedObjectReferences.extractReferences(ruleParams) : null; const extractedReferences = extractedRefsAndParams?.references ?? []; - const params = (extractedRefsAndParams?.params as Params) ?? ruleParams; + const params = (extractedRefsAndParams?.params as ExtractedParams) ?? ruleParams; // Validate that extract references don't use prefix reserved for actions const referencesUsingReservedPrefix = extractedReferences.filter( @@ -1577,26 +1580,26 @@ export class AlertsClient { }; } - private injectReferences( - ruleId: string, - ruleType: UntypedNormalizedAlertType, - ruleActions: RawAlert['actions'], - ruleParams: Params, - references: SavedObjectReference[] - ): { - actions: Alert['actions']; - params: Params; - } { - const actions = this.injectReferencesIntoActions(ruleId, ruleActions, references); - const params = ruleType?.useSavedObjectReferences?.injectReferences - ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) - : ruleParams; - - return { - actions, - params, - }; - } + // private injectReferences( + // ruleId: string, + // ruleType: UntypedNormalizedAlertType, + // ruleActions: RawAlert['actions'], + // ruleParams: Params, + // references: SavedObjectReference[] + // ): { + // actions: Alert['actions']; + // params: Params; + // } { + // const actions = this.injectReferencesIntoActions(ruleId, ruleActions, references); + // const params = ruleType?.useSavedObjectReferences?.injectReferences + // ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) + // : ruleParams; + + // return { + // actions, + // params, + // }; + // } private async denormalizeActions( alertActions: NormalizedAlertAction[] diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts index 8587316500802..b2435e23b2461 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts @@ -859,7 +859,7 @@ describe('create()', () => { }, references: [], }); - const result = await alertsClient.create({ data }); + /* const result = */ await alertsClient.create({ data }); expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts index 469c704bb1c79..fb7646480e9eb 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts @@ -477,7 +477,7 @@ describe('update()', () => { }, ], }); - const result = await alertsClient.update({ + /* const result =*/ await alertsClient.update({ id: '1', data: { schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts index adfd4c9107c33..bf91954fa964c 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_factory.ts @@ -81,6 +81,7 @@ export class TaskRunnerFactory { return new TaskRunner< Params, + ExtractedParams, State, InstanceState, InstanceContext, 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 b7c6881b20390..6371cf9cc3a59 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 @@ -90,6 +90,7 @@ export async function registerLogThresholdAlertType( alertingPlugin.registerType< AlertTypeParams, + never, // Only use if defining useSavedObjectReferences hook AlertTypeState, AlertInstanceState, AlertInstanceContext, diff --git a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts index f39b3850b71b1..b051f9983cbb1 100644 --- a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts +++ b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts @@ -46,6 +46,7 @@ export function registerAnomalyDetectionAlertType({ }: RegisterAlertParams) { alerting.registerType< MlAnomalyDetectionAlertParams, + never, // Only use if defining useSavedObjectReferences hook AlertTypeState, AlertInstanceState, AnomalyDetectionAlertContext, 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 4205c2d6d8b2c..8724f2cc822fc 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 @@ -172,6 +172,7 @@ export const isAlertExecutor = ( obj: SignalRuleAlertTypeDefinition ): obj is AlertType< RuleParams, + never, // Only use if defining useSavedObjectReferences hook AlertTypeState, AlertInstanceState, AlertInstanceContext, @@ -182,6 +183,7 @@ export const isAlertExecutor = ( export type SignalRuleAlertTypeDefinition = AlertType< RuleParams, + never, // Only use if defining useSavedObjectReferences hook AlertTypeState, AlertInstanceState, AlertInstanceContext, 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 ee0b36bcd1766..023ea168a77d2 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 @@ -26,6 +26,7 @@ export function register(params: RegisterParams) { const { logger, alerting } = params; alerting.registerType< GeoContainmentParams, + never, // Only use if defining useSavedObjectReferences hook GeoContainmentState, GeoContainmentInstanceState, GeoContainmentInstanceContext, From 3cce73bc16b686ff451099e2dee5e51a77b35efc Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 10 Jun 2021 14:43:13 -0400 Subject: [PATCH 07/20] Calling injectReferences function if defined. Finishing unit tests for create and update --- .../server/alerts_client/alerts_client.ts | 61 +++++++++----- .../server/alerts_client/tests/create.test.ts | 84 +++++++++++++++++-- .../server/alerts_client/tests/update.test.ts | 47 +++++++++-- x-pack/plugins/alerting/server/types.ts | 2 +- 4 files changed, 160 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index ae10897727bb9..cb85c972ee703 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -16,6 +16,7 @@ import { SavedObject, PluginInitializerContext, SavedObjectsUtils, + SavedObjectAttributes, } from '../../../../../src/core/server'; import { esKuery } from '../../../../../src/plugins/data/server'; import { ActionsClient, ActionsAuthorization } from '../../../actions/server'; @@ -821,7 +822,12 @@ export class AlertsClient { throw e; } - return this.getPartialAlertFromRaw(id, updatedObject.attributes, updatedObject.references); + return this.getPartialAlertFromRaw( + id, + alertType, + updatedObject.attributes, + updatedObject.references + ); } private apiKeyAsAlertAttributes( @@ -1453,15 +1459,25 @@ export class AlertsClient { rawAlert: RawAlert, references: SavedObjectReference[] | undefined ): Alert { + const ruleType = this.alertTypeRegistry.get(rawAlert.alertTypeId); // 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, ruleType, rawAlert, references) as Alert; } private getPartialAlertFromRaw( id: string, - { createdAt, updatedAt, meta, notifyWhen, scheduledTaskId, ...rawAlert }: Partial, + ruleType: UntypedNormalizedAlertType, + { + createdAt, + updatedAt, + meta, + notifyWhen, + scheduledTaskId, + params, + ...rawAlert + }: Partial, references: SavedObjectReference[] | undefined ): PartialAlert { // Not the prettiest code here, but if we want to use most of the @@ -1474,6 +1490,7 @@ export class AlertsClient { }; delete rawAlertWithoutExecutionStatus.executionStatus; const executionStatus = alertExecutionStatusFromRaw(this.logger, id, rawAlert.executionStatus); + return { id, notifyWhen, @@ -1484,6 +1501,7 @@ export class AlertsClient { actions: rawAlert.actions ? this.injectReferencesIntoActions(id, rawAlert.actions, references || []) : [], + params: this.injectReferencesIntoParams(id, ruleType, params, references || []) as Params, ...(updatedAt ? { updatedAt: new Date(updatedAt) } : {}), ...(createdAt ? { createdAt: new Date(createdAt) } : {}), ...(scheduledTaskId ? { scheduledTaskId } : {}), @@ -1573,6 +1591,7 @@ export class AlertsClient { } const references = [...actionReferences, ...extractedReferences]; + return { actions, params, @@ -1580,26 +1599,22 @@ export class AlertsClient { }; } - // private injectReferences( - // ruleId: string, - // ruleType: UntypedNormalizedAlertType, - // ruleActions: RawAlert['actions'], - // ruleParams: Params, - // references: SavedObjectReference[] - // ): { - // actions: Alert['actions']; - // params: Params; - // } { - // const actions = this.injectReferencesIntoActions(ruleId, ruleActions, references); - // const params = ruleType?.useSavedObjectReferences?.injectReferences - // ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) - // : ruleParams; - - // return { - // actions, - // params, - // }; - // } + private injectReferencesIntoParams( + ruleId: string, + ruleType: UntypedNormalizedAlertType, + ruleParams: SavedObjectAttributes | undefined, + references: SavedObjectReference[] + ): Params { + try { + return ruleParams && ruleType?.useSavedObjectReferences?.injectReferences + ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) + : (ruleParams as Params); + } catch (err) { + throw new Error( + `Error injecting reference into rule params for rule id ${ruleId} - ${err.message}` + ); + } + } private async denormalizeActions( alertActions: NormalizedAlertAction[] diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts index b2435e23b2461..374140f03f729 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts @@ -819,7 +819,10 @@ describe('create()', () => { }, ], }); - const injectReferencesFn = jest.fn(); + const injectReferencesFn = jest.fn().mockReturnValue({ + bar: true, + parameterThatIsSavedObjectId: '9', + }); alertTypeRegistry.get.mockImplementation(() => ({ id: '123', name: 'Test', @@ -837,6 +840,43 @@ describe('create()', () => { const data = getMockData({ params: ruleParams, }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + notifyWhen: null, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'soRef_0', + type: 'someSavedObjecType', + id: '9', + }, + ], + }); taskManager.schedule.mockResolvedValueOnce({ id: 'task-123', taskType: 'alerting:123', @@ -859,7 +899,7 @@ describe('create()', () => { }, references: [], }); - /* const result = */ await alertsClient.create({ data }); + const result = await alertsClient.create({ data }); expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( @@ -901,9 +941,43 @@ describe('create()', () => { } ); - // TODO - // expect(injectReferencesFn).toHaveBeenCalledWith(); - // expect(result).toEqual(); + expect(injectReferencesFn).toHaveBeenCalledWith( + { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + [ + { id: '1', name: 'action_0', type: 'action' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjecType' }, + ] + ); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "createdAt": 2019-02-12T21:01:22.479Z, + "id": "1", + "notifyWhen": null, + "params": Object { + "bar": true, + "parameterThatIsSavedObjectId": "9", + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); }); test('should trim alert name when creating API key', async () => { diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts index fb7646480e9eb..c03ae6c3c5bf7 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts @@ -424,7 +424,10 @@ describe('update()', () => { }, ], }); - const injectReferencesFn = jest.fn(); + const injectReferencesFn = jest.fn().mockReturnValue({ + bar: true, + parameterThatIsSavedObjectId: '9', + }); alertTypeRegistry.get.mockImplementation(() => ({ id: 'myType', name: 'Test', @@ -477,7 +480,7 @@ describe('update()', () => { }, ], }); - /* const result =*/ await alertsClient.update({ + const result = await alertsClient.update({ id: '1', data: { schedule: { interval: '10s' }, @@ -532,9 +535,43 @@ describe('update()', () => { } ); - // TODO - // expect(injectReferencesFn).toHaveBeenCalledWith(); - // expect(result).toEqual(); + expect(injectReferencesFn).toHaveBeenCalledWith( + { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + [ + { id: '1', name: 'action_0', type: 'action' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjecType' }, + ] + ); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "createdAt": 2019-02-12T21:01:22.479Z, + "enabled": true, + "id": "1", + "notifyWhen": "onActiveAlert", + "params": Object { + "bar": true, + "parameterThatIsSavedObjectId": "9", + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); }); it('calls the createApiKey function', async () => { diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index b73a78b089c9d..f5a03eae4a8c0 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -154,7 +154,7 @@ export interface AlertType< minimumLicenseRequired: LicenseType; useSavedObjectReferences?: { extractReferences: (params: Params) => RuleParamsAndRefs; - injectReferences: (params: ExtractedParams, references: SavedObjectReference[]) => Params; + injectReferences: (params: SavedObjectAttributes, references: SavedObjectReference[]) => Params; }; } From 878209a2615245dc93a91d2a0e47957bc1185275 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 10 Jun 2021 15:13:21 -0400 Subject: [PATCH 08/20] Adding tests for get --- .../server/alerts_client/tests/create.test.ts | 10 +- .../server/alerts_client/tests/find.test.ts | 1 + .../server/alerts_client/tests/get.test.ts | 156 ++++++++++++++++++ .../server/alerts_client/tests/update.test.ts | 10 +- 4 files changed, 167 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts index 374140f03f729..b14f3cf4e8fa0 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts @@ -814,7 +814,7 @@ describe('create()', () => { references: [ { name: 'soRef_0', - type: 'someSavedObjecType', + type: 'someSavedObjectType', id: '9', }, ], @@ -872,7 +872,7 @@ describe('create()', () => { }, { name: 'soRef_0', - type: 'someSavedObjecType', + type: 'someSavedObjectType', id: '9', }, ], @@ -936,7 +936,7 @@ describe('create()', () => { id: 'mock-saved-object-id', references: [ { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjecType' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, ], } ); @@ -948,7 +948,7 @@ describe('create()', () => { }, [ { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjecType' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, ] ); expect(result).toMatchInlineSnapshot(` @@ -1493,7 +1493,7 @@ describe('create()', () => { references: [ { name: 'action_0', - type: 'someSavedObjecType', + type: 'someSavedObjectType', id: '9', }, ], diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts index 8fa8ae7ae38b0..3ae507a3ed9ad 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts @@ -255,6 +255,7 @@ describe('find()', () => { "actions": Array [], "id": "1", "notifyWhen": undefined, + "params": undefined, "schedule": undefined, "tags": Array [ "myTag", diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts index a958ea4061ae5..d7ad255568010 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts @@ -17,6 +17,7 @@ import { ActionsAuthorization } from '../../../../actions/server'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { auditServiceMock } from '../../../../security/server/audit/index.mock'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { RecoveredActionGroup } from '../../../common'; const taskManager = taskManagerMock.createStart(); const alertTypeRegistry = alertTypeRegistryMock.create(); @@ -118,6 +119,101 @@ describe('get()', () => { `); }); + test('should call useSavedObjectReferences.injectReferences if defined for rule type', async () => { + const injectReferencesFn = jest.fn().mockReturnValue({ + bar: true, + parameterThatIsSavedObjectId: '9', + }); + alertTypeRegistry.get.mockImplementation(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: jest.fn(), + injectReferences: injectReferencesFn, + }, + })); + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + notifyWhen: 'onActiveAlert', + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'soRef_0', + type: 'someSavedObjectType', + id: '9', + }, + ], + }); + const result = await alertsClient.get({ id: '1' }); + + expect(injectReferencesFn).toHaveBeenCalledWith( + { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + [ + { id: '1', name: 'action_0', type: 'action' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, + ] + ); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "createdAt": 2019-02-12T21:01:22.479Z, + "id": "1", + "notifyWhen": "onActiveAlert", + "params": Object { + "bar": true, + "parameterThatIsSavedObjectId": "9", + }, + "schedule": Object { + "interval": "10s", + }, + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + }); + test(`throws an error when references aren't found`, async () => { const alertsClient = new AlertsClient(alertsClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ @@ -146,6 +242,66 @@ describe('get()', () => { ); }); + test('throws an error if useSavedObjectReferences.injectReferences throws an error', async () => { + const injectReferencesFn = jest.fn().mockImplementation(() => { + throw new Error('something went wrong!'); + }); + alertTypeRegistry.get.mockImplementation(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: jest.fn(), + injectReferences: injectReferencesFn, + }, + })); + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + notifyWhen: 'onActiveAlert', + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'soRef_0', + type: 'someSavedObjectType', + id: '9', + }, + ], + }); + await expect(alertsClient.get({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error injecting reference into rule params for rule id 1 - something went wrong!"` + ); + }); + describe('authorization', () => { beforeEach(() => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts index c03ae6c3c5bf7..2ebb789e52421 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts @@ -419,7 +419,7 @@ describe('update()', () => { references: [ { name: 'soRef_0', - type: 'someSavedObjecType', + type: 'someSavedObjectType', id: '9', }, ], @@ -475,7 +475,7 @@ describe('update()', () => { }, { name: 'soRef_0', - type: 'someSavedObjecType', + type: 'someSavedObjectType', id: '9', }, ], @@ -529,7 +529,7 @@ describe('update()', () => { overwrite: true, references: [ { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjecType' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, ], version: '123', } @@ -542,7 +542,7 @@ describe('update()', () => { }, [ { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjecType' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, ] ); expect(result).toMatchInlineSnapshot(` @@ -1269,7 +1269,7 @@ describe('update()', () => { references: [ { name: 'action_0', - type: 'someSavedObjecType', + type: 'someSavedObjectType', id: '9', }, ], From dc97b5b6a5e5a0eb912888966fdf3ed8e005d18c Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 14 Jun 2021 12:08:47 -0400 Subject: [PATCH 09/20] Adding tests for find --- .../server/alerts_client/tests/find.test.ts | 326 ++++++++++++++++++ 1 file changed, 326 insertions(+) diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts index 3ae507a3ed9ad..be4b93f7fd6d3 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts @@ -189,6 +189,332 @@ describe('find()', () => { expect(jest.requireMock('../lib/map_sort_field').mapSortField).toHaveBeenCalledWith('name'); }); + test('should call useSavedObjectReferences.injectReferences if defined for rule type', async () => { + jest.resetAllMocks(); + authorization.getFindAuthorizationFilter.mockResolvedValue({ + ensureRuleTypeIsAuthorized() {}, + logSuccessfulAuthorization() {}, + }); + const injectReferencesFn = jest.fn().mockReturnValue({ + bar: true, + parameterThatIsSavedObjectId: '9', + }); + alertTypeRegistry.list.mockReturnValue( + new Set([ + ...listedTypes, + { + actionGroups: [], + recoveryActionGroup: RecoveredActionGroup, + actionVariables: undefined, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + id: '123', + name: 'myType', + producer: 'myApp', + enabledInLicense: true, + }, + ]) + ); + alertTypeRegistry.get.mockImplementationOnce(() => ({ + id: 'myType', + name: 'myType', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'myApp', + })); + alertTypeRegistry.get.mockImplementationOnce(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: jest.fn(), + injectReferences: injectReferencesFn, + }, + })); + unsecuredSavedObjectsClient.find.mockResolvedValue({ + total: 2, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'alert', + attributes: { + alertTypeId: 'myType', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + score: 1, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + { + id: '2', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '20s' }, + params: { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + score: 1, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'soRef_0', + type: 'someSavedObjectType', + id: '9', + }, + ], + }, + ], + }); + const alertsClient = new AlertsClient(alertsClientParams); + const result = await alertsClient.find({ options: {} }); + + expect(injectReferencesFn).toHaveBeenCalledTimes(1); + expect(injectReferencesFn).toHaveBeenCalledWith( + { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + [ + { id: '1', name: 'action_0', type: 'action' }, + { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, + ] + ); + + expect(result).toMatchInlineSnapshot(` + Object { + "data": Array [ + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "myType", + "createdAt": 2019-02-12T21:01:22.479Z, + "id": "1", + "notifyWhen": "onActiveAlert", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "updatedAt": 2019-02-12T21:01:22.479Z, + }, + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "createdAt": 2019-02-12T21:01:22.479Z, + "id": "2", + "notifyWhen": "onActiveAlert", + "params": Object { + "bar": true, + "parameterThatIsSavedObjectId": "9", + }, + "schedule": Object { + "interval": "20s", + }, + "updatedAt": 2019-02-12T21:01:22.479Z, + }, + ], + "page": 1, + "perPage": 10, + "total": 2, + } + `); + }); + + test('throws an error if useSavedObjectReferences.injectReferences throws an error', async () => { + jest.resetAllMocks(); + authorization.getFindAuthorizationFilter.mockResolvedValue({ + ensureRuleTypeIsAuthorized() {}, + logSuccessfulAuthorization() {}, + }); + const injectReferencesFn = jest.fn().mockImplementation(() => { + throw new Error('something went wrong!'); + }); + alertTypeRegistry.list.mockReturnValue( + new Set([ + ...listedTypes, + { + actionGroups: [], + recoveryActionGroup: RecoveredActionGroup, + actionVariables: undefined, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + id: '123', + name: 'myType', + producer: 'myApp', + enabledInLicense: true, + }, + ]) + ); + alertTypeRegistry.get.mockImplementationOnce(() => ({ + id: 'myType', + name: 'myType', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'myApp', + })); + alertTypeRegistry.get.mockImplementationOnce(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: jest.fn(), + injectReferences: injectReferencesFn, + }, + })); + unsecuredSavedObjectsClient.find.mockResolvedValue({ + total: 2, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'alert', + attributes: { + alertTypeId: 'myType', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + score: 1, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + { + id: '2', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '20s' }, + params: { + bar: true, + parameterThatIsSavedObjectRef: 'soRef_0', + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + notifyWhen: 'onActiveAlert', + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + score: 1, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'soRef_0', + type: 'someSavedObjectType', + id: '9', + }, + ], + }, + ], + }); + const alertsClient = new AlertsClient(alertsClientParams); + await expect(alertsClient.find({ options: {} })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error injecting reference into rule params for rule id 1 - something went wrong!"` + ); + }); + describe('authorization', () => { test('ensures user is query filter types down to those the user is authorized to find', async () => { const filter = esKuery.fromKueryExpression( From b710b3996d945825c35d04a50625b28fe4bb523d Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 14 Jun 2021 12:12:37 -0400 Subject: [PATCH 10/20] Cleanup --- .../alert_types/index_threshold/alert_type.ts | 48 ++----------------- .../index_threshold/alert_type_params.ts | 1 - 2 files changed, 4 insertions(+), 45 deletions(-) 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 68de5e0a58f95..008eb2944ff2b 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 @@ -6,17 +6,9 @@ */ import { i18n } from '@kbn/i18n'; -import { - Logger, - // SavedObjectReference -} from 'src/core/server'; -import { - AlertType, - AlertExecutorOptions, - StackAlertsStartDeps, - // RuleParamsAndRefs, -} from '../../types'; -import { Params, ExtractedParams, ParamsSchema } from './alert_type_params'; +import { Logger } from 'src/core/server'; +import { AlertType, AlertExecutorOptions, StackAlertsStartDeps } from '../../types'; +import { Params, ParamsSchema } from './alert_type_params'; import { ActionContext, BaseActionContext, addMessages } from './action_context'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { @@ -31,7 +23,7 @@ const ActionGroupId = 'threshold met'; export function getAlertType( logger: Logger, data: Promise -): AlertType { +): AlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.indexThreshold.alertTypeTitle', { defaultMessage: 'Index threshold', }); @@ -135,38 +127,6 @@ export function getAlertType( minimumLicenseRequired: 'basic', executor, producer: STACK_ALERTS_FEATURE_ID, - // useSavedObjectReferences: { - // extractReferences: (params: Params): RuleParamsAndRefs => { - // const { index, ...otherParams } = params; - - // let indexRef: string | string[]; - // const references = []; - // if (Array.isArray(index)) { - // const extractedIndex: string[] = []; - // index.forEach((indexId: string, idx: number) => { - // extractedIndex.push(`indexRef_${idx}`); - // references.push({ - // name: `indexRef_${idx}`, - // id: indexId, - // type: 'index-pattern', - // }); - // }); - // indexRef = extractedIndex; - // } else { - // indexRef = `indexRef_0`; - // references.push({ - // name: `indexRef_0`, - // id: index, - // type: 'index-pattern', - // }); - // } - // return { params: { ...otherParams, indexRef }, references }; - // }, - // injectReferences: (params: ExtractedParams, references: SavedObjectReference[]) => { - // const { indexRef, ...otherParams } = params; - // return { ...otherParams, index: 'hi' }; - // }, - // }, }; async function executor( diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts index 6a81a1214d67b..d32e7890b17c6 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type_params.ts @@ -16,7 +16,6 @@ import { // alert type parameters export type Params = TypeOf; -export type ExtractedParams = Omit & { indexRef: string | string[] }; export const ParamsSchema = schema.object( { From 3ec8fd46c57ef310d4194bd83d5df5ea88957fac Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 14 Jun 2021 12:45:20 -0400 Subject: [PATCH 11/20] Fixing types check --- x-pack/plugins/alerting/server/task_runner/task_runner.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 9d93ea6ba7c27..bd106370c9770 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -686,6 +686,7 @@ interface GenerateNewAndRecoveredInstanceEventsParams< alertLabel: string; namespace: string | undefined; ruleType: NormalizedAlertType< + AlertTypeParams, AlertTypeParams, AlertTypeState, { From 4980b8a8d253ed56a868ab581c9b17a025df4d02 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 14 Jun 2021 14:07:37 -0400 Subject: [PATCH 12/20] Fixing functional tests --- .../server/alerts_client/alerts_client.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index cb85c972ee703..cc73d06fcd316 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -366,7 +366,12 @@ export class AlertsClient { }); createdAlert.attributes.scheduledTaskId = scheduledTask.id; } - return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references); + return this.getAlertFromRaw( + createdAlert.id, + createdAlert.attributes.alertTypeId, + createdAlert.attributes, + references + ); } public async get({ @@ -398,7 +403,12 @@ export class AlertsClient { savedObject: { type: 'alert', id }, }) ); - return this.getAlertFromRaw(result.id, result.attributes, result.references); + return this.getAlertFromRaw( + result.id, + result.attributes.alertTypeId, + result.attributes, + result.references + ); } public async getAlertState({ id }: { id: string }): Promise { @@ -527,6 +537,7 @@ export class AlertsClient { } return this.getAlertFromRaw( id, + attributes.alertTypeId, fields ? (pick(attributes, fields) as RawAlert) : attributes, references ); @@ -1456,10 +1467,11 @@ export class AlertsClient { private getAlertFromRaw( id: string, + ruleTypeId: string, rawAlert: RawAlert, references: SavedObjectReference[] | undefined ): Alert { - const ruleType = this.alertTypeRegistry.get(rawAlert.alertTypeId); + const ruleType = this.alertTypeRegistry.get(ruleTypeId); // 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 From 75e75fe2a49cf57b80f95c0faa53a0bcf1a64447 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 14 Jun 2021 16:17:43 -0400 Subject: [PATCH 13/20] Fixing functional tests --- .../fixtures/plugins/alerts/server/alert_types.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 cbe5c75812599..2b451c4940da8 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 @@ -536,6 +536,19 @@ export function defineAlertTypes( await new Promise((resolve) => setTimeout(resolve, 5000)); }, }; + const exampleAlwaysFiringAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { + id: 'example.always-firing', + name: 'Always firing', + actionGroups: [ + { id: 'small', name: 'Small t-shirt' }, + { id: 'medium', name: 'Medium t-shirt' }, + { id: 'large', name: 'Large t-shirt' }, + ], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alertsFixture', + }; alerting.registerType(getAlwaysFiringAlertType()); alerting.registerType(getCumulativeFiringAlertType()); @@ -550,4 +563,5 @@ export function defineAlertTypes( alerting.registerType(throwAlertType); alerting.registerType(longRunningAlertType); alerting.registerType(goldNoopAlertType); + alerting.registerType(exampleAlwaysFiringAlertType); } From 4cec840665ef00acf13a9c4603f7d0e9d38b2852 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 14 Jun 2021 19:48:03 -0400 Subject: [PATCH 14/20] Fixing tests --- .../alerting/server/alerts_client/tests/find.test.ts | 2 +- .../fixtures/plugins/alerts/server/alert_types.ts | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts index be4b93f7fd6d3..c1c97dbde9756 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts @@ -511,7 +511,7 @@ describe('find()', () => { }); const alertsClient = new AlertsClient(alertsClientParams); await expect(alertsClient.find({ options: {} })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Error injecting reference into rule params for rule id 1 - something went wrong!"` + `"Error injecting reference into rule params for rule id 2 - something went wrong!"` ); }); 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 2b451c4940da8..fbe3f73c233e6 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 @@ -536,7 +536,14 @@ export function defineAlertTypes( await new Promise((resolve) => setTimeout(resolve, 5000)); }, }; - const exampleAlwaysFiringAlertType: AlertType<{}, {}, {}, {}, {}, 'default'> = { + const exampleAlwaysFiringAlertType: AlertType< + {}, + {}, + {}, + {}, + {}, + 'small' | 'medium' | 'large' + > = { id: 'example.always-firing', name: 'Always firing', actionGroups: [ @@ -544,7 +551,7 @@ export function defineAlertTypes( { id: 'medium', name: 'Medium t-shirt' }, { id: 'large', name: 'Large t-shirt' }, ], - defaultActionGroupId: 'default', + defaultActionGroupId: 'small', minimumLicenseRequired: 'basic', async executor() {}, producer: 'alertsFixture', From 188eb10c3e3fe42b8b9560893a0b91801ae6dc49 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 15 Jun 2021 10:35:47 -0400 Subject: [PATCH 15/20] Updating README --- x-pack/plugins/alerting/README.md | 47 +++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index 7d21b71a5a61d..394f4fbc6a0f8 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -118,6 +118,8 @@ The following table describes the properties of the `options` object. |executor|This is where the code for the rule type lives. This is a function to be called when executing a rule on an interval basis. For full details, see the executor section below.|Function| |producer|The id of the application producing this rule type.|string| |minimumLicenseRequired|The value of a minimum license. Most of the rules are licensed as "basic".|string| +|useSavedObjectReferences.extractReferences|(Optional) When developing a rule type, you can choose to implement hooks for extracting saved object references from rule parameters. This hook will be invoked when a rule is created or updated. Implementing this hook is optional, but if an extract hook is implemented, an inject hook must also be implemented.|Function +|useSavedObjectReferences.injectReferences|(Optional) When developing a rule type, you can choose to implement hooks for injecting saved object references into rule parameters. This hook will be invoked when a rule is retrieved (get or find). Implementing this hook is optional, but if an inject hook is implemented, an extract hook must also be implemented.|Function ### Executor @@ -172,6 +174,19 @@ For example, if the `context` has one variable `foo` which is an object that has } ``` +### useSavedObjectReferences Hooks + +This is an optional pair of functions that can be implemented by a rule type. Both `extractReferences` and `injectReferences` functions must be implemented if either is impemented. + +**useSavedObjectReferences.extractReferences** + +This function should take the rule type params as input and extract out any saved object IDs stored within the params. For each saved object ID, a new saved object reference should be created and a saved object reference should replace the saved object ID in the rule params. This function should return the modified rule type params (with saved object references, not IDs) and an array of saved object references. Note that any extracted saved object reference name must not start with the prefix `action_`, which is reserved for use by the framework. + + +**useSavedObjectReferences.injectReferences** + + +This function should take the rule type params (with saved object references) and the saved object references array as input and inject the saved object ID in place of any saved object references in the rule type params. Note that any error thrown within this function will be propagated. ## Licensing Currently most rule types are free features. But some rule types are subscription features, such as the tracking containment rule. @@ -209,6 +224,13 @@ import { interface MyRuleTypeParams extends AlertTypeParams { server: string; threshold: number; + testSavedObjectId: string; +} + +interface MyRuleTypeExtractedParams extends AlertTypeParams { + server: string; + threshold: number; + testSavedObjectRef: string; } interface MyRuleTypeState extends AlertTypeState { @@ -273,6 +295,7 @@ const myRuleType: AlertType< rule, }: AlertExecutorOptions< MyRuleTypeParams, + MyRuleTypeExtractedParams, MyRuleTypeState, MyRuleTypeAlertState, MyRuleTypeAlertContext, @@ -319,8 +342,28 @@ const myRuleType: AlertType< }; }, producer: 'alerting', - useSavedObjectReferences?: { - + useSavedObjectReferences: { + extractReferences: (params: Params): RuleParamsAndRefs => { + const { testSavedObjectId, ...otherParams } = params; + + const testSavedObjectRef = 'testRef_0'; + const references = [ + { + name: `testRef_0`, + id: testSavedObjectId, + type: 'index-pattern', + }, + ]; + return { params: { ...otherParams, testSavedObjectRef }, references }; + }, + injectReferences: (params: SavedObjectAttributes, references: SavedObjectReference[]) => { + const { testSavedObjectRef, ...otherParams } = params; + const reference = references.find((ref) => ref.name === testSavedObjectRef); + if (!reference) { + throw new Error(`Test reference "${testSavedObjectRef}"`); + } + return { ...otherParams, testSavedObjectId: reference.id } as Params; + }, } }; From 8a0c98a3b4a74d597f2c8d198dd039fb9561589e Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 15 Jun 2021 12:47:44 -0400 Subject: [PATCH 16/20] Throwing boom error instead of normal error --- x-pack/plugins/alerting/server/alerts_client/alerts_client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index cc73d06fcd316..794dfd5469a4d 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -1622,7 +1622,7 @@ export class AlertsClient { ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) : (ruleParams as Params); } catch (err) { - throw new Error( + throw Boom.badRequest( `Error injecting reference into rule params for rule id ${ruleId} - ${err.message}` ); } From 40c5344b7f21d4abd387e2aba5d59eed2a937948 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 21 Jun 2021 15:17:39 -0400 Subject: [PATCH 17/20] Adding framework level prefix to extracted saved object reference names --- x-pack/plugins/alerting/README.md | 2 +- .../server/alerts_client/alerts_client.ts | 36 +-- .../server/alerts_client/tests/create.test.ts | 229 ++++++++++++++---- .../server/alerts_client/tests/find.test.ts | 7 +- .../server/alerts_client/tests/get.test.ts | 7 +- .../server/alerts_client/tests/update.test.ts | 70 +----- 6 files changed, 207 insertions(+), 144 deletions(-) diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index 394f4fbc6a0f8..319956dac33ba 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -180,7 +180,7 @@ This is an optional pair of functions that can be implemented by a rule type. Bo **useSavedObjectReferences.extractReferences** -This function should take the rule type params as input and extract out any saved object IDs stored within the params. For each saved object ID, a new saved object reference should be created and a saved object reference should replace the saved object ID in the rule params. This function should return the modified rule type params (with saved object references, not IDs) and an array of saved object references. Note that any extracted saved object reference name must not start with the prefix `action_`, which is reserved for use by the framework. +This function should take the rule type params as input and extract out any saved object IDs stored within the params. For each saved object ID, a new saved object reference should be created and a saved object reference should replace the saved object ID in the rule params. This function should return the modified rule type params (with saved object reference name, not IDs) and an array of saved object references. **useSavedObjectReferences.injectReferences** diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index fcd8fa33c9188..11b1ddf84d2fe 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -184,7 +184,7 @@ export interface GetAlertInstanceSummaryParams { dateStart?: string; } -const reservedSavedObjectReferenceNamePrefix = 'action_'; +const extractedSavedObjectParamReferenceNamePrefix = 'param:'; const alertingAuthorizationFilterOpts: AlertingAuthorizationFilterOpts = { type: AlertingAuthorizationFilterType.KQL, @@ -285,7 +285,6 @@ export class AlertsClient { throw Boom.badRequest(`Error creating rule: could not create API key - ${error.message}`); } - // Validate actions await this.validateActions(alertType, data.actions); // Extract saved object references for this rule @@ -1590,19 +1589,13 @@ export class AlertsClient { const extractedReferences = extractedRefsAndParams?.references ?? []; const params = (extractedRefsAndParams?.params as ExtractedParams) ?? ruleParams; - // Validate that extract references don't use prefix reserved for actions - const referencesUsingReservedPrefix = extractedReferences.filter( - (reference: SavedObjectReference) => - reference.name.startsWith(reservedSavedObjectReferenceNamePrefix) - ); - - if (referencesUsingReservedPrefix.length > 0) { - throw Boom.badRequest( - `Error creating rule: extracted saved object reference names are cannot start with ${reservedSavedObjectReferenceNamePrefix}` - ); - } + // Prefix extracted references in order to avoid clashes with framework level references + const paramReferences = extractedReferences.map((reference: SavedObjectReference) => ({ + ...reference, + name: `${extractedSavedObjectParamReferenceNamePrefix}${reference.name}`, + })); - const references = [...actionReferences, ...extractedReferences]; + const references = [...actionReferences, ...paramReferences]; return { actions, @@ -1618,8 +1611,19 @@ export class AlertsClient { references: SavedObjectReference[] ): Params { try { + const paramReferences = references + .filter((reference: SavedObjectReference) => + reference.name.startsWith(extractedSavedObjectParamReferenceNamePrefix) + ) + .map((reference: SavedObjectReference) => ({ + ...reference, + name: reference.name.replace(extractedSavedObjectParamReferenceNamePrefix, ''), + })); return ruleParams && ruleType?.useSavedObjectReferences?.injectReferences - ? (ruleType.useSavedObjectReferences.injectReferences(ruleParams, references) as Params) + ? (ruleType.useSavedObjectReferences.injectReferences( + ruleParams, + paramReferences + ) as Params) : (ruleParams as Params); } catch (err) { throw Boom.badRequest( @@ -1645,7 +1649,7 @@ export class AlertsClient { alertActions.forEach(({ id, ...alertAction }, i) => { const actionResultValue = actionResults.find((action) => action.id === id); if (actionResultValue) { - const actionRef = `${reservedSavedObjectReferenceNamePrefix}${i}`; + const actionRef = `action_${i}`; references.push({ id, name: actionRef, diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts index e635e12cb3dab..b54770f0e49b2 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts @@ -871,7 +871,7 @@ describe('create()', () => { id: '1', }, { - name: 'soRef_0', + name: 'param:soRef_0', type: 'someSavedObjectType', id: '9', }, @@ -936,7 +936,7 @@ describe('create()', () => { id: 'mock-saved-object-id', references: [ { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, + { id: '9', name: 'param:soRef_0', type: 'someSavedObjectType' }, ], } ); @@ -946,10 +946,7 @@ describe('create()', () => { bar: true, parameterThatIsSavedObjectRef: 'soRef_0', }, - [ - { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, - ] + [{ id: '9', name: 'soRef_0', type: 'someSavedObjectType' }] ); expect(result).toMatchInlineSnapshot(` Object { @@ -980,6 +977,182 @@ describe('create()', () => { `); }); + test('should allow rule types to use action_ prefix for saved object reference names', async () => { + const ruleParams = { + bar: true, + parameterThatIsSavedObjectId: '8', + }; + const extractReferencesFn = jest.fn().mockReturnValue({ + params: { + bar: true, + parameterThatIsSavedObjectRef: 'action_0', + }, + references: [ + { + name: 'action_0', + type: 'someSavedObjectType', + id: '8', + }, + ], + }); + const injectReferencesFn = jest.fn().mockReturnValue({ + bar: true, + parameterThatIsSavedObjectId: '8', + }); + alertTypeRegistry.get.mockImplementation(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup: RecoveredActionGroup, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + async executor() {}, + producer: 'alerts', + useSavedObjectReferences: { + extractReferences: extractReferencesFn, + injectReferences: injectReferencesFn, + }, + })); + const data = getMockData({ + params: ruleParams, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + parameterThatIsSavedObjectRef: 'action_0', + }, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + notifyWhen: null, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'param:action_0', + type: 'someSavedObjectType', + id: '8', + }, + ], + }); + taskManager.schedule.mockResolvedValueOnce({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + scheduledTaskId: 'task-123', + }, + references: [], + }); + const result = await alertsClient.create({ data }); + + expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'alert', + { + actions: [ + { actionRef: 'action_0', actionTypeId: 'test', group: 'default', params: { foo: true } }, + ], + alertTypeId: '123', + apiKey: null, + apiKeyOwner: null, + consumer: 'bar', + createdAt: '2019-02-12T21:01:22.479Z', + createdBy: 'elastic', + enabled: true, + executionStatus: { + error: null, + lastExecutionDate: '2019-02-12T21:01:22.479Z', + status: 'pending', + }, + meta: { versionApiKeyLastmodified: 'v7.10.0' }, + muteAll: false, + mutedInstanceIds: [], + name: 'abc', + notifyWhen: 'onActiveAlert', + params: { bar: true, parameterThatIsSavedObjectRef: 'action_0' }, + schedule: { interval: '10s' }, + tags: ['foo'], + throttle: null, + updatedAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + }, + { + id: 'mock-saved-object-id', + references: [ + { id: '1', name: 'action_0', type: 'action' }, + { id: '8', name: 'param:action_0', type: 'someSavedObjectType' }, + ], + } + ); + + expect(injectReferencesFn).toHaveBeenCalledWith( + { + bar: true, + parameterThatIsSavedObjectRef: 'action_0', + }, + [{ id: '8', name: 'action_0', type: 'someSavedObjectType' }] + ); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "createdAt": 2019-02-12T21:01:22.479Z, + "id": "1", + "notifyWhen": null, + "params": Object { + "bar": true, + "parameterThatIsSavedObjectId": "8", + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + }); + test('should trim alert name when creating API key', async () => { const data = getMockData({ name: ' my alert name ' }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ @@ -1480,50 +1653,6 @@ describe('create()', () => { ); }); - test('throws error if extracted references uses reserved name prefix', async () => { - const ruleParams = { - bar: true, - parameterThatIsSavedObjectId: '9', - }; - const extractReferencesFn = jest.fn().mockReturnValue({ - params: { - bar: true, - parameterThatIsSavedObjectRef: 'action_0', - }, - references: [ - { - name: 'action_0', - type: 'someSavedObjectType', - id: '9', - }, - ], - }); - alertTypeRegistry.get.mockImplementation(() => ({ - id: '123', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup: RecoveredActionGroup, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - async executor() {}, - producer: 'alerts', - useSavedObjectReferences: { - extractReferences: extractReferencesFn, - injectReferences: jest.fn(), - }, - })); - const data = getMockData({ - params: ruleParams, - }); - await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Error creating rule: extracted saved object reference names are cannot start with action_"` - ); - - expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); - expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - expect(taskManager.schedule).not.toHaveBeenCalled(); - }); - test('throws error if loading actions fails', async () => { const data = getMockData(); // Reset from default behaviour diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts index 6402d10b6b11f..f77cfcfd59d91 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts @@ -306,7 +306,7 @@ describe('find()', () => { id: '1', }, { - name: 'soRef_0', + name: 'param:soRef_0', type: 'someSavedObjectType', id: '9', }, @@ -323,10 +323,7 @@ describe('find()', () => { bar: true, parameterThatIsSavedObjectRef: 'soRef_0', }, - [ - { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, - ] + [{ id: '9', name: 'soRef_0', type: 'someSavedObjectType' }] ); expect(result).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts index 43ef552827afb..0c3c03f2715d9 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts @@ -169,7 +169,7 @@ describe('get()', () => { id: '1', }, { - name: 'soRef_0', + name: 'param:soRef_0', type: 'someSavedObjectType', id: '9', }, @@ -182,10 +182,7 @@ describe('get()', () => { bar: true, parameterThatIsSavedObjectRef: 'soRef_0', }, - [ - { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, - ] + [{ id: '9', name: 'soRef_0', type: 'someSavedObjectType' }] ); expect(result).toMatchInlineSnapshot(` Object { diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts index 168482e7f376b..ff262c21e1df3 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts @@ -474,7 +474,7 @@ describe('update()', () => { id: '1', }, { - name: 'soRef_0', + name: 'param:soRef_0', type: 'someSavedObjectType', id: '9', }, @@ -529,7 +529,7 @@ describe('update()', () => { overwrite: true, references: [ { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, + { id: '9', name: 'param:soRef_0', type: 'someSavedObjectType' }, ], version: '123', } @@ -540,10 +540,7 @@ describe('update()', () => { bar: true, parameterThatIsSavedObjectRef: 'soRef_0', }, - [ - { id: '1', name: 'action_0', type: 'action' }, - { id: '9', name: 'soRef_0', type: 'someSavedObjectType' }, - ] + [{ id: '9', name: 'soRef_0', type: 'someSavedObjectType' }] ); expect(result).toMatchInlineSnapshot(` Object { @@ -1256,67 +1253,6 @@ describe('update()', () => { ).toBe('234'); }); - test('throws error if extracted references uses reserved name prefix', async () => { - const ruleParams = { - bar: true, - parameterThatIsSavedObjectId: '9', - }; - const extractReferencesFn = jest.fn().mockReturnValue({ - params: { - bar: true, - parameterThatIsSavedObjectRef: 'action_0', - }, - references: [ - { - name: 'action_0', - type: 'someSavedObjectType', - id: '9', - }, - ], - }); - alertTypeRegistry.get.mockImplementation(() => ({ - id: 'myType', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - recoveryActionGroup: RecoveredActionGroup, - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - async executor() {}, - producer: 'alerts', - useSavedObjectReferences: { - extractReferences: extractReferencesFn, - injectReferences: jest.fn(), - }, - })); - await expect( - alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: ruleParams, - throttle: null, - notifyWhen: 'onActiveAlert', - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Error creating rule: extracted saved object reference names are cannot start with action_"` - ); - - expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); - expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - }); - describe('updating an alert schedule', () => { function mockApiCalls( alertId: string, From fad6eab2df75da162d354e8e6ed86d0ffe72c1dc Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 29 Jun 2021 10:14:01 -0400 Subject: [PATCH 18/20] Fixing types --- .../alerting/server/alerts_client/tests/create.test.ts | 2 ++ .../alerting/server/alerts_client/tests/find.test.ts | 6 ++++++ .../plugins/alerting/server/alerts_client/tests/get.test.ts | 2 ++ .../alerting/server/alerts_client/tests/update.test.ts | 1 + 4 files changed, 11 insertions(+) diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts index 00d782bdc3ccd..3275e25b85df5 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/create.test.ts @@ -830,6 +830,7 @@ describe('create()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'alerts', useSavedObjectReferences: { @@ -1006,6 +1007,7 @@ describe('create()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'alerts', useSavedObjectReferences: { diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts index 01865a086443a..0633dda8e7a59 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/find.test.ts @@ -210,6 +210,7 @@ describe('find()', () => { actionVariables: undefined, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, id: '123', name: 'myType', producer: 'myApp', @@ -224,6 +225,7 @@ describe('find()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'myApp', })); @@ -234,6 +236,7 @@ describe('find()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'alerts', useSavedObjectReferences: { @@ -402,6 +405,7 @@ describe('find()', () => { actionVariables: undefined, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, id: '123', name: 'myType', producer: 'myApp', @@ -416,6 +420,7 @@ describe('find()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'myApp', })); @@ -426,6 +431,7 @@ describe('find()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'alerts', useSavedObjectReferences: { diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts index 0c3c03f2715d9..82a8acefb386d 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/get.test.ts @@ -131,6 +131,7 @@ describe('get()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'alerts', useSavedObjectReferences: { @@ -250,6 +251,7 @@ describe('get()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'alerts', useSavedObjectReferences: { diff --git a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts index eb4fa8ccec4bc..b65f3e06df9fc 100644 --- a/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client/tests/update.test.ts @@ -436,6 +436,7 @@ describe('update()', () => { recoveryActionGroup: RecoveredActionGroup, defaultActionGroupId: 'default', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'alerts', useSavedObjectReferences: { From d01e3230fef6eb92ea30b8295eb720a7bc4acedd Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 29 Jun 2021 10:55:14 -0400 Subject: [PATCH 19/20] Fixing types --- .../common/fixtures/plugins/alerts/server/alert_types.ts | 1 + 1 file changed, 1 insertion(+) 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 8c57b81952e77..f75f9ce996269 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 @@ -566,6 +566,7 @@ export function defineAlertTypes( ], defaultActionGroupId: 'small', minimumLicenseRequired: 'basic', + isExportable: true, async executor() {}, producer: 'alertsFixture', }; From b3570f5a78dff8333c390606527d2adc170dee2a Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 12 Jul 2021 15:23:27 -0400 Subject: [PATCH 20/20] PR fixes --- .../alerting/server/alerts_client/alerts_client.ts | 8 ++++++-- x-pack/plugins/alerting/server/types.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index 11b1ddf84d2fe..3b121413e489b 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -184,6 +184,7 @@ export interface GetAlertInstanceSummaryParams { dateStart?: string; } +// NOTE: Changing this prefix will require a migration to update the prefix in all existing `rule` saved objects const extractedSavedObjectParamReferenceNamePrefix = 'param:'; const alertingAuthorizationFilterOpts: AlertingAuthorizationFilterOpts = { @@ -1604,7 +1605,10 @@ export class AlertsClient { }; } - private injectReferencesIntoParams( + private injectReferencesIntoParams< + Params extends AlertTypeParams, + ExtractedParams extends AlertTypeParams + >( ruleId: string, ruleType: UntypedNormalizedAlertType, ruleParams: SavedObjectAttributes | undefined, @@ -1621,7 +1625,7 @@ export class AlertsClient { })); return ruleParams && ruleType?.useSavedObjectReferences?.injectReferences ? (ruleType.useSavedObjectReferences.injectReferences( - ruleParams, + ruleParams as ExtractedParams, paramReferences ) as Params) : (ruleParams as Params); diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index a7d2773c3ec4a..b12341a5a602d 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -154,7 +154,7 @@ export interface AlertType< minimumLicenseRequired: LicenseType; useSavedObjectReferences?: { extractReferences: (params: Params) => RuleParamsAndRefs; - injectReferences: (params: SavedObjectAttributes, references: SavedObjectReference[]) => Params; + injectReferences: (params: ExtractedParams, references: SavedObjectReference[]) => Params; }; isExportable: boolean; }