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 c81fa7927ef7df..8fe466491ae8f2 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 f8846035e6b02d..898028549b1e05 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 8fe466491ae8f2..2382c2397d9024 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 a2d5a5e0386c4e..85873165008023 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 c743312ef2c4b4..469c704bb1c792 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 898028549b1e05..0e4bd6e1008153 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 6e9ec0d367c9a5..01f7de3514197a 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 45ea6b48bf6f4f..f157e9353eefdf 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 cb43e534080905..74dc3180e84260 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 7f34760c73199c..ab494a20692879 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 21feb769267914..f499700b09d84d 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 72e3325107f316..4c759434ef1edb 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 a1c326656f735a..b4e9b265494810 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 dc5d9d278b6b5f..837fecde11659c 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 4e9249944a6bf9..0894558ee5668c 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 769243b8feaf6a..1ba12f69285e03 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 5ab25fbfa39e7a..adad5ec9ab5ca1 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 ef93179bdaba16..c51ba5fd38e5b8 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 fd82b38b493d79..8afc5fbe0bdbb1 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 a023776134e9cf..adfd4c9107c333 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 0e4bd6e1008153..b73a78b089c9d8 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 81204c7b71a688..912ea716f9e640 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 2adf37c60cf3a9..b15bbcce5a487a 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 7dbae03f20fbd6..8b47620468eafc 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 bb80d84210a481..6b830e8dc3f6db 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 959c05fd1334e2..8d6ab51bb36016 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 0a32e0c21a075a..50ad98865544ea 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 b81bc19d5c731e..af49ecb3d9bf40 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 e3d379f2869b91..23a22461c09d6b 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 a242c1e0eb29e3..68de5e0a58f957 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 d32e7890b17c60..6a81a1214d67b1 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 9725d901383002..b78aa4e6432d5e 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 29f2f0cca82bcd..20b65a20c5883f 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 6f9ca42e54adc7..00d233b48f362a 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 3e622b49d03df7..cbe5c75812599d 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 884af7801855f9..fb4019b8c7e4a2 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 10a81e43090886..5858a8a0dfe0a1 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 f499700b09d84d..b0f08fa0506993 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 2382c2397d9024..ae10897727bb93 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 85873165008023..b2435e23b24618 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 469c704bb1c792..fb7646480e9eb4 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 adfd4c9107c333..bf91954fa964c1 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 b7c6881b20390c..6371cf9cc3a591 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 f39b3850b71b14..b051f9983cbb17 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 ee0b36bcd17668..023ea168a77d2a 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 f499700b09d84d..b0f08fa0506993 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 2382c2397d9024..ae10897727bb93 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 85873165008023..b2435e23b24618 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 469c704bb1c792..fb7646480e9eb4 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 adfd4c9107c333..bf91954fa964c1 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 b7c6881b20390c..6371cf9cc3a591 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 f39b3850b71b14..b051f9983cbb17 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 4205c2d6d8b2c5..8724f2cc822fcb 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 ee0b36bcd17668..023ea168a77d2a 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 ae10897727bb93..cb85c972ee7037 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 b2435e23b24618..374140f03f7294 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 fb7646480e9eb4..c03ae6c3c5bf7d 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 b73a78b089c9d8..f5a03eae4a8c04 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 374140f03f7294..b14f3cf4e8fa0e 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 8fa8ae7ae38b09..3ae507a3ed9ad4 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 a958ea4061ae5e..d7ad255568010c 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 c03ae6c3c5bf7d..2ebb789e52421b 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 3ae507a3ed9ad4..be4b93f7fd6d30 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 68de5e0a58f957..008eb2944ff2bc 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 6a81a1214d67b1..d32e7890b17c60 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 9d93ea6ba7c27c..bd106370c9770c 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 cb85c972ee7037..cc73d06fcd3161 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 cbe5c75812599d..2b451c4940da85 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 be4b93f7fd6d30..c1c97dbde97562 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 2b451c4940da85..fbe3f73c233e64 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 7d21b71a5a61d6..394f4fbc6a0f83 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 cc73d06fcd3161..794dfd5469a4d2 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 394f4fbc6a0f83..319956dac33ba7 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 fcd8fa33c91883..11b1ddf84d2fef 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 e635e12cb3dab5..b54770f0e49b20 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 6402d10b6b11fa..f77cfcfd59d916 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 43ef552827afb5..0c3c03f2715d9e 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 168482e7f376b8..ff262c21e1df3b 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 00d782bdc3ccd4..3275e25b85df5b 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 01865a086443af..0633dda8e7a59f 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 0c3c03f2715d9e..82a8acefb386df 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 eb4fa8ccec4bc1..b65f3e06df9fcd 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 8c57b81952e77e..f75f9ce9962692 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 11b1ddf84d2fef..3b121413e489bd 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 a7d2773c3ec4a1..b12341a5a602da 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; }