Skip to content

Commit

Permalink
Added default dedupKey value as an {{alertInstanceId}} to provide gro…
Browse files Browse the repository at this point in the history
…uping functionality for PagerDuty incidents. (#84598)

* Added default dedupKey value as an {{alertInstanceId}} to provide grouping functionality for PagerDuty incidents.

* fixed type check
  • Loading branch information
YulNaumenko committed Dec 1, 2020
1 parent 5ed39f2 commit 67564b9
Show file tree
Hide file tree
Showing 13 changed files with 92 additions and 14 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ The PagerDuty action uses the [V2 Events API](https://v2.developer.pagerduty.com
| Property | Description | Type |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| eventAction | One of `trigger` _(default)_, `resolve`, or `acknowlege`. See [event action](https://v2.developer.pagerduty.com/docs/events-api-v2#event-action) for more details. | string _(optional)_ |
| dedupKey | All actions sharing this key will be associated with the same PagerDuty alert. Used to correlate trigger and resolution. Defaults to `action:<action id>`. The maximum length is **255** characters. See [alert deduplication](https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication) for details. | string _(optional)_ |
| dedupKey | All actions sharing this key will be associated with the same PagerDuty alert. Used to correlate trigger and resolution. The maximum length is **255** characters. See [alert deduplication](https://v2.developer.pagerduty.com/docs/events-api-v2#alert-de-duplication) for details. | string _(optional)_ |
| summary | A text summary of the event, defaults to `No summary provided`. The maximum length is **1024** characters. | string _(optional)_ |
| source | The affected system, preferably a hostname or fully qualified domain name. Defaults to `Kibana Action <action id>`. | string _(optional)_ |
| severity | The perceived severity of on the affected system. This can be one of `critical`, `error`, `warning` or `info`_(default)_. | string _(optional)_ |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import JiraParamsFields from './jira_params';
import { useGetIssueTypes } from './use_get_issue_types';
import { useGetFieldsByIssueType } from './use_get_fields_by_issue_type';
import { ActionConnector } from '../../../../types';
import { AlertProvidedActionVariables } from '../../../lib/action_variables';
jest.mock('../../../../common/lib/kibana');

jest.mock('./use_get_issue_types');
Expand Down Expand Up @@ -86,7 +87,7 @@ describe('JiraParamsFields renders', () => {
errors={{ title: [] }}
editAction={() => {}}
index={0}
messageVariables={[{ name: 'alertId', description: '' }]}
messageVariables={[{ name: AlertProvidedActionVariables.alertId, description: '' }]}
actionConnector={connector}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { useGetIssueTypes } from './use_get_issue_types';
import { useGetFieldsByIssueType } from './use_get_fields_by_issue_type';
import { SearchIssues } from './search_issues';
import { extractActionVariable } from '../extract_action_variable';
import { AlertProvidedActionVariables } from '../../../lib/action_variables';
import { useKibana } from '../../../../common/lib/kibana';

const JiraParamsFields: React.FunctionComponent<ActionParamsProps<JiraActionParams>> = ({
Expand All @@ -51,7 +52,7 @@ const JiraParamsFields: React.FunctionComponent<ActionParamsProps<JiraActionPara
const [prioritiesSelectOptions, setPrioritiesSelectOptions] = useState<EuiSelectOption[]>([]);

const isActionBeingConfiguredByAnAlert = messageVariables
? isSome(extractActionVariable(messageVariables, 'alertId'))
? isSome(extractActionVariable(messageVariables, AlertProvidedActionVariables.alertId))
: false;

useEffect(() => {
Expand Down Expand Up @@ -144,7 +145,7 @@ const JiraParamsFields: React.FunctionComponent<ActionParamsProps<JiraActionPara
editAction('subAction', 'pushToService', index);
}
if (!savedObjectId && isActionBeingConfiguredByAnAlert) {
editSubActionProperty('savedObjectId', '{{alertId}}');
editSubActionProperty('savedObjectId', `${AlertProvidedActionVariables.alertId}`);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ describe('PagerDutyParamsFields renders', () => {
expect(wrapper.find('[data-test-subj="severitySelect"]').first().prop('value')).toStrictEqual(
'critical'
);
expect(wrapper.find('[data-test-subj="dedupKeyInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="dedupKeyInput"]').first().prop('value')).toStrictEqual(
'test'
);
expect(wrapper.find('[data-test-subj="eventActionSelect"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="dedupKeyInput"]').length > 0).toBeTruthy();
expect(wrapper.find('[data-test-subj="timestampInput"]').length > 0).toBeTruthy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { mountWithIntl } from '@kbn/test/jest';
import ResilientParamsFields from './resilient_params';
import { useGetIncidentTypes } from './use_get_incident_types';
import { useGetSeverity } from './use_get_severity';
import { AlertProvidedActionVariables } from '../../../lib/action_variables';

jest.mock('./use_get_incident_types');
jest.mock('./use_get_severity');
Expand Down Expand Up @@ -82,7 +83,7 @@ describe('ResilientParamsFields renders', () => {
errors={{ title: [] }}
editAction={() => {}}
index={0}
messageVariables={[{ name: 'alertId', description: '' }]}
messageVariables={[{ name: AlertProvidedActionVariables.alertId, description: '' }]}
actionConnector={connector}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { TextFieldWithMessageVariables } from '../../text_field_with_message_var
import { useGetIncidentTypes } from './use_get_incident_types';
import { useGetSeverity } from './use_get_severity';
import { extractActionVariable } from '../extract_action_variable';
import { AlertProvidedActionVariables } from '../../../lib/action_variables';
import { useKibana } from '../../../../common/lib/kibana';

const ResilientParamsFields: React.FunctionComponent<ActionParamsProps<ResilientActionParams>> = ({
Expand All @@ -46,7 +47,7 @@ const ResilientParamsFields: React.FunctionComponent<ActionParamsProps<Resilient
actionParams.subActionParams || {};

const isActionBeingConfiguredByAnAlert = messageVariables
? isSome(extractActionVariable(messageVariables, 'alertId'))
? isSome(extractActionVariable(messageVariables, AlertProvidedActionVariables.alertId))
: false;

const [incidentTypesComboBoxOptions, setIncidentTypesComboBoxOptions] = useState<
Expand Down Expand Up @@ -110,7 +111,7 @@ const ResilientParamsFields: React.FunctionComponent<ActionParamsProps<Resilient
editAction('subAction', 'pushToService', index);
}
if (!savedObjectId && isActionBeingConfiguredByAnAlert) {
editSubActionProperty('savedObjectId', '{{alertId}}');
editSubActionProperty('savedObjectId', `${AlertProvidedActionVariables.alertId}`);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [actionConnector, savedObjectId]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
import ServiceNowParamsFields from './servicenow_params';
import { AlertProvidedActionVariables } from '../../../lib/action_variables';

describe('ServiceNowParamsFields renders', () => {
test('all params fields is rendered', () => {
Expand All @@ -29,7 +30,7 @@ describe('ServiceNowParamsFields renders', () => {
errors={{ title: [] }}
editAction={() => {}}
index={0}
messageVariables={[{ name: 'alertId', description: '' }]}
messageVariables={[{ name: AlertProvidedActionVariables.alertId, description: '' }]}
/>
);
expect(wrapper.find('[data-test-subj="urgencySelect"]').length > 0).toBeTruthy();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ServiceNowActionParams } from './types';
import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables';
import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables';
import { extractActionVariable } from '../extract_action_variable';
import { AlertProvidedActionVariables } from '../../../lib/action_variables';

const ServiceNowParamsFields: React.FunctionComponent<
ActionParamsProps<ServiceNowActionParams>
Expand All @@ -30,7 +31,7 @@ const ServiceNowParamsFields: React.FunctionComponent<
actionParams.subActionParams || {};

const isActionBeingConfiguredByAnAlert = messageVariables
? isSome(extractActionVariable(messageVariables, 'alertId'))
? isSome(extractActionVariable(messageVariables, AlertProvidedActionVariables.alertId))
: false;

const selectOptions = [
Expand Down Expand Up @@ -73,7 +74,7 @@ const ServiceNowParamsFields: React.FunctionComponent<
editAction('subAction', 'pushToService', index);
}
if (!savedObjectId && isActionBeingConfiguredByAnAlert) {
editSubActionProperty('savedObjectId', '{{alertId}}');
editSubActionProperty('savedObjectId', `${AlertProvidedActionVariables.alertId}`);
}
if (!urgency) {
editSubActionProperty('urgency', '3');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface Props {
inputTargetValue?: string;
editAction: (property: string, value: any, index: number) => void;
errors?: string[];
defaultValue?: string | number | string[];
}

export const TextFieldWithMessageVariables: React.FunctionComponent<Props> = ({
Expand All @@ -25,6 +26,7 @@ export const TextFieldWithMessageVariables: React.FunctionComponent<Props> = ({
inputTargetValue,
editAction,
errors,
defaultValue,
}) => {
const [currentTextElement, setCurrentTextElement] = useState<HTMLInputElement | null>(null);

Expand All @@ -51,6 +53,7 @@ export const TextFieldWithMessageVariables: React.FunctionComponent<Props> = ({
isInvalid={errors && errors.length > 0 && inputTargetValue !== undefined}
data-test-subj={`${paramsProperty}Input`}
value={inputTargetValue || ''}
defaultValue={defaultValue}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChangeWithMessageVariable(e)}
onFocus={(e: React.FocusEvent<HTMLInputElement>) => {
setCurrentTextElement(e.target);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export function transformActionVariables(actionVariables: ActionVariables): Acti
return alwaysProvidedVars.concat(contextVars, paramsVars, stateVars);
}

export enum AlertProvidedActionVariables {
alertId = 'alertId',
alertName = 'alertName',
spaceId = 'spaceId',
tags = 'tags',
alertInstanceId = 'alertInstanceId',
}

function prefixKeys(actionVariables: ActionVariable[], prefix: string): ActionVariable[] {
return actionVariables.map((actionVariable) => {
return { name: `${prefix}${actionVariable.name}`, description: actionVariable.description };
Expand All @@ -31,28 +39,28 @@ function getAlwaysProvidedActionVariables(): ActionVariable[] {
const result: ActionVariable[] = [];

result.push({
name: 'alertId',
name: AlertProvidedActionVariables.alertId,
description: i18n.translate('xpack.triggersActionsUI.actionVariables.alertIdLabel', {
defaultMessage: 'The id of the alert.',
}),
});

result.push({
name: 'alertName',
name: AlertProvidedActionVariables.alertName,
description: i18n.translate('xpack.triggersActionsUI.actionVariables.alertNameLabel', {
defaultMessage: 'The name of the alert.',
}),
});

result.push({
name: 'spaceId',
name: AlertProvidedActionVariables.spaceId,
description: i18n.translate('xpack.triggersActionsUI.actionVariables.spaceIdLabel', {
defaultMessage: 'The spaceId of the alert.',
}),
});

result.push({
name: 'tags',
name: AlertProvidedActionVariables.tags,
description: i18n.translate('xpack.triggersActionsUI.actionVariables.tagsLabel', {
defaultMessage: 'The tags of the alert.',
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { ResolvedActionGroup } from '../../../../alerts/common';
import { AlertProvidedActionVariables } from './action_variables';
import { getDefaultsForActionParams } from './get_defaults_for_action_params';

describe('getDefaultsForActionParams', () => {
test('pagerduty defaults', async () => {
expect(getDefaultsForActionParams('.pagerduty', 'test')).toEqual({
dedupKey: `{{${AlertProvidedActionVariables.alertId}}}:{{${AlertProvidedActionVariables.alertInstanceId}}}`,
eventAction: 'trigger',
});
});

test('pagerduty defaults for resolved action group', async () => {
expect(getDefaultsForActionParams('.pagerduty', ResolvedActionGroup.id)).toEqual({
dedupKey: `{{${AlertProvidedActionVariables.alertId}}}:{{${AlertProvidedActionVariables.alertInstanceId}}}`,
eventAction: 'resolve',
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { AlertActionParam, ResolvedActionGroup } from '../../../../alerts/common';
import { AlertProvidedActionVariables } from './action_variables';

export const getDefaultsForActionParams = (
actionTypeId: string,
actionGroupId: string
): Record<string, AlertActionParam> | undefined => {
switch (actionTypeId) {
case '.pagerduty':
const pagerDutyDefaults = {
dedupKey: `{{${AlertProvidedActionVariables.alertId}}}:{{${AlertProvidedActionVariables.alertInstanceId}}}`,
eventAction: 'trigger',
};
if (actionGroupId === ResolvedActionGroup.id) {
pagerDutyDefaults.eventAction = 'resolve';
}
return pagerDutyDefaults;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { ActionAccordionFormProps } from './action_form';
import { transformActionVariables } from '../../lib/action_variables';
import { resolvedActionGroupMessage } from '../../constants';
import { useKibana } from '../../../common/lib/kibana';
import { getDefaultsForActionParams } from '../../lib/get_defaults_for_action_params';

export type ActionTypeFormProps = {
actionItem: AlertAction;
Expand Down Expand Up @@ -108,6 +109,12 @@ export const ActionTypeForm = ({
? resolvedActionGroupMessage
: defaultActionMessage;
setAvailableDefaultActionMessage(res);
const paramsDefaults = getDefaultsForActionParams(actionItem.actionTypeId, actionItem.group);
if (paramsDefaults) {
for (const [key, paramValue] of Object.entries(paramsDefaults)) {
setActionParamsProperty(key, paramValue, index);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [actionItem.group]);

Expand Down

0 comments on commit 67564b9

Please sign in to comment.