mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[7.x] Added default dedupKey value as an {{alertInstanceId}} to provide 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
This commit is contained in:
parent
674179df22
commit
8a0c7bedf8
13 changed files with 92 additions and 14 deletions
|
@ -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)_ |
|
||||
|
|
|
@ -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');
|
||||
|
@ -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}
|
||||
|
|
|
@ -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,
|
||||
|
@ -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(() => {
|
||||
|
@ -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
|
||||
}, [
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
@ -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}
|
||||
|
|
|
@ -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,
|
||||
|
@ -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<
|
||||
|
@ -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]);
|
||||
|
|
|
@ -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', () => {
|
||||
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
@ -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 = [
|
||||
|
@ -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');
|
||||
|
|
|
@ -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> = ({
|
||||
|
@ -25,6 +26,7 @@ export const TextFieldWithMessageVariables: React.FunctionComponent<Props> = ({
|
|||
inputTargetValue,
|
||||
editAction,
|
||||
errors,
|
||||
defaultValue,
|
||||
}) => {
|
||||
const [currentTextElement, setCurrentTextElement] = useState<HTMLInputElement | null>(null);
|
||||
|
||||
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
|
@ -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.',
|
||||
}),
|
||||
|
|
|
@ -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',
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue