Skip to content

Commit 73685c7

Browse files
authored
[Alerting UI] Added ability to assign alert actions to resolved action group in UI (#83139) (#83389)
* Added ability to assign alert actions to resolved action group in UI * Added unit test * Fixed due to comments
1 parent 8f08058 commit 73685c7

11 files changed

Lines changed: 160 additions & 33 deletions

File tree

x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_params.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ActionParamsProps } from '../../../../types';
1111
import { EmailActionParams } from '../types';
1212
import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables';
1313
import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables';
14+
import { resolvedActionGroupMessage } from '../../../constants';
1415

1516
export const EmailParamsFields = ({
1617
actionParams,
@@ -28,11 +29,18 @@ export const EmailParamsFields = ({
2829
const [addBCC, setAddBCC] = useState<boolean>(false);
2930

3031
useEffect(() => {
31-
if (!message && defaultMessage && defaultMessage.length > 0) {
32+
if (defaultMessage === resolvedActionGroupMessage) {
33+
editAction('message', defaultMessage, index);
34+
} else if (
35+
(!message || message === resolvedActionGroupMessage) &&
36+
defaultMessage &&
37+
defaultMessage.length > 0
38+
) {
3239
editAction('message', defaultMessage, index);
3340
}
41+
3442
// eslint-disable-next-line react-hooks/exhaustive-deps
35-
}, []);
43+
}, [defaultMessage]);
3644

3745
return (
3846
<Fragment>

x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log_params.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { EuiSelect, EuiFormRow } from '@elastic/eui';
99
import { ActionParamsProps } from '../../../../types';
1010
import { ServerLogActionParams } from '.././types';
1111
import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables';
12+
import { resolvedActionGroupMessage } from '../../../constants';
1213

1314
export const ServerLogParamsFields: React.FunctionComponent<ActionParamsProps<
1415
ServerLogActionParams
@@ -25,11 +26,22 @@ export const ServerLogParamsFields: React.FunctionComponent<ActionParamsProps<
2526

2627
useEffect(() => {
2728
editAction('level', 'info', index);
28-
if (!message && defaultMessage && defaultMessage.length > 0) {
29+
// eslint-disable-next-line react-hooks/exhaustive-deps
30+
}, []);
31+
32+
useEffect(() => {
33+
if (defaultMessage === resolvedActionGroupMessage) {
34+
editAction('message', defaultMessage, index);
35+
} else if (
36+
(!message || message === resolvedActionGroupMessage) &&
37+
defaultMessage &&
38+
defaultMessage.length > 0
39+
) {
2940
editAction('message', defaultMessage, index);
3041
}
42+
3143
// eslint-disable-next-line react-hooks/exhaustive-deps
32-
}, []);
44+
}, [defaultMessage]);
3345

3446
return (
3547
<Fragment>

x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack_params.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n';
88
import { ActionParamsProps } from '../../../../types';
99
import { SlackActionParams } from '../types';
1010
import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables';
11+
import { resolvedActionGroupMessage } from '../../../constants';
1112

1213
const SlackParamsFields: React.FunctionComponent<ActionParamsProps<SlackActionParams>> = ({
1314
actionParams,
@@ -19,11 +20,18 @@ const SlackParamsFields: React.FunctionComponent<ActionParamsProps<SlackActionPa
1920
}) => {
2021
const { message } = actionParams;
2122
useEffect(() => {
22-
if (!message && defaultMessage && defaultMessage.length > 0) {
23+
if (defaultMessage === resolvedActionGroupMessage) {
24+
editAction('message', defaultMessage, index);
25+
} else if (
26+
(!message || message === resolvedActionGroupMessage) &&
27+
defaultMessage &&
28+
defaultMessage.length > 0
29+
) {
2330
editAction('message', defaultMessage, index);
2431
}
32+
2533
// eslint-disable-next-line react-hooks/exhaustive-deps
26-
}, []);
34+
}, [defaultMessage]);
2735

2836
return (
2937
<TextAreaWithMessageVariables

x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7+
import { i18n } from '@kbn/i18n';
8+
79
export { BASE_ALERT_API_PATH } from '../../../../alerts/common';
810
export { BASE_ACTION_API_PATH } from '../../../../actions/common';
911

@@ -14,6 +16,13 @@ export const routeToConnectors = `/connectors`;
1416
export const routeToAlerts = `/alerts`;
1517
export const routeToAlertDetails = `/alert/:alertId`;
1618

19+
export const resolvedActionGroupMessage = i18n.translate(
20+
'xpack.triggersActionsUI.sections.actionForm.ResolvedMessage',
21+
{
22+
defaultMessage: 'Resolved',
23+
}
24+
);
25+
1726
export { TIME_UNITS } from './time_units';
1827
export enum SORT_ORDERS {
1928
ASCENDING = 'asc',

x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
*/
66

77
import { AlertType, ActionVariables } from '../../types';
8-
import { actionVariablesFromAlertType } from './action_variables';
8+
import { transformActionVariables } from './action_variables';
99
import { ALERTS_FEATURE_ID } from '../../../../alerts/common';
1010

1111
beforeEach(() => jest.resetAllMocks());
1212

13-
describe('actionVariablesFromAlertType', () => {
13+
describe('transformActionVariables', () => {
1414
test('should return correct variables when no state or context provided', async () => {
1515
const alertType = getAlertType({ context: [], state: [], params: [] });
16-
expect(actionVariablesFromAlertType(alertType)).toMatchInlineSnapshot(`
16+
expect(transformActionVariables(alertType.actionVariables)).toMatchInlineSnapshot(`
1717
Array [
1818
Object {
1919
"description": "The id of the alert.",
@@ -48,7 +48,7 @@ describe('actionVariablesFromAlertType', () => {
4848
state: [],
4949
params: [],
5050
});
51-
expect(actionVariablesFromAlertType(alertType)).toMatchInlineSnapshot(`
51+
expect(transformActionVariables(alertType.actionVariables)).toMatchInlineSnapshot(`
5252
Array [
5353
Object {
5454
"description": "The id of the alert.",
@@ -91,7 +91,7 @@ describe('actionVariablesFromAlertType', () => {
9191
],
9292
params: [],
9393
});
94-
expect(actionVariablesFromAlertType(alertType)).toMatchInlineSnapshot(`
94+
expect(transformActionVariables(alertType.actionVariables)).toMatchInlineSnapshot(`
9595
Array [
9696
Object {
9797
"description": "The id of the alert.",
@@ -137,7 +137,7 @@ describe('actionVariablesFromAlertType', () => {
137137
],
138138
params: [{ name: 'fooP', description: 'fooP-description' }],
139139
});
140-
expect(actionVariablesFromAlertType(alertType)).toMatchInlineSnapshot(`
140+
expect(transformActionVariables(alertType.actionVariables)).toMatchInlineSnapshot(`
141141
Array [
142142
Object {
143143
"description": "The id of the alert.",

x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
*/
66

77
import { i18n } from '@kbn/i18n';
8-
import { AlertType, ActionVariable } from '../../types';
8+
import { ActionVariable, ActionVariables } from '../../types';
99

1010
// return a "flattened" list of action variables for an alertType
11-
export function actionVariablesFromAlertType(alertType: AlertType): ActionVariable[] {
11+
export function transformActionVariables(actionVariables: ActionVariables): ActionVariable[] {
1212
const alwaysProvidedVars = getAlwaysProvidedActionVariables();
13-
const contextVars = prefixKeys(alertType.actionVariables.context, 'context.');
14-
const paramsVars = prefixKeys(alertType.actionVariables.params, 'params.');
15-
const stateVars = prefixKeys(alertType.actionVariables.state, 'state.');
13+
const contextVars = actionVariables.context
14+
? prefixKeys(actionVariables.context, 'context.')
15+
: [];
16+
const paramsVars = prefixKeys(actionVariables.params, 'params.');
17+
const stateVars = prefixKeys(actionVariables.state, 'state.');
1618

1719
return alwaysProvidedVars.concat(contextVars, paramsVars, stateVars);
1820
}

x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { act } from 'react-dom/test-utils';
1010
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
1111
import { ValidationResult, Alert, AlertAction } from '../../../types';
1212
import ActionForm from './action_form';
13+
import { ResolvedActionGroup } from '../../../../../alerts/common';
1314
jest.mock('../../lib/action_connector_api', () => ({
1415
loadAllActions: jest.fn(),
1516
loadActionTypes: jest.fn(),
@@ -217,15 +218,22 @@ describe('action_form', () => {
217218
const wrapper = mountWithIntl(
218219
<ActionForm
219220
actions={initialAlert.actions}
220-
messageVariables={[
221-
{ name: 'testVar1', description: 'test var1' },
222-
{ name: 'testVar2', description: 'test var2' },
223-
]}
221+
messageVariables={{
222+
params: [
223+
{ name: 'testVar1', description: 'test var1' },
224+
{ name: 'testVar2', description: 'test var2' },
225+
],
226+
state: [],
227+
context: [{ name: 'contextVar', description: 'context var1' }],
228+
}}
224229
defaultActionGroupId={'default'}
225230
setActionIdByIndex={(id: string, index: number) => {
226231
initialAlert.actions[index].id = id;
227232
}}
228-
actionGroups={[{ id: 'default', name: 'Default' }]}
233+
actionGroups={[
234+
{ id: 'default', name: 'Default' },
235+
{ id: 'resolved', name: 'Resolved' },
236+
]}
229237
setActionGroupIdByIndex={(group: string, index: number) => {
230238
initialAlert.actions[index].group = group;
231239
}}
@@ -346,10 +354,52 @@ describe('action_form', () => {
346354
"inputDisplay": "Default",
347355
"value": "default",
348356
},
357+
Object {
358+
"data-test-subj": "addNewActionConnectorActionGroup-0-option-resolved",
359+
"inputDisplay": "Resolved",
360+
"value": "resolved",
361+
},
349362
]
350363
`);
351364
});
352365

366+
it('renders selected Resolved action group', async () => {
367+
const wrapper = await setup([
368+
{
369+
group: ResolvedActionGroup.id,
370+
id: 'test',
371+
actionTypeId: actionType.id,
372+
params: {
373+
message: '',
374+
},
375+
},
376+
]);
377+
const actionOption = wrapper.find(
378+
`[data-test-subj="${actionType.id}-ActionTypeSelectOption"]`
379+
);
380+
actionOption.first().simulate('click');
381+
const actionGroupsSelect = wrapper.find(
382+
`[data-test-subj="addNewActionConnectorActionGroup-0"]`
383+
);
384+
expect((actionGroupsSelect.first().props() as any).options).toMatchInlineSnapshot(`
385+
Array [
386+
Object {
387+
"data-test-subj": "addNewActionConnectorActionGroup-0-option-default",
388+
"inputDisplay": "Default",
389+
"value": "default",
390+
},
391+
Object {
392+
"data-test-subj": "addNewActionConnectorActionGroup-0-option-resolved",
393+
"inputDisplay": "Resolved",
394+
"value": "resolved",
395+
},
396+
]
397+
`);
398+
expect(actionGroupsSelect.first().text()).toEqual(
399+
'Select an option: Resolved, is selectedResolved'
400+
);
401+
});
402+
353403
it('renders available connectors for the selected action type', async () => {
354404
const wrapper = await setup();
355405
const actionOption = wrapper.find(

x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
ActionTypeIndex,
2828
ActionConnector,
2929
ActionType,
30-
ActionVariable,
30+
ActionVariables,
3131
} from '../../../types';
3232
import { SectionLoading } from '../../components/section_loading';
3333
import { ConnectorAddModal } from './connector_add_modal';
@@ -51,7 +51,7 @@ export interface ActionAccordionFormProps {
5151
toastNotifications: ToastsSetup;
5252
docLinks: DocLinksStart;
5353
actionTypes?: ActionType[];
54-
messageVariables?: ActionVariable[];
54+
messageVariables?: ActionVariables;
5555
defaultActionMessage?: string;
5656
setHasActionsDisabled?: (value: boolean) => void;
5757
setHasActionsWithBrokenConnector?: (value: boolean) => void;

x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React, { Fragment, Suspense, useState } from 'react';
7+
import React, { Fragment, Suspense, useEffect, useState } from 'react';
88
import { i18n } from '@kbn/i18n';
99
import { FormattedMessage } from '@kbn/i18n/react';
1010
import {
@@ -25,10 +25,20 @@ import {
2525
EuiLoadingSpinner,
2626
EuiBadge,
2727
} from '@elastic/eui';
28-
import { IErrorObject, AlertAction, ActionTypeIndex, ActionConnector } from '../../../types';
28+
import { ResolvedActionGroup } from '../../../../../alerts/common';
29+
import {
30+
IErrorObject,
31+
AlertAction,
32+
ActionTypeIndex,
33+
ActionConnector,
34+
ActionVariables,
35+
ActionVariable,
36+
} from '../../../types';
2937
import { checkActionFormActionTypeEnabled } from '../../lib/check_action_type_enabled';
3038
import { hasSaveActionsCapability } from '../../lib/capabilities';
3139
import { ActionAccordionFormProps } from './action_form';
40+
import { transformActionVariables } from '../../lib/action_variables';
41+
import { resolvedActionGroupMessage } from '../../constants';
3242

3343
export type ActionTypeFormProps = {
3444
actionItem: AlertAction;
@@ -88,6 +98,20 @@ export const ActionTypeForm = ({
8898
setActionGroupIdByIndex,
8999
}: ActionTypeFormProps) => {
90100
const [isOpen, setIsOpen] = useState(true);
101+
const [availableActionVariables, setAvailableActionVariables] = useState<ActionVariable[]>([]);
102+
const [availableDefaultActionMessage, setAvailableDefaultActionMessage] = useState<
103+
string | undefined
104+
>(undefined);
105+
106+
useEffect(() => {
107+
setAvailableActionVariables(getAvailableActionVariables(messageVariables, actionItem.group));
108+
const res =
109+
actionItem.group === ResolvedActionGroup.id
110+
? resolvedActionGroupMessage
111+
: defaultActionMessage;
112+
setAvailableDefaultActionMessage(res);
113+
// eslint-disable-next-line react-hooks/exhaustive-deps
114+
}, [actionItem.group]);
91115

92116
const canSave = hasSaveActionsCapability(capabilities);
93117
const getSelectedOptions = (actionItemId: string) => {
@@ -244,8 +268,8 @@ export const ActionTypeForm = ({
244268
index={index}
245269
errors={actionParamsErrors.errors}
246270
editAction={setActionParamsProperty}
247-
messageVariables={messageVariables}
248-
defaultMessage={defaultActionMessage ?? undefined}
271+
messageVariables={availableActionVariables}
272+
defaultMessage={availableDefaultActionMessage}
249273
docLinks={docLinks}
250274
http={http}
251275
toastNotifications={toastNotifications}
@@ -337,3 +361,20 @@ export const ActionTypeForm = ({
337361
</Fragment>
338362
);
339363
};
364+
365+
function getAvailableActionVariables(
366+
actionVariables: ActionVariables | undefined,
367+
actionGroup: string
368+
) {
369+
if (!actionVariables) {
370+
return [];
371+
}
372+
const filteredActionVariables =
373+
actionGroup === ResolvedActionGroup.id
374+
? { params: actionVariables.params, state: actionVariables.state }
375+
: actionVariables;
376+
377+
return transformActionVariables(filteredActionVariables).sort((a, b) =>
378+
a.name.toUpperCase().localeCompare(b.name.toUpperCase())
379+
);
380+
}

x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import {
4040
getDurationUnitValue,
4141
} from '../../../../../alerts/common/parse_duration';
4242
import { loadAlertTypes } from '../../lib/alert_api';
43-
import { actionVariablesFromAlertType } from '../../lib/action_variables';
4443
import { AlertReducerAction } from './alert_reducer';
4544
import {
4645
AlertTypeModel,
@@ -458,9 +457,7 @@ export const AlertForm = ({
458457
actions={alert.actions}
459458
setHasActionsDisabled={setHasActionsDisabled}
460459
setHasActionsWithBrokenConnector={setHasActionsWithBrokenConnector}
461-
messageVariables={actionVariablesFromAlertType(
462-
alertTypesIndex.get(alert.alertTypeId)!
463-
).sort((a, b) => a.name.toUpperCase().localeCompare(b.name.toUpperCase()))}
460+
messageVariables={alertTypesIndex.get(alert.alertTypeId)!.actionVariables}
464461
defaultActionGroupId={defaultActionGroupId}
465462
actionGroups={alertTypesIndex.get(alert.alertTypeId)!.actionGroups}
466463
setActionIdByIndex={(id: string, index: number) => setActionProperty('id', id, index)}

0 commit comments

Comments
 (0)