[Alerting] renames Resolved action group to Recovered (#84123)

This PR changes the default term from “Resolved” to “Recovered”, as it fits most use cases and we feel users are most likely to understand its meaning across domains.
This commit is contained in:
Gidi Meir Morris 2020-12-01 10:38:28 +00:00 committed by GitHub
parent 4507865d10
commit 7dcaff5ddd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 147 additions and 90 deletions

View file

@ -6,13 +6,13 @@
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { ActionGroup } from './alert_type'; import { ActionGroup } from './alert_type';
export const ResolvedActionGroup: ActionGroup = { export const RecoveredActionGroup: ActionGroup = {
id: 'resolved', id: 'recovered',
name: i18n.translate('xpack.alerts.builtinActionGroups.resolved', { name: i18n.translate('xpack.alerts.builtinActionGroups.recovered', {
defaultMessage: 'Resolved', defaultMessage: 'Recovered',
}), }),
}; };
export function getBuiltinActionGroups(): ActionGroup[] { export function getBuiltinActionGroups(): ActionGroup[] {
return [ResolvedActionGroup]; return [RecoveredActionGroup];
} }

View file

@ -105,8 +105,8 @@ describe('register()', () => {
name: 'Default', name: 'Default',
}, },
{ {
id: 'resolved', id: 'recovered',
name: 'Resolved', name: 'Recovered',
}, },
], ],
defaultActionGroupId: 'default', defaultActionGroupId: 'default',
@ -117,7 +117,7 @@ describe('register()', () => {
expect(() => registry.register(alertType)).toThrowError( expect(() => registry.register(alertType)).toThrowError(
new Error( new Error(
`Alert type [id="${alertType.id}"] cannot be registered. Action groups [resolved] are reserved by the framework.` `Alert type [id="${alertType.id}"] cannot be registered. Action groups [recovered] are reserved by the framework.`
) )
); );
}); });
@ -229,8 +229,8 @@ describe('get()', () => {
"name": "Default", "name": "Default",
}, },
Object { Object {
"id": "resolved", "id": "recovered",
"name": "Resolved", "name": "Recovered",
}, },
], ],
"actionVariables": Object { "actionVariables": Object {
@ -287,8 +287,8 @@ describe('list()', () => {
"name": "Test Action Group", "name": "Test Action Group",
}, },
Object { Object {
"id": "resolved", "id": "recovered",
"name": "Resolved", "name": "Recovered",
}, },
], ],
"actionVariables": Object { "actionVariables": Object {

View file

@ -122,7 +122,7 @@ describe('getAlertInstanceSummary()', () => {
.addActiveInstance('instance-previously-active', 'action group B') .addActiveInstance('instance-previously-active', 'action group B')
.advanceTime(10000) .advanceTime(10000)
.addExecute() .addExecute()
.addResolvedInstance('instance-previously-active') .addRecoveredInstance('instance-previously-active')
.addActiveInstance('instance-currently-active', 'action group A') .addActiveInstance('instance-currently-active', 'action group A')
.getEvents(); .getEvents();
const eventsResult = { const eventsResult = {

View file

@ -6,7 +6,7 @@
import { SanitizedAlert, AlertInstanceSummary } from '../types'; import { SanitizedAlert, AlertInstanceSummary } from '../types';
import { IValidatedEvent } from '../../../event_log/server'; import { IValidatedEvent } from '../../../event_log/server';
import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER } from '../plugin'; import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER, LEGACY_EVENT_LOG_ACTIONS } from '../plugin';
import { alertInstanceSummaryFromEventLog } from './alert_instance_summary_from_event_log'; import { alertInstanceSummaryFromEventLog } from './alert_instance_summary_from_event_log';
const ONE_HOUR_IN_MILLIS = 60 * 60 * 1000; const ONE_HOUR_IN_MILLIS = 60 * 60 * 1000;
@ -189,7 +189,43 @@ describe('alertInstanceSummaryFromEventLog', () => {
.addActiveInstance('instance-1', 'action group A') .addActiveInstance('instance-1', 'action group A')
.advanceTime(10000) .advanceTime(10000)
.addExecute() .addExecute()
.addResolvedInstance('instance-1') .addRecoveredInstance('instance-1')
.getEvents();
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
alert,
events,
dateStart,
dateEnd,
});
const { lastRun, status, instances } = summary;
expect({ lastRun, status, instances }).toMatchInlineSnapshot(`
Object {
"instances": Object {
"instance-1": Object {
"actionGroupId": undefined,
"activeStartDate": undefined,
"muted": false,
"status": "OK",
},
},
"lastRun": "2020-06-18T00:00:10.000Z",
"status": "OK",
}
`);
});
test('legacy alert with currently inactive instance', async () => {
const alert = createAlert({});
const eventsFactory = new EventsFactory();
const events = eventsFactory
.addExecute()
.addNewInstance('instance-1')
.addActiveInstance('instance-1', 'action group A')
.advanceTime(10000)
.addExecute()
.addLegacyResolvedInstance('instance-1')
.getEvents(); .getEvents();
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({ const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
@ -224,7 +260,7 @@ describe('alertInstanceSummaryFromEventLog', () => {
.addActiveInstance('instance-1', 'action group A') .addActiveInstance('instance-1', 'action group A')
.advanceTime(10000) .advanceTime(10000)
.addExecute() .addExecute()
.addResolvedInstance('instance-1') .addRecoveredInstance('instance-1')
.getEvents(); .getEvents();
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({ const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
@ -406,7 +442,7 @@ describe('alertInstanceSummaryFromEventLog', () => {
.advanceTime(10000) .advanceTime(10000)
.addExecute() .addExecute()
.addActiveInstance('instance-1', 'action group A') .addActiveInstance('instance-1', 'action group A')
.addResolvedInstance('instance-2') .addRecoveredInstance('instance-2')
.getEvents(); .getEvents();
const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({ const summary: AlertInstanceSummary = alertInstanceSummaryFromEventLog({
@ -451,7 +487,7 @@ describe('alertInstanceSummaryFromEventLog', () => {
.advanceTime(10000) .advanceTime(10000)
.addExecute() .addExecute()
.addActiveInstance('instance-1', 'action group A') .addActiveInstance('instance-1', 'action group A')
.addResolvedInstance('instance-2') .addRecoveredInstance('instance-2')
.advanceTime(10000) .advanceTime(10000)
.addExecute() .addExecute()
.addActiveInstance('instance-1', 'action group B') .addActiveInstance('instance-1', 'action group B')
@ -561,12 +597,24 @@ export class EventsFactory {
return this; return this;
} }
addResolvedInstance(instanceId: string): EventsFactory { addRecoveredInstance(instanceId: string): EventsFactory {
this.events.push({ this.events.push({
'@timestamp': this.date, '@timestamp': this.date,
event: { event: {
provider: EVENT_LOG_PROVIDER, provider: EVENT_LOG_PROVIDER,
action: EVENT_LOG_ACTIONS.resolvedInstance, action: EVENT_LOG_ACTIONS.recoveredInstance,
},
kibana: { alerting: { instance_id: instanceId } },
});
return this;
}
addLegacyResolvedInstance(instanceId: string): EventsFactory {
this.events.push({
'@timestamp': this.date,
event: {
provider: EVENT_LOG_PROVIDER,
action: LEGACY_EVENT_LOG_ACTIONS.resolvedInstance,
}, },
kibana: { alerting: { instance_id: instanceId } }, kibana: { alerting: { instance_id: instanceId } },
}); });

View file

@ -6,7 +6,7 @@
import { SanitizedAlert, AlertInstanceSummary, AlertInstanceStatus } from '../types'; import { SanitizedAlert, AlertInstanceSummary, AlertInstanceStatus } from '../types';
import { IEvent } from '../../../event_log/server'; import { IEvent } from '../../../event_log/server';
import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER } from '../plugin'; import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER, LEGACY_EVENT_LOG_ACTIONS } from '../plugin';
export interface AlertInstanceSummaryFromEventLogParams { export interface AlertInstanceSummaryFromEventLogParams {
alert: SanitizedAlert; alert: SanitizedAlert;
@ -80,7 +80,8 @@ export function alertInstanceSummaryFromEventLog(
status.status = 'Active'; status.status = 'Active';
status.actionGroupId = event?.kibana?.alerting?.action_group_id; status.actionGroupId = event?.kibana?.alerting?.action_group_id;
break; break;
case EVENT_LOG_ACTIONS.resolvedInstance: case LEGACY_EVENT_LOG_ACTIONS.resolvedInstance:
case EVENT_LOG_ACTIONS.recoveredInstance:
status.status = 'OK'; status.status = 'OK';
status.activeStartDate = undefined; status.activeStartDate = undefined;
status.actionGroupId = undefined; status.actionGroupId = undefined;

View file

@ -82,9 +82,12 @@ export const EVENT_LOG_ACTIONS = {
execute: 'execute', execute: 'execute',
executeAction: 'execute-action', executeAction: 'execute-action',
newInstance: 'new-instance', newInstance: 'new-instance',
resolvedInstance: 'resolved-instance', recoveredInstance: 'recovered-instance',
activeInstance: 'active-instance', activeInstance: 'active-instance',
}; };
export const LEGACY_EVENT_LOG_ACTIONS = {
resolvedInstance: 'resolved-instance',
};
export interface PluginSetupContract { export interface PluginSetupContract {
registerType: AlertTypeRegistry['register']; registerType: AlertTypeRegistry['register'];

View file

@ -26,12 +26,12 @@ import { alertsMock, alertsClientMock } from '../mocks';
import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock';
import { IEventLogger } from '../../../event_log/server'; import { IEventLogger } from '../../../event_log/server';
import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server';
import { Alert, ResolvedActionGroup } from '../../common'; import { Alert, RecoveredActionGroup } from '../../common';
import { omit } from 'lodash'; import { omit } from 'lodash';
const alertType = { const alertType = {
id: 'test', id: 'test',
name: 'My test alert', name: 'My test alert',
actionGroups: [{ id: 'default', name: 'Default' }, ResolvedActionGroup], actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup],
defaultActionGroupId: 'default', defaultActionGroupId: 'default',
executor: jest.fn(), executor: jest.fn(),
producer: 'alerts', producer: 'alerts',
@ -114,7 +114,7 @@ describe('Task Runner', () => {
}, },
}, },
{ {
group: ResolvedActionGroup.id, group: RecoveredActionGroup.id,
id: '2', id: '2',
actionTypeId: 'action', actionTypeId: 'action',
params: { params: {
@ -517,7 +517,7 @@ describe('Task Runner', () => {
`); `);
}); });
test('fire resolved actions for execution for the alertInstances which is in the resolved state', async () => { test('fire recovered actions for execution for the alertInstances which is in the recovered state', async () => {
taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true);
taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true);
@ -650,7 +650,7 @@ describe('Task Runner', () => {
Array [ Array [
Object { Object {
"event": Object { "event": Object {
"action": "resolved-instance", "action": "recovered-instance",
}, },
"kibana": Object { "kibana": Object {
"alerting": Object { "alerting": Object {
@ -666,7 +666,7 @@ describe('Task Runner', () => {
}, },
], ],
}, },
"message": "test:1: 'alert-name' resolved instance: '2'", "message": "test:1: 'alert-name' instance '2' has recovered",
}, },
], ],
Array [ Array [

View file

@ -39,7 +39,7 @@ import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_l
import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error'; import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error';
import { AlertsClient } from '../alerts_client'; import { AlertsClient } from '../alerts_client';
import { partiallyUpdateAlert } from '../saved_objects'; import { partiallyUpdateAlert } from '../saved_objects';
import { ResolvedActionGroup } from '../../common'; import { RecoveredActionGroup } from '../../common';
const FALLBACK_RETRY_INTERVAL = '5m'; const FALLBACK_RETRY_INTERVAL = '5m';
@ -219,7 +219,7 @@ export class TaskRunner {
alertInstance.hasScheduledActions() alertInstance.hasScheduledActions()
); );
generateNewAndResolvedInstanceEvents({ generateNewAndRecoveredInstanceEvents({
eventLogger, eventLogger,
originalAlertInstances, originalAlertInstances,
currentAlertInstances: instancesWithScheduledActions, currentAlertInstances: instancesWithScheduledActions,
@ -229,7 +229,7 @@ export class TaskRunner {
}); });
if (!muteAll) { if (!muteAll) {
scheduleActionsForResolvedInstances( scheduleActionsForRecoveredInstances(
alertInstances, alertInstances,
executionHandler, executionHandler,
originalAlertInstances, originalAlertInstances,
@ -436,7 +436,7 @@ export class TaskRunner {
} }
} }
interface GenerateNewAndResolvedInstanceEventsParams { interface GenerateNewAndRecoveredInstanceEventsParams {
eventLogger: IEventLogger; eventLogger: IEventLogger;
originalAlertInstances: Dictionary<AlertInstance>; originalAlertInstances: Dictionary<AlertInstance>;
currentAlertInstances: Dictionary<AlertInstance>; currentAlertInstances: Dictionary<AlertInstance>;
@ -445,18 +445,20 @@ interface GenerateNewAndResolvedInstanceEventsParams {
namespace: string | undefined; namespace: string | undefined;
} }
function generateNewAndResolvedInstanceEvents(params: GenerateNewAndResolvedInstanceEventsParams) { function generateNewAndRecoveredInstanceEvents(
params: GenerateNewAndRecoveredInstanceEventsParams
) {
const { eventLogger, alertId, namespace, currentAlertInstances, originalAlertInstances } = params; const { eventLogger, alertId, namespace, currentAlertInstances, originalAlertInstances } = params;
const originalAlertInstanceIds = Object.keys(originalAlertInstances); const originalAlertInstanceIds = Object.keys(originalAlertInstances);
const currentAlertInstanceIds = Object.keys(currentAlertInstances); const currentAlertInstanceIds = Object.keys(currentAlertInstances);
const newIds = without(currentAlertInstanceIds, ...originalAlertInstanceIds); const newIds = without(currentAlertInstanceIds, ...originalAlertInstanceIds);
const resolvedIds = without(originalAlertInstanceIds, ...currentAlertInstanceIds); const recoveredIds = without(originalAlertInstanceIds, ...currentAlertInstanceIds);
for (const id of resolvedIds) { for (const id of recoveredIds) {
const actionGroup = originalAlertInstances[id].getLastScheduledActions()?.group; const actionGroup = originalAlertInstances[id].getLastScheduledActions()?.group;
const message = `${params.alertLabel} resolved instance: '${id}'`; const message = `${params.alertLabel} instance '${id}' has recovered`;
logInstanceEvent(id, EVENT_LOG_ACTIONS.resolvedInstance, message, actionGroup); logInstanceEvent(id, EVENT_LOG_ACTIONS.recoveredInstance, message, actionGroup);
} }
for (const id of newIds) { for (const id of newIds) {
@ -496,7 +498,7 @@ function generateNewAndResolvedInstanceEvents(params: GenerateNewAndResolvedInst
} }
} }
function scheduleActionsForResolvedInstances( function scheduleActionsForRecoveredInstances(
alertInstancesMap: Record<string, AlertInstance>, alertInstancesMap: Record<string, AlertInstance>,
executionHandler: ReturnType<typeof createExecutionHandler>, executionHandler: ReturnType<typeof createExecutionHandler>,
originalAlertInstances: Record<string, AlertInstance>, originalAlertInstances: Record<string, AlertInstance>,
@ -505,22 +507,22 @@ function scheduleActionsForResolvedInstances(
) { ) {
const currentAlertInstanceIds = Object.keys(currentAlertInstances); const currentAlertInstanceIds = Object.keys(currentAlertInstances);
const originalAlertInstanceIds = Object.keys(originalAlertInstances); const originalAlertInstanceIds = Object.keys(originalAlertInstances);
const resolvedIds = without( const recoveredIds = without(
originalAlertInstanceIds, originalAlertInstanceIds,
...currentAlertInstanceIds, ...currentAlertInstanceIds,
...mutedInstanceIds ...mutedInstanceIds
); );
for (const id of resolvedIds) { for (const id of recoveredIds) {
const instance = alertInstancesMap[id]; const instance = alertInstancesMap[id];
instance.updateLastScheduledActions(ResolvedActionGroup.id); instance.updateLastScheduledActions(RecoveredActionGroup.id);
instance.unscheduleActions(); instance.unscheduleActions();
executionHandler({ executionHandler({
actionGroup: ResolvedActionGroup.id, actionGroup: RecoveredActionGroup.id,
context: {}, context: {},
state: {}, state: {},
alertInstanceId: id, alertInstanceId: id,
}); });
instance.scheduleActions(ResolvedActionGroup.id); instance.scheduleActions(RecoveredActionGroup.id);
} }
} }

View file

@ -9,7 +9,7 @@ import moment from 'moment';
import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label'; import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label';
import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { toMetricOpt } from '../../../../common/snapshot_metric_i18n';
import { AlertStates, InventoryMetricConditions } from './types'; import { AlertStates, InventoryMetricConditions } from './types';
import { ResolvedActionGroup } from '../../../../../alerts/common'; import { RecoveredActionGroup } from '../../../../../alerts/common';
import { AlertExecutorOptions } from '../../../../../alerts/server'; import { AlertExecutorOptions } from '../../../../../alerts/server';
import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types';
import { InfraBackendLibs } from '../../infra_types'; import { InfraBackendLibs } from '../../infra_types';
@ -103,7 +103,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
} }
if (reason) { if (reason) {
const actionGroupId = const actionGroupId =
nextState === AlertStates.OK ? ResolvedActionGroup.id : FIRED_ACTIONS.id; nextState === AlertStates.OK ? RecoveredActionGroup.id : FIRED_ACTIONS.id;
alertInstance.scheduleActions(actionGroupId, { alertInstance.scheduleActions(actionGroupId, {
group: item, group: item,
alertState: stateToAlertMessage[nextState], alertState: stateToAlertMessage[nextState],

View file

@ -6,7 +6,7 @@
import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor';
import { Comparator, AlertStates } from './types'; import { Comparator, AlertStates } from './types';
import * as mocks from './test_mocks'; import * as mocks from './test_mocks';
import { ResolvedActionGroup } from '../../../../../alerts/common'; import { RecoveredActionGroup } from '../../../../../alerts/common';
import { AlertExecutorOptions } from '../../../../../alerts/server'; import { AlertExecutorOptions } from '../../../../../alerts/server';
import { import {
alertsMock, alertsMock,
@ -367,7 +367,7 @@ describe('The metric threshold alert type', () => {
expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id);
expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); expect(getState(instanceID).alertState).toBe(AlertStates.ALERT);
await execute([2]); await execute([2]);
expect(mostRecentAction(instanceID).id).toBe(ResolvedActionGroup.id); expect(mostRecentAction(instanceID).id).toBe(RecoveredActionGroup.id);
expect(getState(instanceID).alertState).toBe(AlertStates.OK); expect(getState(instanceID).alertState).toBe(AlertStates.OK);
}); });
test('does not continue to send a recovery alert if the metric is still OK', async () => { test('does not continue to send a recovery alert if the metric is still OK', async () => {
@ -383,7 +383,7 @@ describe('The metric threshold alert type', () => {
expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id);
expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); expect(getState(instanceID).alertState).toBe(AlertStates.ALERT);
await execute([2]); await execute([2]);
expect(mostRecentAction(instanceID).id).toBe(ResolvedActionGroup.id); expect(mostRecentAction(instanceID).id).toBe(RecoveredActionGroup.id);
expect(getState(instanceID).alertState).toBe(AlertStates.OK); expect(getState(instanceID).alertState).toBe(AlertStates.OK);
}); });
}); });

View file

@ -6,7 +6,7 @@
import { first, last } from 'lodash'; import { first, last } from 'lodash';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import moment from 'moment'; import moment from 'moment';
import { ResolvedActionGroup } from '../../../../../alerts/common'; import { RecoveredActionGroup } from '../../../../../alerts/common';
import { AlertExecutorOptions } from '../../../../../alerts/server'; import { AlertExecutorOptions } from '../../../../../alerts/server';
import { InfraBackendLibs } from '../../infra_types'; import { InfraBackendLibs } from '../../infra_types';
import { import {
@ -89,7 +89,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
const firstResult = first(alertResults); const firstResult = first(alertResults);
const timestamp = (firstResult && firstResult[group].timestamp) ?? moment().toISOString(); const timestamp = (firstResult && firstResult[group].timestamp) ?? moment().toISOString();
const actionGroupId = const actionGroupId =
nextState === AlertStates.OK ? ResolvedActionGroup.id : FIRED_ACTIONS.id; nextState === AlertStates.OK ? RecoveredActionGroup.id : FIRED_ACTIONS.id;
alertInstance.scheduleActions(actionGroupId, { alertInstance.scheduleActions(actionGroupId, {
group, group,
alertState: stateToAlertMessage[nextState], alertState: stateToAlertMessage[nextState],

View file

@ -16,10 +16,10 @@ export const routeToConnectors = `/connectors`;
export const routeToAlerts = `/alerts`; export const routeToAlerts = `/alerts`;
export const routeToAlertDetails = `/alert/:alertId`; export const routeToAlertDetails = `/alert/:alertId`;
export const resolvedActionGroupMessage = i18n.translate( export const recoveredActionGroupMessage = i18n.translate(
'xpack.triggersActionsUI.sections.actionForm.ResolvedMessage', 'xpack.triggersActionsUI.sections.actionForm.RecoveredMessage',
{ {
defaultMessage: 'Resolved', defaultMessage: 'Recovered',
} }
); );

View file

@ -10,8 +10,9 @@ import { act } from 'react-dom/test-utils';
import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { actionTypeRegistryMock } from '../../action_type_registry.mock';
import { ValidationResult, Alert, AlertAction } from '../../../types'; import { ValidationResult, Alert, AlertAction } from '../../../types';
import ActionForm from './action_form'; import ActionForm from './action_form';
import { ResolvedActionGroup } from '../../../../../alerts/common'; import { RecoveredActionGroup } from '../../../../../alerts/common';
import { useKibana } from '../../../common/lib/kibana'; import { useKibana } from '../../../common/lib/kibana';
import { EuiScreenReaderOnly } from '@elastic/eui';
jest.mock('../../../common/lib/kibana'); jest.mock('../../../common/lib/kibana');
jest.mock('../../lib/action_connector_api', () => ({ jest.mock('../../lib/action_connector_api', () => ({
loadAllActions: jest.fn(), loadAllActions: jest.fn(),
@ -228,7 +229,7 @@ describe('action_form', () => {
}} }}
actionGroups={[ actionGroups={[
{ id: 'default', name: 'Default' }, { id: 'default', name: 'Default' },
{ id: 'resolved', name: 'Resolved' }, { id: 'recovered', name: 'Recovered' },
]} ]}
setActionGroupIdByIndex={(group: string, index: number) => { setActionGroupIdByIndex={(group: string, index: number) => {
initialAlert.actions[index].group = group; initialAlert.actions[index].group = group;
@ -347,18 +348,18 @@ describe('action_form', () => {
"value": "default", "value": "default",
}, },
Object { Object {
"data-test-subj": "addNewActionConnectorActionGroup-0-option-resolved", "data-test-subj": "addNewActionConnectorActionGroup-0-option-recovered",
"inputDisplay": "Resolved", "inputDisplay": "Recovered",
"value": "resolved", "value": "recovered",
}, },
] ]
`); `);
}); });
it('renders selected Resolved action group', async () => { it('renders selected Recovered action group', async () => {
const wrapper = await setup([ const wrapper = await setup([
{ {
group: ResolvedActionGroup.id, group: RecoveredActionGroup.id,
id: 'test', id: 'test',
actionTypeId: actionType.id, actionTypeId: actionType.id,
params: { params: {
@ -381,15 +382,17 @@ describe('action_form', () => {
"value": "default", "value": "default",
}, },
Object { Object {
"data-test-subj": "addNewActionConnectorActionGroup-0-option-resolved", "data-test-subj": "addNewActionConnectorActionGroup-0-option-recovered",
"inputDisplay": "Resolved", "inputDisplay": "Recovered",
"value": "resolved", "value": "recovered",
}, },
] ]
`); `);
expect(actionGroupsSelect.first().text()).toEqual(
'Select an option: Resolved, is selectedResolved' expect(actionGroupsSelect.first().find(EuiScreenReaderOnly).text()).toEqual(
'Select an option: Recovered, is selected'
); );
expect(actionGroupsSelect.first().find('button').first().text()).toEqual('Recovered');
}); });
it('renders available connectors for the selected action type', async () => { it('renders available connectors for the selected action type', async () => {

View file

@ -26,7 +26,7 @@ import {
EuiBadge, EuiBadge,
EuiErrorBoundary, EuiErrorBoundary,
} from '@elastic/eui'; } from '@elastic/eui';
import { AlertActionParam, ResolvedActionGroup } from '../../../../../alerts/common'; import { AlertActionParam, RecoveredActionGroup } from '../../../../../alerts/common';
import { import {
IErrorObject, IErrorObject,
AlertAction, AlertAction,
@ -40,7 +40,7 @@ import { checkActionFormActionTypeEnabled } from '../../lib/check_action_type_en
import { hasSaveActionsCapability } from '../../lib/capabilities'; import { hasSaveActionsCapability } from '../../lib/capabilities';
import { ActionAccordionFormProps } from './action_form'; import { ActionAccordionFormProps } from './action_form';
import { transformActionVariables } from '../../lib/action_variables'; import { transformActionVariables } from '../../lib/action_variables';
import { resolvedActionGroupMessage } from '../../constants'; import { recoveredActionGroupMessage } from '../../constants';
import { useKibana } from '../../../common/lib/kibana'; import { useKibana } from '../../../common/lib/kibana';
import { getDefaultsForActionParams } from '../../lib/get_defaults_for_action_params'; import { getDefaultsForActionParams } from '../../lib/get_defaults_for_action_params';
@ -105,8 +105,8 @@ export const ActionTypeForm = ({
useEffect(() => { useEffect(() => {
setAvailableActionVariables(getAvailableActionVariables(messageVariables, actionItem.group)); setAvailableActionVariables(getAvailableActionVariables(messageVariables, actionItem.group));
const res = const res =
actionItem.group === ResolvedActionGroup.id actionItem.group === RecoveredActionGroup.id
? resolvedActionGroupMessage ? recoveredActionGroupMessage
: defaultActionMessage; : defaultActionMessage;
setAvailableDefaultActionMessage(res); setAvailableDefaultActionMessage(res);
const paramsDefaults = getDefaultsForActionParams(actionItem.actionTypeId, actionItem.group); const paramsDefaults = getDefaultsForActionParams(actionItem.actionTypeId, actionItem.group);
@ -374,7 +374,7 @@ function getAvailableActionVariables(
return []; return [];
} }
const filteredActionVariables = const filteredActionVariables =
actionGroup === ResolvedActionGroup.id actionGroup === RecoveredActionGroup.id
? { params: actionVariables.params, state: actionVariables.state } ? { params: actionVariables.params, state: actionVariables.state }
: actionVariables; : actionVariables;

View file

@ -17,7 +17,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) {
const expectedNoOpType = { const expectedNoOpType = {
actionGroups: [ actionGroups: [
{ id: 'default', name: 'Default' }, { id: 'default', name: 'Default' },
{ id: 'resolved', name: 'Resolved' }, { id: 'recovered', name: 'Recovered' },
], ],
defaultActionGroupId: 'default', defaultActionGroupId: 'default',
id: 'test.noop', id: 'test.noop',
@ -33,7 +33,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) {
const expectedRestrictedNoOpType = { const expectedRestrictedNoOpType = {
actionGroups: [ actionGroups: [
{ id: 'default', name: 'Default' }, { id: 'default', name: 'Default' },
{ id: 'resolved', name: 'Resolved' }, { id: 'recovered', name: 'Recovered' },
], ],
defaultActionGroupId: 'default', defaultActionGroupId: 'default',
id: 'test.restricted-noop', id: 'test.restricted-noop',

View file

@ -6,7 +6,7 @@
import expect from '@kbn/expect'; import expect from '@kbn/expect';
import { Response as SupertestResponse } from 'supertest'; import { Response as SupertestResponse } from 'supertest';
import { ResolvedActionGroup } from '../../../../../plugins/alerts/common'; import { RecoveredActionGroup } from '../../../../../plugins/alerts/common';
import { Space } from '../../../common/types'; import { Space } from '../../../common/types';
import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { FtrProviderContext } from '../../../common/ftr_provider_context';
import { import {
@ -137,7 +137,7 @@ instanceStateValue: true
await taskManagerUtils.waitForActionTaskParamsToBeCleanedUp(testStart); await taskManagerUtils.waitForActionTaskParamsToBeCleanedUp(testStart);
}); });
it('should fire actions when an alert instance is resolved', async () => { it('should fire actions when an alert instance is recovered', async () => {
const reference = alertUtils.generateReference(); const reference = alertUtils.generateReference();
const { body: createdAction } = await supertestWithoutAuth const { body: createdAction } = await supertestWithoutAuth
@ -174,12 +174,12 @@ instanceStateValue: true
params: {}, params: {},
}, },
{ {
group: ResolvedActionGroup.id, group: RecoveredActionGroup.id,
id: indexRecordActionId, id: indexRecordActionId,
params: { params: {
index: ES_TEST_INDEX_NAME, index: ES_TEST_INDEX_NAME,
reference, reference,
message: 'Resolved message', message: 'Recovered message',
}, },
}, },
], ],
@ -194,10 +194,10 @@ instanceStateValue: true
await esTestIndexTool.waitForDocs('action:test.index-record', reference) await esTestIndexTool.waitForDocs('action:test.index-record', reference)
)[0]; )[0];
expect(actionTestRecord._source.params.message).to.eql('Resolved message'); expect(actionTestRecord._source.params.message).to.eql('Recovered message');
}); });
it('should not fire actions when an alert instance is resolved, but alert is muted', async () => { it('should not fire actions when an alert instance is recovered, but alert is muted', async () => {
const testStart = new Date(); const testStart = new Date();
const reference = alertUtils.generateReference(); const reference = alertUtils.generateReference();
@ -237,12 +237,12 @@ instanceStateValue: true
params: {}, params: {},
}, },
{ {
group: ResolvedActionGroup.id, group: RecoveredActionGroup.id,
id: indexRecordActionId, id: indexRecordActionId,
params: { params: {
index: ES_TEST_INDEX_NAME, index: ES_TEST_INDEX_NAME,
reference, reference,
message: 'Resolved message', message: 'Recovered message',
}, },
}, },
], ],

View file

@ -78,7 +78,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) {
'execute-action', 'execute-action',
'new-instance', 'new-instance',
'active-instance', 'active-instance',
'resolved-instance', 'recovered-instance',
], ],
}); });
}); });
@ -87,25 +87,25 @@ export default function eventLogTests({ getService }: FtrProviderContext) {
const executeEvents = getEventsByAction(events, 'execute'); const executeEvents = getEventsByAction(events, 'execute');
const executeActionEvents = getEventsByAction(events, 'execute-action'); const executeActionEvents = getEventsByAction(events, 'execute-action');
const newInstanceEvents = getEventsByAction(events, 'new-instance'); const newInstanceEvents = getEventsByAction(events, 'new-instance');
const resolvedInstanceEvents = getEventsByAction(events, 'resolved-instance'); const recoveredInstanceEvents = getEventsByAction(events, 'recovered-instance');
expect(executeEvents.length >= 4).to.be(true); expect(executeEvents.length >= 4).to.be(true);
expect(executeActionEvents.length).to.be(2); expect(executeActionEvents.length).to.be(2);
expect(newInstanceEvents.length).to.be(1); expect(newInstanceEvents.length).to.be(1);
expect(resolvedInstanceEvents.length).to.be(1); expect(recoveredInstanceEvents.length).to.be(1);
// make sure the events are in the right temporal order // make sure the events are in the right temporal order
const executeTimes = getTimestamps(executeEvents); const executeTimes = getTimestamps(executeEvents);
const executeActionTimes = getTimestamps(executeActionEvents); const executeActionTimes = getTimestamps(executeActionEvents);
const newInstanceTimes = getTimestamps(newInstanceEvents); const newInstanceTimes = getTimestamps(newInstanceEvents);
const resolvedInstanceTimes = getTimestamps(resolvedInstanceEvents); const recoveredInstanceTimes = getTimestamps(recoveredInstanceEvents);
expect(executeTimes[0] < newInstanceTimes[0]).to.be(true); expect(executeTimes[0] < newInstanceTimes[0]).to.be(true);
expect(executeTimes[1] <= newInstanceTimes[0]).to.be(true); expect(executeTimes[1] <= newInstanceTimes[0]).to.be(true);
expect(executeTimes[2] > newInstanceTimes[0]).to.be(true); expect(executeTimes[2] > newInstanceTimes[0]).to.be(true);
expect(executeTimes[1] <= executeActionTimes[0]).to.be(true); expect(executeTimes[1] <= executeActionTimes[0]).to.be(true);
expect(executeTimes[2] > executeActionTimes[0]).to.be(true); expect(executeTimes[2] > executeActionTimes[0]).to.be(true);
expect(resolvedInstanceTimes[0] > newInstanceTimes[0]).to.be(true); expect(recoveredInstanceTimes[0] > newInstanceTimes[0]).to.be(true);
// validate each event // validate each event
let executeCount = 0; let executeCount = 0;
@ -136,8 +136,8 @@ export default function eventLogTests({ getService }: FtrProviderContext) {
case 'new-instance': case 'new-instance':
validateInstanceEvent(event, `created new instance: 'instance'`); validateInstanceEvent(event, `created new instance: 'instance'`);
break; break;
case 'resolved-instance': case 'recovered-instance':
validateInstanceEvent(event, `resolved instance: 'instance'`); validateInstanceEvent(event, `recovered instance: 'instance'`);
break; break;
case 'active-instance': case 'active-instance':
validateInstanceEvent(event, `active instance: 'instance' in actionGroup: 'default'`); validateInstanceEvent(event, `active instance: 'instance' in actionGroup: 'default'`);

View file

@ -216,7 +216,7 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr
await alertUtils.muteInstance(createdAlert.id, 'instanceC'); await alertUtils.muteInstance(createdAlert.id, 'instanceC');
await alertUtils.muteInstance(createdAlert.id, 'instanceD'); await alertUtils.muteInstance(createdAlert.id, 'instanceD');
await waitForEvents(createdAlert.id, ['new-instance', 'resolved-instance']); await waitForEvents(createdAlert.id, ['new-instance', 'recovered-instance']);
const response = await supertest.get( const response = await supertest.get(
`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary` `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary`
); );

View file

@ -25,7 +25,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) {
expect(fixtureAlertType).to.eql({ expect(fixtureAlertType).to.eql({
actionGroups: [ actionGroups: [
{ id: 'default', name: 'Default' }, { id: 'default', name: 'Default' },
{ id: 'resolved', name: 'Resolved' }, { id: 'recovered', name: 'Recovered' },
], ],
defaultActionGroupId: 'default', defaultActionGroupId: 'default',
id: 'test.noop', id: 'test.noop',