From 4b4ffad257795b6076d7af94999a584ace7f6d59 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 15 Jul 2021 13:36:45 -0400 Subject: [PATCH 1/3] Adding fallback migration function for rule and connector migrations --- .../server/saved_objects/migrations.ts | 89 ++++++++---- .../server/saved_objects/migrations.ts | 129 +++++++++++------- 2 files changed, 142 insertions(+), 76 deletions(-) diff --git a/x-pack/plugins/actions/server/saved_objects/migrations.ts b/x-pack/plugins/actions/server/saved_objects/migrations.ts index 17932b6b90f97..a70e0740a3f2a 100644 --- a/x-pack/plugins/actions/server/saved_objects/migrations.ts +++ b/x-pack/plugins/actions/server/saved_objects/migrations.ts @@ -15,34 +15,49 @@ import { import { RawAction } from '../types'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; -interface ActionsLogMeta extends LogMeta { - migrations: { actionDocument: SavedObjectUnsanitizedDoc }; +// Remove this when we finish updating terminology in the code +type RawConnector = RawAction; + +interface ConnectorLogMeta extends LogMeta { + migrations: { connectorDocument: SavedObjectUnsanitizedDoc }; } -type ActionMigration = ( - doc: SavedObjectUnsanitizedDoc -) => SavedObjectUnsanitizedDoc; +type ConnectorMigration = ( + doc: SavedObjectUnsanitizedDoc +) => SavedObjectUnsanitizedDoc; + +type IsMigrationNeededPredicate = ( + doc: SavedObjectUnsanitizedDoc +) => doc is SavedObjectUnsanitizedDoc; + +interface ConnectorMigrationFns { + esoMigrationFn: SavedObjectMigrationFn; + fallbackMigrationFn: SavedObjectMigrationFn; +} export function getMigrations( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup ): SavedObjectMigrationMap { - const migrationActionsTen = encryptedSavedObjects.createMigration( - (doc): doc is SavedObjectUnsanitizedDoc => + const migrationActionsTen = createMigrationFns( + encryptedSavedObjects, + (doc): doc is SavedObjectUnsanitizedDoc => doc.attributes.config?.hasOwnProperty('casesConfiguration') || doc.attributes.actionTypeId === '.email', pipeMigrations(renameCasesConfigurationObject, addHasAuthConfigurationObject) ); - const migrationActionsEleven = encryptedSavedObjects.createMigration( - (doc): doc is SavedObjectUnsanitizedDoc => + const migrationActionsEleven = createMigrationFns( + encryptedSavedObjects, + (doc): doc is SavedObjectUnsanitizedDoc => doc.attributes.config?.hasOwnProperty('isCaseOwned') || doc.attributes.config?.hasOwnProperty('incidentConfiguration') || doc.attributes.actionTypeId === '.webhook', pipeMigrations(removeCasesFieldMappings, addHasAuthConfigurationObject) ); - const migrationActionsFourteen = encryptedSavedObjects.createMigration( - (doc): doc is SavedObjectUnsanitizedDoc => true, + const migrationActionsFourteen = createMigrationFns( + encryptedSavedObjects, + (doc): doc is SavedObjectUnsanitizedDoc => true, pipeMigrations(addisMissingSecretsField) ); @@ -54,29 +69,29 @@ export function getMigrations( } function executeMigrationWithErrorHandling( - migrationFunc: SavedObjectMigrationFn, + migrationFunctions: ConnectorMigrationFns, version: string ) { - return (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => { + return (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => { try { - return migrationFunc(doc, context); + return migrationFunctions.esoMigrationFn(doc, context); } catch (ex) { - context.log.error( + context.log.error( `encryptedSavedObject ${version} migration failed for action ${doc.id} with error: ${ex.message}`, { migrations: { - actionDocument: doc, + connectorDocument: doc, }, } ); } - return doc; + return migrationFunctions.fallbackMigrationFn(doc, context); }; } function renameCasesConfigurationObject( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { if (!doc.attributes.config?.casesConfiguration) { return doc; } @@ -95,8 +110,8 @@ function renameCasesConfigurationObject( } function removeCasesFieldMappings( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { if ( !doc.attributes.config?.hasOwnProperty('isCaseOwned') && !doc.attributes.config?.hasOwnProperty('incidentConfiguration') @@ -115,8 +130,8 @@ function removeCasesFieldMappings( } const addHasAuthConfigurationObject = ( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc => { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc => { if (doc.attributes.actionTypeId !== '.email' && doc.attributes.actionTypeId !== '.webhook') { return doc; } @@ -134,8 +149,8 @@ const addHasAuthConfigurationObject = ( }; const addisMissingSecretsField = ( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc => { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc => { return { ...doc, attributes: { @@ -145,7 +160,27 @@ const addisMissingSecretsField = ( }; }; -function pipeMigrations(...migrations: ActionMigration[]): ActionMigration { - return (doc: SavedObjectUnsanitizedDoc) => +function pipeMigrations(...migrations: ConnectorMigration[]): ConnectorMigration { + return (doc: SavedObjectUnsanitizedDoc) => migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc); } + +function createMigrationFns( + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, + isMigrationNeededPredicate: IsMigrationNeededPredicate, + migrationFunc: ConnectorMigration +): ConnectorMigrationFns { + return { + esoMigrationFn: encryptedSavedObjects.createMigration( + isMigrationNeededPredicate, + migrationFunc + ), + fallbackMigrationFn: (doc: SavedObjectUnsanitizedDoc) => { + if (!isMigrationNeededPredicate(doc)) { + return doc; + } + + return migrationFunc(doc); + }, + }; +} diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.ts index 8969e3ad0fdef..d985cd38233c3 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.ts @@ -19,34 +19,45 @@ import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objec const SIEM_APP_ID = 'securitySolution'; const SIEM_SERVER_APP_ID = 'siem'; +const SUPPORT_INCIDENTS_ACTION_TYPES = ['.servicenow', '.jira', '.resilient']; export const LEGACY_LAST_MODIFIED_VERSION = 'pre-7.10.0'; -interface AlertLogMeta extends LogMeta { - migrations: { alertDocument: SavedObjectUnsanitizedDoc }; +// Remove this when we finish updating terminology in the code +type RawRule = RawAlert; +type RawRuleAction = RawAlertAction; + +interface RuleLogMeta extends LogMeta { + migrations: { ruleDocument: SavedObjectUnsanitizedDoc }; } -type AlertMigration = ( - doc: SavedObjectUnsanitizedDoc -) => SavedObjectUnsanitizedDoc; +type RuleMigration = ( + doc: SavedObjectUnsanitizedDoc +) => SavedObjectUnsanitizedDoc; -const SUPPORT_INCIDENTS_ACTION_TYPES = ['.servicenow', '.jira', '.resilient']; +type IsMigrationNeededPredicate = ( + doc: SavedObjectUnsanitizedDoc +) => doc is SavedObjectUnsanitizedDoc; -export const isAnyActionSupportIncidents = (doc: SavedObjectUnsanitizedDoc): boolean => +interface RuleMigrationFns { + esoMigrationFn: SavedObjectMigrationFn; + fallbackMigrationFn: SavedObjectMigrationFn; +} + +export const isAnyActionSupportIncidents = (doc: SavedObjectUnsanitizedDoc): boolean => doc.attributes.actions.some((action) => SUPPORT_INCIDENTS_ACTION_TYPES.includes(action.actionTypeId) ); -export const isSecuritySolutionRule = (doc: SavedObjectUnsanitizedDoc): boolean => +export const isSecuritySolutionRule = (doc: SavedObjectUnsanitizedDoc): boolean => doc.attributes.alertTypeId === 'siem.signals'; export function getMigrations( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup ): SavedObjectMigrationMap { - const migrationWhenRBACWasIntroduced = encryptedSavedObjects.createMigration( - function shouldBeMigrated(doc): doc is SavedObjectUnsanitizedDoc { - // migrate all documents in 7.10 in order to add the "meta" RBAC field - return true; - }, + const migrationWhenRBACWasIntroduced = createMigrationFns( + encryptedSavedObjects, + // migrate all documents in 7.10 in order to add the "meta" RBAC field + (doc): doc is SavedObjectUnsanitizedDoc => true, pipeMigrations( markAsLegacyAndChangeConsumer, setAlertIdAsDefaultDedupkeyOnPagerDutyActions, @@ -54,22 +65,22 @@ export function getMigrations( ) ); - const migrationAlertUpdatedAtAndNotifyWhen = encryptedSavedObjects.createMigration< - RawAlert, - RawAlert - >( + const migrationAlertUpdatedAtAndNotifyWhen = createMigrationFns( + encryptedSavedObjects, // migrate all documents in 7.11 in order to add the "updatedAt" and "notifyWhen" fields - (doc): doc is SavedObjectUnsanitizedDoc => true, + (doc): doc is SavedObjectUnsanitizedDoc => true, pipeMigrations(setAlertUpdatedAtDate, setNotifyWhen) ); - const migrationActions7112 = encryptedSavedObjects.createMigration( - (doc): doc is SavedObjectUnsanitizedDoc => isAnyActionSupportIncidents(doc), + const migrationActions7112 = createMigrationFns( + encryptedSavedObjects, + (doc): doc is SavedObjectUnsanitizedDoc => isAnyActionSupportIncidents(doc), pipeMigrations(restructureConnectorsThatSupportIncident) ); - const migrationSecurityRules713 = encryptedSavedObjects.createMigration( - (doc): doc is SavedObjectUnsanitizedDoc => isSecuritySolutionRule(doc), + const migrationSecurityRules713 = createMigrationFns( + encryptedSavedObjects, + (doc): doc is SavedObjectUnsanitizedDoc => isSecuritySolutionRule(doc), pipeMigrations(removeNullsFromSecurityRules) ); @@ -82,29 +93,29 @@ export function getMigrations( } function executeMigrationWithErrorHandling( - migrationFunc: SavedObjectMigrationFn, + migrationFunctions: RuleMigrationFns, version: string ) { - return (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => { + return (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => { try { - return migrationFunc(doc, context); + return migrationFunctions.esoMigrationFn(doc, context); } catch (ex) { - context.log.error( - `encryptedSavedObject ${version} migration failed for alert ${doc.id} with error: ${ex.message}`, + context.log.error( + `encryptedSavedObject ${version} migration failed for rule ${doc.id} with error: ${ex.message}`, { migrations: { - alertDocument: doc, + ruleDocument: doc, }, } ); } - return doc; + return migrationFunctions.fallbackMigrationFn(doc, context); }; } const setAlertUpdatedAtDate = ( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc => { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc => { const updatedAt = doc.updated_at || doc.attributes.createdAt; return { ...doc, @@ -116,8 +127,8 @@ const setAlertUpdatedAtDate = ( }; const setNotifyWhen = ( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc => { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc => { const notifyWhen = doc.attributes.throttle ? 'onThrottleInterval' : 'onActiveAlert'; return { ...doc, @@ -137,8 +148,8 @@ const consumersToChange: Map = new Map( ); function markAsLegacyAndChangeConsumer( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { const { attributes: { consumer }, } = doc; @@ -156,8 +167,8 @@ function markAsLegacyAndChangeConsumer( } function setAlertIdAsDefaultDedupkeyOnPagerDutyActions( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { const { attributes } = doc; return { ...doc, @@ -184,8 +195,8 @@ function setAlertIdAsDefaultDedupkeyOnPagerDutyActions( } function initializeExecutionStatus( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { const { attributes } = doc; return { ...doc, @@ -210,8 +221,8 @@ function isEmptyObject(obj: {}) { } function restructureConnectorsThatSupportIncident( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { const { actions } = doc.attributes; const newActions = actions.reduce((acc, action) => { if ( @@ -268,7 +279,7 @@ function restructureConnectorsThatSupportIncident( }, }, }, - ] as RawAlertAction[]; + ] as RawRuleAction[]; } else if (action.actionTypeId === '.jira') { const { title, @@ -308,7 +319,7 @@ function restructureConnectorsThatSupportIncident( }, }, }, - ] as RawAlertAction[]; + ] as RawRuleAction[]; } else if (action.actionTypeId === '.resilient') { const { title, comments, description, incidentTypes, severityCode, name } = action.params .subActionParams as { @@ -336,12 +347,12 @@ function restructureConnectorsThatSupportIncident( }, }, }, - ] as RawAlertAction[]; + ] as RawRuleAction[]; } } return [...acc, action]; - }, [] as RawAlertAction[]); + }, [] as RawRuleAction[]); return { ...doc, @@ -357,8 +368,8 @@ function convertNullToUndefined(attribute: SavedObjectAttribute) { } function removeNullsFromSecurityRules( - doc: SavedObjectUnsanitizedDoc -): SavedObjectUnsanitizedDoc { + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { const { attributes: { params }, } = doc; @@ -420,7 +431,27 @@ function removeNullsFromSecurityRules( }; } -function pipeMigrations(...migrations: AlertMigration[]): AlertMigration { - return (doc: SavedObjectUnsanitizedDoc) => +function pipeMigrations(...migrations: RuleMigration[]): RuleMigration { + return (doc: SavedObjectUnsanitizedDoc) => migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc); } + +function createMigrationFns( + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup, + isMigrationNeededPredicate: IsMigrationNeededPredicate, + migrationFunc: RuleMigration +): RuleMigrationFns { + return { + esoMigrationFn: encryptedSavedObjects.createMigration( + isMigrationNeededPredicate, + migrationFunc + ), + fallbackMigrationFn: (doc: SavedObjectUnsanitizedDoc) => { + if (!isMigrationNeededPredicate(doc)) { + return doc; + } + + return migrationFunc(doc); + }, + }; +} From 592e6b4d52e028384c4d0eb34275d49241927e49 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 15 Jul 2021 15:11:45 -0400 Subject: [PATCH 2/3] Adding unit tests for migrations to ensure they are migrated even if decryption fails --- .../server/saved_objects/migrations.test.ts | 153 +++--- .../server/saved_objects/migrations.ts | 2 +- .../server/saved_objects/migrations.test.ts | 486 +++++++++--------- 3 files changed, 337 insertions(+), 304 deletions(-) diff --git a/x-pack/plugins/actions/server/saved_objects/migrations.test.ts b/x-pack/plugins/actions/server/saved_objects/migrations.test.ts index 4c30925e61894..b51cb6a08f10a 100644 --- a/x-pack/plugins/actions/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/actions/server/saved_objects/migrations.test.ts @@ -13,124 +13,145 @@ import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/serv import { migrationMocks } from 'src/core/server/mocks'; const context = migrationMocks.createContext(); -const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); +const encryptedSavedObjectsSetupNoError = encryptedSavedObjectsMock.createSetup(); +const encryptedSavedObjectsSetupThrowsError = encryptedSavedObjectsMock.createSetup(); -describe('7.10.0', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementation( - (shouldMigrateWhenPredicate, migration) => migration - ); +beforeAll(() => { + encryptedSavedObjectsSetupNoError.createMigration.mockImplementation((_, migration) => migration); + encryptedSavedObjectsSetupThrowsError.createMigration.mockImplementation(() => () => { + throw new Error(`Can't migrate!`); }); +}); + +function testMigrationWhenNoEsoErrors( + connector: SavedObjectUnsanitizedDoc>, + expectedMigratedConnector: SavedObjectUnsanitizedDoc>, + version: string +) { + // should migrate correctly when no decrypt errors + expect( + getMigrations(encryptedSavedObjectsSetupNoError)[version](connector, context) + ).toMatchObject(expectedMigratedConnector); +} + +function testMigrationWhenEsoThrowsError( + connector: SavedObjectUnsanitizedDoc>, + expectedMigratedConnector: SavedObjectUnsanitizedDoc>, + version: string +) { + // should log error when decryption throws error but migrated correctly + expect( + getMigrations(encryptedSavedObjectsSetupThrowsError)[version](connector, context) + ).toMatchObject(expectedMigratedConnector); + expect(context.log.error).toHaveBeenCalledWith( + `encryptedSavedObject ${version} migration failed for connector ${connector.id} with error: Can't migrate!`, + { + migrations: { + connectorDocument: connector, + }, + } + ); +} +describe('7.10.0', () => { test('add hasAuth config property for .email actions', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const action = getMockDataForEmail({}); - const migratedAction = migration710(action, context); - expect(migratedAction.attributes.config).toEqual({ - hasAuth: true, - }); - expect(migratedAction).toEqual({ - ...action, + const connector = getMockDataForEmail({}); + const expectedMigratedConnector: SavedObjectUnsanitizedDoc> = { + ...connector, attributes: { - ...action.attributes, + ...connector.attributes, config: { hasAuth: true, }, }, - }); + }; + + testMigrationWhenNoEsoErrors(connector, expectedMigratedConnector, '7.10.0'); + testMigrationWhenEsoThrowsError(connector, expectedMigratedConnector, '7.10.0'); }); test('rename cases configuration object', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const action = getCasesMockData({}); - const migratedAction = migration710(action, context); - expect(migratedAction.attributes.config).toEqual({ - incidentConfiguration: { mapping: [] }, - }); - expect(migratedAction).toEqual({ - ...action, + const connector = getCasesMockData({}); + const expectedMigratedConnector: SavedObjectUnsanitizedDoc> = { + ...connector, attributes: { - ...action.attributes, + ...connector.attributes, config: { incidentConfiguration: { mapping: [] }, }, }, - }); + }; + + testMigrationWhenNoEsoErrors(connector, expectedMigratedConnector, '7.10.0'); + testMigrationWhenEsoThrowsError(connector, expectedMigratedConnector, '7.10.0'); }); }); describe('7.11.0', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementation( - (shouldMigrateWhenPredicate, migration) => migration - ); - }); - test('add hasAuth = true for .webhook actions with user and password', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const action = getMockDataForWebhook({}, true); - expect(migration711(action, context)).toMatchObject({ - ...action, + const connector = getMockDataForWebhook({}, true); + const expectedMigratedConnector: SavedObjectUnsanitizedDoc> = { + ...connector, attributes: { - ...action.attributes, + ...connector.attributes, config: { hasAuth: true, }, }, - }); + }; + + testMigrationWhenNoEsoErrors(connector, expectedMigratedConnector, '7.11.0'); + testMigrationWhenEsoThrowsError(connector, expectedMigratedConnector, '7.11.0'); }); test('add hasAuth = false for .webhook actions without user and password', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const action = getMockDataForWebhook({}, false); - expect(migration711(action, context)).toMatchObject({ - ...action, + const connector = getMockDataForWebhook({}, false); + const expectedMigratedConnector: SavedObjectUnsanitizedDoc> = { + ...connector, attributes: { - ...action.attributes, + ...connector.attributes, config: { hasAuth: false, }, }, - }); + }; + + testMigrationWhenNoEsoErrors(connector, expectedMigratedConnector, '7.11.0'); + testMigrationWhenEsoThrowsError(connector, expectedMigratedConnector, '7.11.0'); }); + test('remove cases mapping object', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const action = getMockData({ + const connector = getMockData({ config: { incidentConfiguration: { mapping: [] }, isCaseOwned: true, another: 'value' }, }); - expect(migration711(action, context)).toEqual({ - ...action, + const expectedMigratedConnector: SavedObjectUnsanitizedDoc> = { + ...connector, attributes: { - ...action.attributes, + ...connector.attributes, config: { another: 'value', }, }, - }); + }; + + testMigrationWhenNoEsoErrors(connector, expectedMigratedConnector, '7.11.0'); + testMigrationWhenEsoThrowsError(connector, expectedMigratedConnector, '7.11.0'); }); }); describe('7.14.0', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementation( - (shouldMigrateWhenPredicate, migration) => migration - ); - }); - test('add isMissingSecrets property for actions', () => { - const migration714 = getMigrations(encryptedSavedObjectsSetup)['7.14.0']; - const action = getMockData({ isMissingSecrets: undefined }); - const migratedAction = migration714(action, context); - expect(migratedAction).toEqual({ - ...action, + const connector = getMockData({ isMissingSecrets: undefined }); + const expectedMigratedConnector: SavedObjectUnsanitizedDoc> = { + ...connector, attributes: { - ...action.attributes, + ...connector.attributes, isMissingSecrets: false, }, - }); + }; + + testMigrationWhenNoEsoErrors(connector, expectedMigratedConnector, '7.14.0'); + testMigrationWhenEsoThrowsError(connector, expectedMigratedConnector, '7.14.0'); }); }); diff --git a/x-pack/plugins/actions/server/saved_objects/migrations.ts b/x-pack/plugins/actions/server/saved_objects/migrations.ts index a70e0740a3f2a..64ee05b5c8f16 100644 --- a/x-pack/plugins/actions/server/saved_objects/migrations.ts +++ b/x-pack/plugins/actions/server/saved_objects/migrations.ts @@ -77,7 +77,7 @@ function executeMigrationWithErrorHandling( return migrationFunctions.esoMigrationFn(doc, context); } catch (ex) { context.log.error( - `encryptedSavedObject ${version} migration failed for action ${doc.id} with error: ${ex.message}`, + `encryptedSavedObject ${version} migration failed for connector ${doc.id} with error: ${ex.message}`, { migrations: { connectorDocument: doc, diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts index 4888116e43602..f33b5c50f5931 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import sinon from 'sinon'; import uuid from 'uuid'; import { getMigrations, isAnyActionSupportIncidents } from './migrations'; import { RawAlert } from '../types'; @@ -13,88 +14,132 @@ import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/serv import { migrationMocks } from 'src/core/server/mocks'; const migrationContext = migrationMocks.createContext(); -const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); +const encryptedSavedObjectsSetupNoError = encryptedSavedObjectsMock.createSetup(); +const encryptedSavedObjectsSetupThrowsError = encryptedSavedObjectsMock.createSetup(); -describe('7.10.0', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementation( - (shouldMigrateWhenPredicate, migration) => migration - ); +let clock: sinon.SinonFakeTimers; + +beforeAll(() => { + clock = sinon.useFakeTimers(); + encryptedSavedObjectsSetupNoError.createMigration.mockImplementation((_, migration) => migration); + encryptedSavedObjectsSetupThrowsError.createMigration.mockImplementation(() => () => { + throw new Error(`Can't migrate!`); }); +}); +beforeEach(() => clock.reset()); +afterAll(() => clock.restore()); + +function testMigrationWhenNoEsoErrors( + rule: SavedObjectUnsanitizedDoc>, + expectedMigratedRule: SavedObjectUnsanitizedDoc>, + version: string +) { + // should migrate correctly when no decrypt errors + expect( + getMigrations(encryptedSavedObjectsSetupNoError)[version](rule, migrationContext) + ).toMatchObject(expectedMigratedRule); +} + +function testMigrationWhenEsoThrowsError( + rule: SavedObjectUnsanitizedDoc>, + expectedMigratedRule: SavedObjectUnsanitizedDoc>, + version: string +) { + // should log error when decryption throws error but migrated correctly + expect( + getMigrations(encryptedSavedObjectsSetupThrowsError)[version](rule, migrationContext) + ).toMatchObject(expectedMigratedRule); + expect(migrationContext.log.error).toHaveBeenCalledWith( + `encryptedSavedObject ${version} migration failed for rule ${rule.id} with error: Can't migrate!`, + { + migrations: { + ruleDocument: rule, + }, + } + ); +} - test('marks alerts as legacy', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData({}); - expect(migration710(alert, migrationContext)).toMatchObject({ - ...alert, +describe('7.10.0', () => { + test('marks rules as legacy', () => { + const rule = getMockData({}); + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, meta: { versionApiKeyLastmodified: 'pre-7.10.0', }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.10.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.10.0'); }); test('migrates the consumer for metrics', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData({ + const rule = getMockData({ consumer: 'metrics', }); - expect(migration710(alert, migrationContext)).toMatchObject({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, consumer: 'infrastructure', meta: { versionApiKeyLastmodified: 'pre-7.10.0', }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.10.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.10.0'); }); test('migrates the consumer for siem', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData({ + const rule = getMockData({ consumer: 'securitySolution', }); - expect(migration710(alert, migrationContext)).toMatchObject({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, consumer: 'siem', meta: { versionApiKeyLastmodified: 'pre-7.10.0', }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.10.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.10.0'); }); test('migrates the consumer for alerting', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData({ + const rule = getMockData({ consumer: 'alerting', }); - expect(migration710(alert, migrationContext)).toMatchObject({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, consumer: 'alerts', meta: { versionApiKeyLastmodified: 'pre-7.10.0', }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.10.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.10.0'); }); test('migrates PagerDuty actions to set a default dedupkey of the AlertId', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData({ + const rule = getMockData({ actions: [ { actionTypeId: '.pagerduty', group: 'default', + actionRef: '', params: { summary: 'fired {{alertInstanceId}}', eventAction: 'resolve', @@ -104,14 +149,15 @@ describe('7.10.0', () => { }, ], }); - expect(migration710(alert, migrationContext)).toMatchObject({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, actions: [ { actionTypeId: '.pagerduty', group: 'default', + actionRef: '', params: { summary: 'fired {{alertInstanceId}}', eventAction: 'resolve', @@ -122,16 +168,19 @@ describe('7.10.0', () => { }, ], }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.10.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.10.0'); }); test('skips PagerDuty actions with a specified dedupkey', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData({ + const rule = getMockData({ actions: [ { actionTypeId: '.pagerduty', group: 'default', + actionRef: '', params: { summary: 'fired {{alertInstanceId}}', eventAction: 'trigger', @@ -142,14 +191,15 @@ describe('7.10.0', () => { }, ], }); - expect(migration710(alert, migrationContext)).toMatchObject({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, actions: [ { actionTypeId: '.pagerduty', group: 'default', + actionRef: '', params: { summary: 'fired {{alertInstanceId}}', eventAction: 'trigger', @@ -160,16 +210,18 @@ describe('7.10.0', () => { }, ], }, - }); + }; + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.10.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.10.0'); }); test('skips PagerDuty actions with an eventAction of "trigger"', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData({ + const rule = getMockData({ actions: [ { actionTypeId: '.pagerduty', group: 'default', + actionRef: '', params: { summary: 'fired {{alertInstanceId}}', eventAction: 'trigger', @@ -179,10 +231,10 @@ describe('7.10.0', () => { }, ], }); - expect(migration710(alert, migrationContext)).toMatchObject({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, meta: { versionApiKeyLastmodified: 'pre-7.10.0', }, @@ -190,6 +242,7 @@ describe('7.10.0', () => { { actionTypeId: '.pagerduty', group: 'default', + actionRef: '', params: { summary: 'fired {{alertInstanceId}}', eventAction: 'trigger', @@ -199,144 +252,96 @@ describe('7.10.0', () => { }, ], }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.10.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.10.0'); }); test('creates execution status', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData(); - const dateStart = Date.now(); - const migratedAlert = migration710(alert, migrationContext); - const dateStop = Date.now(); - const dateExecutionStatus = Date.parse( - migratedAlert.attributes.executionStatus.lastExecutionDate - ); - - expect(dateStart).toBeLessThanOrEqual(dateExecutionStatus); - expect(dateStop).toBeGreaterThanOrEqual(dateExecutionStatus); - - expect(migratedAlert).toMatchObject({ - ...alert, + const rule = getMockData(); + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, executionStatus: { - lastExecutionDate: migratedAlert.attributes.executionStatus.lastExecutionDate, + lastExecutionDate: '1970-01-01T00:00:00.000Z', status: 'pending', error: null, }, }, - }); - }); -}); - -describe('7.10.0 migrates with failure', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementationOnce(() => () => { - throw new Error(`Can't migrate!`); - }); - }); + }; - test('should show the proper exception', () => { - const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; - const alert = getMockData({ - consumer: 'alerting', - }); - const res = migration710(alert, migrationContext); - expect(res).toMatchObject({ - ...alert, - attributes: { - ...alert.attributes, - }, - }); - expect(migrationContext.log.error).toHaveBeenCalledWith( - `encryptedSavedObject 7.10.0 migration failed for alert ${alert.id} with error: Can't migrate!`, - { - migrations: { - alertDocument: { - ...alert, - attributes: { - ...alert.attributes, - }, - }, - }, - } - ); + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.10.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.10.0'); }); }); describe('7.11.0', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementation( - (shouldMigrateWhenPredicate, migration) => migration - ); - }); - - test('add updatedAt field to alert - set to SavedObject updated_at attribute', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const alert = getMockData({}, true); - expect(migration711(alert, migrationContext)).toEqual({ - ...alert, + test('add updatedAt field to rule - set to SavedObject updated_at attribute', () => { + const rule = getMockData({}, true); + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, - updatedAt: alert.updated_at, + ...rule.attributes, + updatedAt: rule.updated_at, notifyWhen: 'onActiveAlert', }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.11.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.11.0'); }); - test('add updatedAt field to alert - set to createdAt when SavedObject updated_at is not defined', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const alert = getMockData({}); - expect(migration711(alert, migrationContext)).toEqual({ - ...alert, + test('add updatedAt field to rule - set to createdAt when SavedObject updated_at is not defined', () => { + const rule = getMockData({}); + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, - updatedAt: alert.attributes.createdAt, + ...rule.attributes, + updatedAt: rule.attributes.createdAt, notifyWhen: 'onActiveAlert', }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.11.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.11.0'); }); test('add notifyWhen=onActiveAlert when throttle is null', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const alert = getMockData({}); - expect(migration711(alert, migrationContext)).toEqual({ - ...alert, + const rule = getMockData({}); + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, - updatedAt: alert.attributes.createdAt, + ...rule.attributes, + updatedAt: rule.attributes.createdAt, notifyWhen: 'onActiveAlert', }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.11.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.11.0'); }); test('add notifyWhen=onActiveAlert when throttle is set', () => { - const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0']; - const alert = getMockData({ throttle: '5m' }); - expect(migration711(alert, migrationContext)).toEqual({ - ...alert, + const rule = getMockData({ throttle: '5m' }); + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, - updatedAt: alert.attributes.createdAt, + ...rule.attributes, + updatedAt: rule.attributes.createdAt, notifyWhen: 'onThrottleInterval', }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.11.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.11.0'); }); }); describe('7.11.2', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementation( - (shouldMigrateWhenPredicate, migration) => migration - ); - }); - test('transforms connectors that support incident correctly', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; - const alert = getMockData({ + const rule = getMockData({ actions: [ { actionTypeId: '.jira', @@ -401,11 +406,10 @@ describe('7.11.2', () => { }, ], }); - - expect(migration7112(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, actions: [ { actionTypeId: '.jira', @@ -473,12 +477,14 @@ describe('7.11.2', () => { }, ], }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.11.2'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.11.2'); }); test('it transforms only subAction=pushToService', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; - const alert = getMockData({ + const rule = getMockData({ actions: [ { actionTypeId: '.jira', @@ -492,16 +498,17 @@ describe('7.11.2', () => { ], }); - expect(migration7112(alert, migrationContext)).toEqual(alert); + testMigrationWhenNoEsoErrors(rule, rule, '7.11.2'); + testMigrationWhenEsoThrowsError(rule, rule, '7.11.2'); }); test('it does not transforms other connectors', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; - const alert = getMockData({ + const rule = getMockData({ actions: [ { actionTypeId: '.server-log', group: 'threshold met', + actionRef: '', params: { level: 'info', message: 'log message', @@ -511,6 +518,7 @@ describe('7.11.2', () => { { actionTypeId: '.servicenow', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -527,16 +535,16 @@ describe('7.11.2', () => { }, ], }); - - expect(migration7112(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, actions: [ - alert.attributes.actions![0], + rule.attributes.actions![0], { actionTypeId: '.servicenow', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -554,7 +562,10 @@ describe('7.11.2', () => { }, ], }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.11.2'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.11.2'); }); test.each(['.jira', '.servicenow', '.resilient'])( @@ -574,9 +585,8 @@ describe('7.11.2', () => { expect(isAnyActionSupportIncidents(doc)).toBe(false); }); - test('it does not transforms alerts when the right structure connectors is already applied', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; - const alert = getMockData({ + test('it does not transforms rules when the right structure connectors is already applied', () => { + const rule = getMockData({ actions: [ { actionTypeId: '.server-log', @@ -608,12 +618,12 @@ describe('7.11.2', () => { ], }); - expect(migration7112(alert, migrationContext)).toEqual(alert); + testMigrationWhenNoEsoErrors(rule, rule, '7.11.2'); + testMigrationWhenEsoThrowsError(rule, rule, '7.11.2'); }); test('if incident attribute is an empty object, copy back the related attributes from subActionParams back to incident', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; - const alert = getMockData({ + const rule = getMockData({ actions: [ { actionTypeId: '.server-log', @@ -643,13 +653,12 @@ describe('7.11.2', () => { }, ], }); - - expect(migration7112(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, actions: [ - alert.attributes.actions![0], + rule.attributes.actions![0], { actionTypeId: '.servicenow', group: 'threshold met', @@ -670,12 +679,14 @@ describe('7.11.2', () => { }, ], }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.11.2'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.11.2'); }); test('custom action does not get migrated/loss', () => { - const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2']; - const alert = getMockData({ + const rule = getMockData({ actions: [ { actionTypeId: '.mike', @@ -697,20 +708,14 @@ describe('7.11.2', () => { ], }); - expect(migration7112(alert, migrationContext)).toEqual(alert); + testMigrationWhenNoEsoErrors(rule, rule, '7.11.2'); + testMigrationWhenEsoThrowsError(rule, rule, '7.11.2'); }); }); describe('7.13.0', () => { - beforeEach(() => { - jest.resetAllMocks(); - encryptedSavedObjectsSetup.createMigration.mockImplementation( - (shouldMigrateWhenPredicate, migration) => migration - ); - }); - test('security solution alerts get migrated and remove null values', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0']; - const alert = getMockData({ + test('security solution rules get migrated and remove null values', () => { + const rule = getMockData({ alertTypeId: 'siem.signals', params: { author: ['Elastic'], @@ -758,11 +763,10 @@ describe('7.13.0', () => { }, }, }); - - expect(migration713(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, params: { author: ['Elastic'], description: @@ -801,12 +805,14 @@ describe('7.13.0', () => { }, }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.13.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.13.0'); }); - test('non-null values in security solution alerts are not modified', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0']; - const alert = getMockData({ + test('non-null values in security solution rules are not modified', () => { + const rule = getMockData({ alertTypeId: 'siem.signals', params: { author: ['Elastic'], @@ -869,12 +875,12 @@ describe('7.13.0', () => { }, }); - expect(migration713(alert, migrationContext)).toEqual(alert); + testMigrationWhenNoEsoErrors(rule, rule, '7.13.0'); + testMigrationWhenEsoThrowsError(rule, rule, '7.13.0'); }); - test('security solution threshold alert with string in threshold.field is migrated to array', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0']; - const alert = getMockData({ + test('security solution threshold rule with string in threshold.field is migrated to array', () => { + const rule = getMockData({ alertTypeId: 'siem.signals', params: { threshold: { @@ -883,11 +889,10 @@ describe('7.13.0', () => { }, }, }); - - expect(migration713(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, params: { threshold: { field: ['host.id'], @@ -900,12 +905,14 @@ describe('7.13.0', () => { threat: [], }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.13.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.13.0'); }); - test('security solution threshold alert with empty string in threshold.field is migrated to empty array', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0']; - const alert = getMockData({ + test('security solution threshold rule with empty string in threshold.field is migrated to empty array', () => { + const rule = getMockData({ alertTypeId: 'siem.signals', params: { threshold: { @@ -914,11 +921,10 @@ describe('7.13.0', () => { }, }, }); - - expect(migration713(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, params: { threshold: { field: [], @@ -931,12 +937,14 @@ describe('7.13.0', () => { threat: [], }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.13.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.13.0'); }); - test('security solution threshold alert with array in threshold.field and cardinality is left alone', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0']; - const alert = getMockData({ + test('security solution threshold rule with array in threshold.field and cardinality is left alone', () => { + const rule = getMockData({ alertTypeId: 'siem.signals', params: { threshold: { @@ -951,11 +959,10 @@ describe('7.13.0', () => { }, }, }); - - expect(migration713(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, params: { threshold: { field: ['host.id'], @@ -973,23 +980,24 @@ describe('7.13.0', () => { threat: [], }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.13.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.13.0'); }); - test('security solution ML alert with string in machineLearningJobId is converted to an array', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0']; - const alert = getMockData({ + test('security solution ML rule with string in machineLearningJobId is converted to an array', () => { + const rule = getMockData({ alertTypeId: 'siem.signals', params: { anomalyThreshold: 20, machineLearningJobId: 'my_job_id', }, }); - - expect(migration713(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, params: { anomalyThreshold: 20, machineLearningJobId: ['my_job_id'], @@ -999,23 +1007,24 @@ describe('7.13.0', () => { threat: [], }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.13.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.13.0'); }); - test('security solution ML alert with an array in machineLearningJobId is preserved', () => { - const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0']; - const alert = getMockData({ + test('security solution ML rule with an array in machineLearningJobId is preserved', () => { + const rule = getMockData({ alertTypeId: 'siem.signals', params: { anomalyThreshold: 20, machineLearningJobId: ['my_job_id', 'my_other_job_id'], }, }); - - expect(migration713(alert, migrationContext)).toEqual({ - ...alert, + const expectedMigratedRule: SavedObjectUnsanitizedDoc> = { + ...rule, attributes: { - ...alert.attributes, + ...rule.attributes, params: { anomalyThreshold: 20, machineLearningJobId: ['my_job_id', 'my_other_job_id'], @@ -1025,7 +1034,10 @@ describe('7.13.0', () => { threat: [], }, }, - }); + }; + + testMigrationWhenNoEsoErrors(rule, expectedMigratedRule, '7.13.0'); + testMigrationWhenEsoThrowsError(rule, expectedMigratedRule, '7.13.0'); }); }); From 93e6513cf59313b7f5fb89891063082e73c2997c Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 15 Jul 2021 16:33:42 -0400 Subject: [PATCH 3/3] Fixing types --- .../alerting/server/saved_objects/migrations.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts index f33b5c50f5931..94d1aea6323f0 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations.test.ts @@ -346,6 +346,7 @@ describe('7.11.2', () => { { actionTypeId: '.jira', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -369,6 +370,7 @@ describe('7.11.2', () => { { actionTypeId: '.resilient', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -390,6 +392,7 @@ describe('7.11.2', () => { { actionTypeId: '.servicenow', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -414,6 +417,7 @@ describe('7.11.2', () => { { actionTypeId: '.jira', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -438,6 +442,7 @@ describe('7.11.2', () => { { actionTypeId: '.resilient', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -460,6 +465,7 @@ describe('7.11.2', () => { { actionTypeId: '.servicenow', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -628,6 +634,7 @@ describe('7.11.2', () => { { actionTypeId: '.server-log', group: 'threshold met', + actionRef: '', params: { level: 'info', message: 'log message', @@ -637,6 +644,7 @@ describe('7.11.2', () => { { actionTypeId: '.servicenow', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: { @@ -662,6 +670,7 @@ describe('7.11.2', () => { { actionTypeId: '.servicenow', group: 'threshold met', + actionRef: '', params: { subAction: 'pushToService', subActionParams: {