Skip to content

Commit

Permalink
[7.x] Added default dedupKey value as an {{alertInstanceId}} to provi…
Browse files Browse the repository at this point in the history
…de grouping functionality for PagerDuty incidents. (#83226) (#83985)

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

* Added default dedupKey value as an {{alertInstanceId}} to provide grouping functionality for PagerDuty incidents. Set default savedObjectId as  {{alertInstanceId}} for ServiceNow, Resilient and Jira

* fixed comment

* fixed due to comments

* fixed doc

* fixed due to comments

* fixed type checks
  • Loading branch information
YulNaumenko authored Nov 21, 2020
1 parent 674179d commit 8a0c7be
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 @@ -12,6 +12,7 @@ import { coreMock } from 'src/core/public/mocks';
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('./use_get_issue_types');
jest.mock('./use_get_fields_by_issue_type');
Expand Down Expand Up @@ -90,7 +91,7 @@ describe('JiraParamsFields renders', () => {
errors={{ title: [] }}
editAction={() => {}}
index={0}
messageVariables={[{ name: 'alertId', description: '' }]}
messageVariables={[{ name: AlertProvidedActionVariables.alertId, description: '' }]}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
toastNotifications={mocks.notifications.toasts}
http={mocks.http}
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';

const JiraParamsFields: React.FunctionComponent<ActionParamsProps<JiraActionParams>> = ({
actionParams,
Expand All @@ -48,7 +49,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 @@ -141,7 +142,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 @@ -40,6 +40,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 @@ -11,6 +11,7 @@ import { DocLinksStart } from 'kibana/public';
import { useGetIncidentTypes } from './use_get_incident_types';
import { useGetSeverity } from './use_get_severity';
import { coreMock } from 'src/core/public/mocks';
import { AlertProvidedActionVariables } from '../../../lib/action_variables';

const mocks = coreMock.createSetup();

Expand Down Expand Up @@ -86,7 +87,7 @@ describe('ResilientParamsFields renders', () => {
errors={{ title: [] }}
editAction={() => {}}
index={0}
messageVariables={[{ name: 'alertId', description: '' }]}
messageVariables={[{ name: AlertProvidedActionVariables.alertId, description: '' }]}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
toastNotifications={mocks.notifications.toasts}
http={mocks.http}
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';

const ResilientParamsFields: React.FunctionComponent<ActionParamsProps<ResilientActionParams>> = ({
actionParams,
Expand All @@ -43,7 +44,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 @@ -107,7 +108,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 @@ -8,6 +8,7 @@ import { mountWithIntl } from '@kbn/test/jest';
import ServiceNowParamsFields from './servicenow_params';
import { DocLinksStart } from 'kibana/public';
import { coreMock } from 'src/core/public/mocks';
import { AlertProvidedActionVariables } from '../../../lib/action_variables';

describe('ServiceNowParamsFields renders', () => {
test('all params fields is rendered', () => {
Expand All @@ -32,7 +33,7 @@ describe('ServiceNowParamsFields renders', () => {
errors={{ title: [] }}
editAction={() => {}}
index={0}
messageVariables={[{ name: 'alertId', description: '' }]}
messageVariables={[{ name: AlertProvidedActionVariables.alertId, description: '' }]}
docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart}
toastNotifications={mocks.notifications.toasts}
http={mocks.http}
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<ActionParamsProps<
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<ActionParamsProps<
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 @@ -39,6 +39,7 @@ import { hasSaveActionsCapability } from '../../lib/capabilities';
import { ActionAccordionFormProps } from './action_form';
import { transformActionVariables } from '../../lib/action_variables';
import { resolvedActionGroupMessage } from '../../constants';
import { getDefaultsForActionParams } from '../../lib/get_defaults_for_action_params';

export type ActionTypeFormProps = {
actionItem: AlertAction;
Expand Down Expand Up @@ -110,6 +111,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 8a0c7be

Please sign in to comment.