diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts index e471dd3cea5308..6f8420c8cc4da9 100644 --- a/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts +++ b/x-pack/plugins/alerting/common/routes/rule/apis/create/schemas/v1.ts @@ -63,6 +63,7 @@ export const actionSchema = schema.object({ params: schema.recordOf(schema.string(), schema.maybe(schema.any()), { defaultValue: {} }), frequency: schema.maybe(actionFrequencySchema), alerts_filter: schema.maybe(actionAlertsFilterSchema), + use_alert_data_for_template: schema.maybe(schema.boolean()), }); export const createBodySchema = schema.object({ diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index ff74752df86950..da3788d534598d 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -116,6 +116,7 @@ export interface RuleAction { params: RuleActionParams; frequency?: RuleActionFrequency; alertsFilter?: AlertsFilter; + useAlertDataForTemplate?: boolean; } export interface RuleLastRun { diff --git a/x-pack/plugins/alerting/server/alert/alert.ts b/x-pack/plugins/alerting/server/alert/alert.ts index ee8c48a18447bb..835b8db17a8930 100644 --- a/x-pack/plugins/alerting/server/alert/alert.ts +++ b/x-pack/plugins/alerting/server/alert/alert.ts @@ -6,6 +6,7 @@ */ import { v4 as uuidV4 } from 'uuid'; +import { AADAlert } from '@kbn/alerts-as-data-utils'; import { get, isEmpty } from 'lodash'; import { MutableAlertInstanceMeta } from '@kbn/alerting-state-types'; import { ALERT_UUID } from '@kbn/rule-data-utils'; @@ -36,7 +37,7 @@ export type PublicAlert< Context extends AlertInstanceContext = AlertInstanceContext, ActionGroupIds extends string = DefaultActionGroupId > = Pick< - Alert, + Alert, | 'getContext' | 'getState' | 'getUuid' @@ -50,13 +51,15 @@ export type PublicAlert< export class Alert< State extends AlertInstanceState = AlertInstanceState, Context extends AlertInstanceContext = AlertInstanceContext, - ActionGroupIds extends string = never + ActionGroupIds extends string = never, + AlertAsData extends AADAlert = AADAlert > { private scheduledExecutionOptions?: ScheduledExecutionOptions; private meta: MutableAlertInstanceMeta; private state: State; private context: Context; private readonly id: string; + private alertAsData: AlertAsData | undefined; constructor(id: string, { state, meta = {} }: RawAlertInstance = {}) { this.id = id; @@ -78,6 +81,18 @@ export class Alert< return this.meta.uuid!; } + isAlertAsData() { + return this.alertAsData !== undefined; + } + + setAlertAsData(alertAsData: AlertAsData) { + this.alertAsData = alertAsData; + } + + getAlertAsData() { + return this.alertAsData; + } + getStart(): string | null { return this.state.start ? `${this.state.start}` : null; } diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts index ce51e88c5ce73c..b7e55919969572 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/schemas/create_rule_data_schema.ts @@ -35,6 +35,7 @@ export const createRuleDataSchema = schema.object({ ), uuid: schema.maybe(schema.string()), alertsFilter: schema.maybe(actionAlertsFilterSchema), + useAlertDataForTemplate: schema.maybe(schema.boolean()), }), { defaultValue: [] } ), diff --git a/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts b/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts index f123466eca1ab3..fa8e05f837c254 100644 --- a/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts +++ b/x-pack/plugins/alerting/server/application/rule/schemas/action_schemas.ts @@ -65,6 +65,7 @@ export const actionDomainSchema = schema.object({ params: actionParamsSchema, frequency: schema.maybe(actionFrequencySchema), alertsFilter: schema.maybe(actionDomainAlertsFilterSchema), + useAlertDataAsTemplate: schema.maybe(schema.boolean()), }); /** @@ -89,4 +90,5 @@ export const actionSchema = schema.object({ params: actionParamsSchema, frequency: schema.maybe(actionFrequencySchema), alertsFilter: schema.maybe(actionAlertsFilterSchema), + useAlertDataForTemplate: schema.maybe(schema.boolean()), }); diff --git a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts index 9d6f89e070c3a1..57a089bc9933a8 100644 --- a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts +++ b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts @@ -68,6 +68,7 @@ export const actionsSchema = schema.arrayOf( ), }) ), + use_alert_data_for_template: schema.maybe(schema.boolean()), }), { defaultValue: [] } ); diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts index 2c2b0853d181b4..16309485c18a13 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.ts @@ -15,20 +15,28 @@ export const rewriteActionsReq = ( ): NormalizedAlertAction[] => { if (!actions) return []; - return actions.map(({ frequency, alerts_filter: alertsFilter, ...action }) => { - return { - ...action, - ...(frequency - ? { - frequency: { - ...omit(frequency, 'notify_when'), - notifyWhen: frequency.notify_when, - }, - } - : {}), - ...(alertsFilter ? { alertsFilter } : {}), - }; - }); + return actions.map( + ({ + frequency, + alerts_filter: alertsFilter, + use_alert_data_for_template: useAlertDataForTemplate, + ...action + }) => { + return { + ...action, + useAlertDataForTemplate, + ...(frequency + ? { + frequency: { + ...omit(frequency, 'notify_when'), + notifyWhen: frequency.notify_when, + }, + } + : {}), + ...(alertsFilter ? { alertsFilter } : {}), + }; + } + ); }; export const rewriteActionsRes = (actions?: RuleAction[]) => { @@ -37,14 +45,17 @@ export const rewriteActionsRes = (actions?: RuleAction[]) => { notify_when: notifyWhen, }); if (!actions) return []; - return actions.map(({ actionTypeId, frequency, alertsFilter, ...action }) => ({ - ...action, - connector_type_id: actionTypeId, - ...(frequency ? { frequency: rewriteFrequency(frequency) } : {}), - ...(alertsFilter - ? { - alerts_filter: alertsFilter, - } - : {}), - })); + return actions.map( + ({ actionTypeId, frequency, alertsFilter, useAlertDataForTemplate, ...action }) => ({ + ...action, + connector_type_id: actionTypeId, + use_alert_data_for_template: useAlertDataForTemplate, + ...(frequency ? { frequency: rewriteFrequency(frequency) } : {}), + ...(alertsFilter + ? { + alerts_filter: alertsFilter, + } + : {}), + }) + ); }; diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts index 1b1ed454c52076..9a77d46fb9208c 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/bulk_edit/bulk_edit_rules_route.test.ts @@ -114,6 +114,7 @@ describe('bulkEditRulesRoute', () => { foo: true, }, uuid: '123-456', + use_alert_data_for_template: false, }, ], }), diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts index e952a72ec38590..a12b8ee5e177c2 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/create_rule_route.test.ts @@ -124,6 +124,7 @@ describe('createRuleRoute', () => { }, connector_type_id: 'test', uuid: '123-456', + use_alert_data_for_template: false, }, ], }; @@ -198,6 +199,7 @@ describe('createRuleRoute', () => { "params": Object { "foo": true, }, + "useAlertDataForTemplate": undefined, }, ], "alertTypeId": "1", @@ -314,6 +316,7 @@ describe('createRuleRoute', () => { "params": Object { "foo": true, }, + "useAlertDataForTemplate": undefined, }, ], "alertTypeId": "1", @@ -431,6 +434,7 @@ describe('createRuleRoute', () => { "params": Object { "foo": true, }, + "useAlertDataForTemplate": undefined, }, ], "alertTypeId": "1", @@ -548,6 +552,7 @@ describe('createRuleRoute', () => { "params": Object { "foo": true, }, + "useAlertDataForTemplate": undefined, }, ], "alertTypeId": "1", diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts index c6b29f4577f4c3..a9e9a25d05c95f 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/create/transforms/transform_create_body/v1.ts @@ -15,25 +15,33 @@ import type { RuleParams } from '../../../../../../application/rule/types'; const transformCreateBodyActions = (actions: CreateRuleActionV1[]): CreateRuleData['actions'] => { if (!actions) return []; - return actions.map(({ frequency, alerts_filter: alertsFilter, ...action }) => { - return { - group: action.group, - id: action.id, - params: action.params, - actionTypeId: action.actionTypeId, - ...(action.uuid ? { uuid: action.uuid } : {}), - ...(frequency - ? { - frequency: { - summary: frequency.summary, - throttle: frequency.throttle, - notifyWhen: frequency.notify_when, - }, - } - : {}), - ...(alertsFilter ? { alertsFilter } : {}), - }; - }); + return actions.map( + ({ + frequency, + alerts_filter: alertsFilter, + use_alert_data_for_template: useAlertDataForTemplate, + ...action + }) => { + return { + group: action.group, + id: action.id, + params: action.params, + actionTypeId: action.actionTypeId, + useAlertDataForTemplate, + ...(action.uuid ? { uuid: action.uuid } : {}), + ...(frequency + ? { + frequency: { + summary: frequency.summary, + throttle: frequency.throttle, + notifyWhen: frequency.notify_when, + }, + } + : {}), + ...(alertsFilter ? { alertsFilter } : {}), + }; + } + ); }; export const transformCreateBody = ( diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.test.ts index e83cd05b02edd8..929dc6ad2ccc1c 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.test.ts @@ -45,6 +45,7 @@ describe('resolveRuleRoute', () => { foo: true, }, uuid: '123-456', + useAlertDataForTemplate: false, }, ], consumer: 'bar', @@ -101,6 +102,7 @@ describe('resolveRuleRoute', () => { params: mockedRule.actions[0].params, connector_type_id: mockedRule.actions[0].actionTypeId, uuid: mockedRule.actions[0].uuid, + use_alert_data_for_template: mockedRule.actions[0].useAlertDataForTemplate, }, ], outcome: 'aliasMatch', diff --git a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts index 74508cec9a6405..3968e0e2716209 100644 --- a/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts +++ b/x-pack/plugins/alerting/server/routes/rule/transforms/transform_rule_to_rule_response/v1.ts @@ -49,11 +49,21 @@ export const transformRuleToRuleResponse = ( consumer: rule.consumer, schedule: rule.schedule, actions: rule.actions.map( - ({ group, id, actionTypeId, params, frequency, uuid, alertsFilter }) => ({ + ({ + group, + id, + actionTypeId, + params, + frequency, + uuid, + alertsFilter, + useAlertDataForTemplate, + }) => ({ group, id, params, connector_type_id: actionTypeId, + use_alert_data_for_template: useAlertDataForTemplate ?? false, ...(frequency ? { frequency: { diff --git a/x-pack/plugins/alerting/server/routes/update_rule.test.ts b/x-pack/plugins/alerting/server/routes/update_rule.test.ts index 5a4b3a19c0d7ce..87901feab64cf4 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.test.ts @@ -132,6 +132,7 @@ describe('updateRuleRoute', () => { "params": Object { "baz": true, }, + "useAlertDataForTemplate": undefined, "uuid": "1234-5678", }, ], diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts index 288a4126c25a25..dd1caa21a240cb 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts @@ -20,12 +20,16 @@ import { ActionsClient } from '@kbn/actions-plugin/server/actions_client'; import { chunk } from 'lodash'; import { GetSummarizedAlertsParams, IAlertsClient } from '../alerts_client/types'; import { AlertingEventLogger } from '../lib/alerting_event_logger/alerting_event_logger'; -import { parseDuration, CombinedSummarizedAlerts, ThrottledActions } from '../types'; +import { AlertHit, parseDuration, CombinedSummarizedAlerts, ThrottledActions } from '../types'; import { RuleRunMetricsStore } from '../lib/rule_run_metrics_store'; import { injectActionParams } from './inject_action_params'; import { Executable, ExecutionHandlerOptions, RuleTaskInstance } from './types'; import { TaskRunnerContext } from './task_runner_factory'; -import { transformActionParams, transformSummaryActionParams } from './transform_action_params'; +import { + transformActionParams, + TransformActionParamsOptions, + transformSummaryActionParams, +} from './transform_action_params'; import { Alert } from '../alert'; import { NormalizedRuleType } from '../rule_type_registry'; import { @@ -292,33 +296,40 @@ export class ExecutionHandler< }; } else { const ruleUrl = this.buildRuleUrl(spaceId); + const executableAlert = alert!; + const transformActionParamsOptions: TransformActionParamsOptions = { + actionsPlugin, + alertId: ruleId, + alertType: this.ruleType.id, + actionTypeId, + alertName: this.rule.name, + spaceId, + tags: this.rule.tags, + alertInstanceId: executableAlert.getId(), + alertUuid: executableAlert.getUuid(), + alertActionGroup: actionGroup, + alertActionGroupName: this.ruleTypeActionGroups!.get(actionGroup)!, + context: executableAlert.getContext(), + actionId: action.id, + state: executableAlert.getState(), + kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl, + alertParams: this.rule.params, + actionParams: action.params, + flapping: executableAlert.getFlapping(), + ruleUrl: ruleUrl?.absoluteUrl, + }; + + if (executableAlert.isAlertAsData()) { + transformActionParamsOptions.aadAlert = executableAlert.getAlertAsData(); + } + const actionToRun = { ...action, params: injectActionParams({ actionTypeId, ruleUrl, ruleName: this.rule.name, - actionParams: transformActionParams({ - actionsPlugin, - alertId: ruleId, - alertType: this.ruleType.id, - actionTypeId, - alertName: this.rule.name, - spaceId, - tags: this.rule.tags, - alertInstanceId: alert.getId(), - alertUuid: alert.getUuid(), - alertActionGroup: actionGroup, - alertActionGroupName: this.ruleTypeActionGroups!.get(actionGroup)!, - context: alert.getContext(), - actionId: action.id, - state: alert.getState(), - kibanaBaseUrl: this.taskRunnerContext.kibanaBaseUrl, - alertParams: this.rule.params, - actionParams: action.params, - flapping: alert.getFlapping(), - ruleUrl: ruleUrl?.absoluteUrl, - }), + actionParams: transformActionParams(transformActionParamsOptions), }), }; @@ -570,7 +581,6 @@ export class ExecutionHandler< for (const action of this.rule.actions) { const alertsArray = Object.entries(alerts); let summarizedAlerts = null; - if (this.shouldGetSummarizedAlerts({ action, throttledSummaryActions })) { summarizedAlerts = await this.getSummarizedAlerts({ action, @@ -634,6 +644,15 @@ export class ExecutionHandler< continue; } + if (summarizedAlerts) { + const alertAsData = summarizedAlerts.all.data.find( + (alertHit: AlertHit) => alertHit._id === alert.getUuid() + ); + if (alertAsData) { + alert.setAlertAsData(alertAsData); + } + } + if (action.group === actionGroup && !this.isAlertMuted(alertId)) { if ( this.isRecoveredAlert(action.group) || @@ -667,12 +686,13 @@ export class ExecutionHandler< } return false; } - + if (action.useAlertDataForTemplate) { + return true; + } // we fetch summarizedAlerts to filter alerts in memory as well if (!isSummaryAction(action) && !action.alertsFilter) { return false; } - if ( isSummaryAction(action) && isSummaryActionThrottled({ diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts index 7076e1fc7e35ec..c9c55e07c3d757 100644 --- a/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts +++ b/x-pack/plugins/alerting/server/task_runner/transform_action_params.ts @@ -6,6 +6,7 @@ */ import { PluginStartContract as ActionsPluginStartContract } from '@kbn/actions-plugin/server'; +import { AADAlert } from '@kbn/alerts-as-data-utils'; import { mapKeys, snakeCase } from 'lodash/fp'; import { RuleActionParams, @@ -15,7 +16,7 @@ import { SanitizedRule, } from '../types'; -interface TransformActionParamsOptions { +export interface TransformActionParamsOptions { actionsPlugin: ActionsPluginStartContract; alertId: string; alertType: string; @@ -35,6 +36,7 @@ interface TransformActionParamsOptions { context: AlertInstanceContext; ruleUrl?: string; flapping: boolean; + aadAlert?: AADAlert; } interface SummarizedAlertsWithAll { @@ -76,40 +78,45 @@ export function transformActionParams({ alertParams, ruleUrl, flapping, + aadAlert, }: TransformActionParamsOptions): RuleActionParams { // when the list of variables we pass in here changes, // the UI will need to be updated as well; see: // x-pack/plugins/triggers_actions_ui/public/application/lib/action_variables.ts - const variables = { - alertId, - alertName, - spaceId, - tags, - alertInstanceId, - alertActionGroup, - alertActionGroupName, - context, - date: new Date().toISOString(), - state, - kibanaBaseUrl, - params: alertParams, - rule: { - params: alertParams, - id: alertId, - name: alertName, - type: alertType, - spaceId, - tags, - url: ruleUrl, - }, - alert: { - id: alertInstanceId, - uuid: alertUuid, - actionGroup: alertActionGroup, - actionGroupName: alertActionGroupName, - flapping, - }, - }; + const variables = + aadAlert !== undefined + ? aadAlert + : { + alertId, + alertName, + spaceId, + tags, + alertInstanceId, + alertActionGroup, + alertActionGroupName, + context, + date: new Date().toISOString(), + state, + kibanaBaseUrl, + params: alertParams, + rule: { + params: alertParams, + id: alertId, + name: alertName, + type: alertType, + spaceId, + tags, + url: ruleUrl, + }, + alert: { + id: alertInstanceId, + uuid: alertUuid, + actionGroup: alertActionGroup, + actionGroupName: alertActionGroupName, + flapping, + }, + }; + return actionsPlugin.renderActionParameterTemplates( actionTypeId, actionId, diff --git a/x-pack/plugins/stack_connectors/common/experimental_features.ts b/x-pack/plugins/stack_connectors/common/experimental_features.ts index 4ac02dd9f06db8..b7f1fe2c1b87b2 100644 --- a/x-pack/plugins/stack_connectors/common/experimental_features.ts +++ b/x-pack/plugins/stack_connectors/common/experimental_features.ts @@ -12,7 +12,7 @@ export type ExperimentalFeatures = typeof allowedExperimentalValues; * This object is then used to validate and parse the value entered. */ export const allowedExperimentalValues = Object.freeze({ - isMustacheAutocompleteOn: false, + isMustacheAutocompleteOn: true, sentinelOneConnectorOn: false, }); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/email/email_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/email/email_params.tsx index 2100e2b0d823c7..7da7ae0ce417d4 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/email/email_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/email/email_params.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiComboBox, EuiButtonEmpty, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -15,8 +15,6 @@ import { TextAreaWithMessageVariables, } from '@kbn/triggers-actions-ui-plugin/public'; import { EmailActionParams } from '../types'; -import { getIsExperimentalFeatureEnabled } from '../../common/get_experimental_features'; -import { TextAreaWithAutocomplete } from '../../components/text_area_with_autocomplete'; const noop = () => {}; @@ -32,8 +30,8 @@ export const EmailParamsFields = ({ onBlur = noop, showEmailSubjectAndMessage = true, useDefaultMessage, + ruleTypeId, }: ActionParamsProps) => { - const isMustacheAutocompleteOn = getIsExperimentalFeatureEnabled('isMustacheAutocompleteOn'); const { to, cc, bcc, subject, message } = actionParams; const toOptions = to ? to.map((label: string) => ({ label })) : []; const ccOptions = cc ? cc.map((label: string) => ({ label })) : []; @@ -64,10 +62,6 @@ export const EmailParamsFields = ({ const isBCCInvalid: boolean = errors.bcc !== undefined && errors.bcc.length > 0 && bcc !== undefined; - const TextAreaComponent = useMemo(() => { - return isMustacheAutocompleteOn ? TextAreaWithAutocomplete : TextAreaWithMessageVariables; - }, [isMustacheAutocompleteOn]); - return ( <> )} {showEmailSubjectAndMessage && ( - { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/server_log/server_log_params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/server_log/server_log_params.tsx index 7c742452898333..95314c5fb71269 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/server_log/server_log_params.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/server_log/server_log_params.tsx @@ -37,7 +37,7 @@ export const ServerLogParamsFields: React.FunctionComponent< editAction('level', 'info', index); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [actionParams.level]); const [[isUsingDefault, defaultMessageUsed], setDefaultMessageUsage] = useState< [boolean, string | undefined] diff --git a/x-pack/plugins/stack_connectors/public/connector_types/slack/action_form.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/slack/action_form.test.tsx index 4e86a25ed26d86..ed36daef3abf4c 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/slack/action_form.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/slack/action_form.test.tsx @@ -16,6 +16,7 @@ import { IToasts } from '@kbn/core/public'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { getConnectorType as getSlackConnectorType } from './slack'; import { getSlackApiConnectorType } from '../slack_api'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ @@ -51,6 +52,14 @@ const { loadActionTypes } = jest.requireMock( '@kbn/triggers-actions-ui-plugin/public/application/lib/action_connector_api/connector_types' ); const useKibanaMock = useKibana as jest.Mocked; +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + cacheTime: 0, + }, + }, +}); // GET api/actions/connector_types?feature_id=alerting loadActionTypes.mockResolvedValue([ @@ -95,6 +104,7 @@ actionTypeRegistry.register(getSlackApiConnectorType()); const baseProps = { actions: [], defaultActionGroupId: 'metrics.inventory_threshold.fired', + ruleTypeId: 'metrics.inventory_threshold', hasAlertsMappings: true, featureId: 'alerting', recoveryActionGroup: 'recovered', @@ -170,7 +180,9 @@ describe('ActionForm - Slack API Connector', () => { render( - + + + ); diff --git a/x-pack/plugins/stack_connectors/tsconfig.json b/x-pack/plugins/stack_connectors/tsconfig.json index 4e47b703bf4334..e577ce018bdf3f 100644 --- a/x-pack/plugins/stack_connectors/tsconfig.json +++ b/x-pack/plugins/stack_connectors/tsconfig.json @@ -8,7 +8,7 @@ // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 "server/**/*.json", "common/**/*", - "public/**/*" + "public/**/*", ], "kbn_references": [ "@kbn/core", @@ -33,7 +33,6 @@ "@kbn/core-saved-objects-common", "@kbn/core-http-browser-mocks", "@kbn/core-saved-objects-api-server-mocks", - "@kbn/alerts-ui-shared", "@kbn/alerting-plugin", "@kbn/securitysolution-ecs", "@kbn/ui-theme", diff --git a/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts b/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts index 501d9b7f222397..066487d5af6a49 100644 --- a/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts +++ b/x-pack/plugins/triggers_actions_ui/common/experimental_features.ts @@ -19,6 +19,8 @@ export const allowedExperimentalValues = Object.freeze({ rulesDetailLogs: true, ruleUseExecutionStatus: false, ruleKqlBar: false, + isMustacheAutocompleteOn: false, + showMustacheAutocompleteSwitch: false, }); type ExperimentalConfigKeys = Array; @@ -29,7 +31,8 @@ const allowedKeys = Object.keys(allowedExperimentalValues) as Readonly void; @@ -291,7 +291,6 @@ export const TextAreaWithAutocomplete: React.FunctionComponent closeList(), [closeList]); - const onScroll = useCallback( (evt) => { // FUTURE ENGINEER -> we need to make sure to not close the autocomplete option list diff --git a/x-pack/plugins/stack_connectors/public/lib/filter_suggestions_for_autocomplete.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_autocomplete/lib/filter_suggestions_for_autocomplete.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/lib/filter_suggestions_for_autocomplete.test.ts rename to x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_autocomplete/lib/filter_suggestions_for_autocomplete.test.ts diff --git a/x-pack/plugins/stack_connectors/public/lib/filter_suggestions_for_autocomplete.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_autocomplete/lib/filter_suggestions_for_autocomplete.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/lib/filter_suggestions_for_autocomplete.ts rename to x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_autocomplete/lib/filter_suggestions_for_autocomplete.ts diff --git a/x-pack/plugins/stack_connectors/public/lib/template_action_variable.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_autocomplete/lib/template_action_variable.test.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/lib/template_action_variable.test.ts rename to x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_autocomplete/lib/template_action_variable.test.ts diff --git a/x-pack/plugins/stack_connectors/public/lib/template_action_variable.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_autocomplete/lib/template_action_variable.ts similarity index 100% rename from x-pack/plugins/stack_connectors/public/lib/template_action_variable.ts rename to x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_autocomplete/lib/template_action_variable.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx index c19ac416b46950..e666082638ea71 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/text_area_with_message_variables.tsx @@ -9,6 +9,8 @@ import React, { useState } from 'react'; import { EuiTextArea, EuiFormRow } from '@elastic/eui'; import { ActionVariable } from '@kbn/alerting-plugin/common'; import { AddMessageVariables } from '@kbn/alerts-ui-shared'; +import { getIsExperimentalFeatureEnabled } from '../../common/get_experimental_features'; +import { TextAreaWithAutocomplete } from './text_area_with_autocomplete'; import { templateActionVariable } from '../lib'; interface Props { @@ -23,7 +25,7 @@ interface Props { errors?: string[]; } -export const TextAreaWithMessageVariables: React.FunctionComponent = ({ +const TextAreaWithMessageVariablesLegacy: React.FunctionComponent = ({ messageVariables, paramsProperty, index, @@ -87,3 +89,15 @@ export const TextAreaWithMessageVariables: React.FunctionComponent = ({ ); }; + +export const TextAreaWithMessageVariables = (props: Props) => { + let isMustacheAutocompleteOn; + try { + isMustacheAutocompleteOn = getIsExperimentalFeatureEnabled('isMustacheAutocompleteOn'); + } catch (e) { + isMustacheAutocompleteOn = false; + } + + if (isMustacheAutocompleteOn) return TextAreaWithAutocomplete(props); + return TextAreaWithMessageVariablesLegacy(props); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_template_fields.ts b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_template_fields.ts new file mode 100644 index 00000000000000..99be4c4b118b8a --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_rule_aad_template_fields.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { HttpStart } from '@kbn/core-http-browser'; +import { DataViewField } from '@kbn/data-views-plugin/common'; +import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common'; +import { ActionVariable } from '@kbn/alerting-plugin/common'; +import { useEffect, useMemo, useState } from 'react'; +import { EcsFlat } from '@kbn/ecs'; +import { EcsMetadata } from '@kbn/alerts-as-data-utils/src/field_maps/types'; +import { isEmpty } from 'lodash'; + +export const getDescription = (fieldName: string, ecsFlat: Record) => { + let ecsField = ecsFlat[fieldName]; + if (isEmpty(ecsField?.description ?? '') && fieldName.includes('kibana.alert.')) { + ecsField = ecsFlat[fieldName.replace('kibana.alert.', '')]; + } + return ecsField?.description ?? ''; +}; + +async function loadRuleTypeAadTemplateFields({ + http, + ruleTypeId, +}: { + http: HttpStart; + ruleTypeId: string; +}): Promise { + if (!ruleTypeId || !http) return []; + const fields = await http.get(`${BASE_RAC_ALERTS_API_PATH}/aad_fields`, { + query: { ruleTypeId }, + }); + + return fields; +} + +export function useRuleTypeAadTemplateFields( + http: HttpStart, + ruleTypeId: string | undefined, + enabled: boolean +): { isLoading: boolean; fields: ActionVariable[] } { + // Reimplement useQuery here; this hook is sometimes called in contexts without a QueryClientProvider + const [isLoading, setIsLoading] = useState(false); + const [data, setData] = useState([]); + + useEffect(() => { + if (enabled && data.length === 0 && ruleTypeId) { + setIsLoading(true); + loadRuleTypeAadTemplateFields({ http, ruleTypeId }).then((res) => { + setData(res); + setIsLoading(false); + }); + } + }, [data, enabled, http, ruleTypeId]); + + return useMemo( + () => ({ + isLoading, + fields: data.map((d) => ({ + name: d.name, + description: getDescription(d.name, EcsFlat), + })), + }), + [data, isLoading] + ); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/clone.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/clone.test.ts index 54e987d18c9177..cc9b1ddab02183 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/clone.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/clone.test.ts @@ -75,6 +75,7 @@ describe('cloneRule', () => { "level": "info", "message": "alert ", }, + "useAlertDataForTemplate": undefined, "uuid": "123456", }, ], diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts index e659cfbdfe4bd0..da0cd8419e0785 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/common_transformations.ts @@ -16,11 +16,13 @@ const transformAction: RewriteRequestCase = ({ params, frequency, alerts_filter: alertsFilter, + use_alert_data_for_template: useAlertDataForTemplate, }) => ({ group, id, params, actionTypeId, + useAlertDataForTemplate, ...(frequency ? { frequency: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts index d8b8c85ad26a52..a2ab9c5b4dc3c2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts @@ -27,17 +27,20 @@ const rewriteBodyRequest: RewriteResponseCase = ({ }): any => ({ ...res, rule_type_id: ruleTypeId, - actions: actions.map(({ group, id, params, frequency, alertsFilter }) => ({ - group, - id, - params, - frequency: { - notify_when: frequency!.notifyWhen, - throttle: frequency!.throttle, - summary: frequency!.summary, - }, - alerts_filter: alertsFilter, - })), + actions: actions.map( + ({ group, id, params, frequency, alertsFilter, useAlertDataForTemplate }) => ({ + group, + id, + params, + frequency: { + notify_when: frequency!.notifyWhen, + throttle: frequency!.throttle, + summary: frequency!.summary, + }, + alerts_filter: alertsFilter, + use_alert_data_for_template: useAlertDataForTemplate, + }) + ), }); export async function createRule({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts index ee2418d22262b9..0d6371cfa1fdbd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts @@ -17,18 +17,21 @@ type RuleUpdatesBody = Pick< >; const rewriteBodyRequest: RewriteResponseCase = ({ actions, ...res }): any => ({ ...res, - actions: actions.map(({ group, id, params, frequency, uuid, alertsFilter }) => ({ - group, - id, - params, - frequency: { - notify_when: frequency!.notifyWhen, - throttle: frequency!.throttle, - summary: frequency!.summary, - }, - alerts_filter: alertsFilter, - ...(uuid && { uuid }), - })), + actions: actions.map( + ({ group, id, params, frequency, uuid, alertsFilter, useAlertDataForTemplate }) => ({ + group, + id, + params, + frequency: { + notify_when: frequency!.notifyWhen, + throttle: frequency!.throttle, + summary: frequency!.summary, + }, + alerts_filter: alertsFilter, + use_alert_data_for_template: useAlertDataForTemplate, + ...(uuid && { uuid }), + }) + ), }); export async function updateRule({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index d515cd9510cc6d..6d91e1837b8300 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -60,6 +60,7 @@ export interface ActionAccordionFormProps { defaultActionMessage?: string; setActionIdByIndex: (id: string, index: number) => void; setActionGroupIdByIndex?: (group: string, index: number) => void; + setActionUseAlertDataForTemplate?: (enabled: boolean, index: number) => void; setActions: (actions: RuleAction[]) => void; setActionParamsProperty: (key: string, value: RuleActionParam, index: number) => void; setActionFrequencyProperty: (key: string, value: RuleActionParam, index: number) => void; @@ -70,6 +71,7 @@ export interface ActionAccordionFormProps { ) => void; featureId: string; producerId: string; + ruleTypeId?: string; messageVariables?: ActionVariables; summaryMessageVariables?: ActionVariables; setHasActionsDisabled?: (value: boolean) => void; @@ -84,7 +86,6 @@ export interface ActionAccordionFormProps { minimumThrottleInterval?: [number | undefined, string]; notifyWhenSelectOptions?: NotifyWhenSelectOptions[]; defaultRuleFrequency?: RuleActionFrequency; - ruleTypeId?: string; hasFieldsForAAD?: boolean; disableErrorMessages?: boolean; } @@ -99,6 +100,7 @@ export const ActionForm = ({ defaultActionGroupId, setActionIdByIndex, setActionGroupIdByIndex, + setActionUseAlertDataForTemplate, setActions, setActionParamsProperty, setActionFrequencyProperty, @@ -437,6 +439,7 @@ export const ActionForm = ({ actionConnector={actionConnector} index={index} key={`action-form-action-at-${actionItem.uuid}`} + setActionUseAlertDataForTemplate={setActionUseAlertDataForTemplate} setActionParamsProperty={setActionParamsProperty} setActionFrequencyProperty={setActionFrequencyProperty} setActionAlertsFilterProperty={setActionAlertsFilterProperty} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx index cc0594b365b9ef..8b774325af985b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.test.tsx @@ -64,6 +64,19 @@ jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ useUiSetting: jest.fn().mockImplementation((_, defaultValue) => defaultValue), })); +jest.mock('../../../common/get_experimental_features', () => ({ + getIsExperimentalFeatureEnabled() { + return true; + }, +})); + +jest.mock('../../hooks/use_rule_aad_template_fields', () => ({ + useRuleTypeAadTemplateFields: () => ({ + isLoading: false, + fields: [], + }), +})); + describe('action_type_form', () => { afterEach(() => { jest.clearAllMocks(); @@ -402,6 +415,59 @@ describe('action_type_form', () => { ]); }); + it('clears the default message when the user toggles the "Use template fields from alerts index" switch ', async () => { + const setActionParamsProperty = jest.fn(); + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ + id: '.pagerduty', + iconClass: 'test', + selectMessage: 'test', + validateParams: (): Promise> => { + const validationResult = { errors: {} }; + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + actionParamsFields: mockedActionParamsFields, + defaultActionParams: { + dedupKey: '{{rule.id}}:{{alert.id}}', + eventAction: 'resolve', + }, + }); + actionTypeRegistry.get.mockReturnValue(actionType); + + const wrapper = render( + + {getActionTypeForm({ + index: 1, + ruleTypeId: 'test', + setActionParamsProperty, + actionItem: { + id: '123', + actionTypeId: '.pagerduty', + group: 'recovered', + params: { + eventAction: 'recovered', + dedupKey: '232323', + summary: '2323', + source: 'source', + severity: '1', + timestamp: new Date().toISOString(), + component: 'test', + group: 'group', + class: 'test class', + }, + }, + })} + + ); + + expect(wrapper.getByTestId('mustacheAutocompleteSwitch')).toBeTruthy(); + + await act(async () => { + wrapper.getByTestId('mustacheAutocompleteSwitch').click(); + }); + expect(setActionParamsProperty).toHaveBeenCalledWith('dedupKey', '', 1); + }); + describe('Customize notify when options', () => { it('should not have "On status changes" notify when option for summary actions', async () => { const actionType = actionTypeRegistryMock.createMockActionTypeModel({ @@ -523,6 +589,7 @@ function getActionTypeForm({ onAddConnector, onDeleteAction, onConnectorSelected, + setActionParamsProperty, setActionFrequencyProperty, setActionAlertsFilterProperty, hasAlertsMappings = true, @@ -530,6 +597,7 @@ function getActionTypeForm({ summaryMessageVariables = { context: [], state: [], params: [] }, notifyWhenSelectOptions, defaultNotifyWhenValue, + ruleTypeId, }: { index?: number; actionConnector?: ActionConnector, Record>; @@ -541,12 +609,14 @@ function getActionTypeForm({ onDeleteAction?: () => void; onConnectorSelected?: (id: string) => void; setActionFrequencyProperty?: () => void; + setActionParamsProperty?: () => void; setActionAlertsFilterProperty?: () => void; hasAlertsMappings?: boolean; messageVariables?: ActionVariables; summaryMessageVariables?: ActionVariables; notifyWhenSelectOptions?: NotifyWhenSelectOptions[]; defaultNotifyWhenValue?: RuleNotifyWhenType; + ruleTypeId?: string; }) { const actionConnectorDefault = { actionTypeId: '.pagerduty', @@ -628,7 +698,7 @@ function getActionTypeForm({ onDeleteAction={onDeleteAction ?? jest.fn()} onConnectorSelected={onConnectorSelected ?? jest.fn()} defaultActionGroupId={defaultActionGroupId ?? 'default'} - setActionParamsProperty={jest.fn()} + setActionParamsProperty={setActionParamsProperty ?? jest.fn()} setActionFrequencyProperty={setActionFrequencyProperty ?? jest.fn()} setActionAlertsFilterProperty={setActionAlertsFilterProperty ?? jest.fn()} index={index ?? 1} @@ -641,6 +711,7 @@ function getActionTypeForm({ defaultNotifyWhenValue={defaultNotifyWhenValue} producerId="infrastructure" featureId="infrastructure" + ruleTypeId={ruleTypeId} /> ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 3f0982b1b24f34..83ad089c8e691e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -29,6 +29,7 @@ import { EuiSplitPanel, useEuiTheme, EuiCallOut, + EuiSwitch, } from '@elastic/eui'; import { isEmpty, partition, some } from 'lodash'; import { @@ -42,6 +43,8 @@ import { getDurationUnitValue, parseDuration, } from '@kbn/alerting-plugin/common/parse_duration'; +import { SavedObjectAttribute } from '@kbn/core-saved-objects-api-server'; +import { getIsExperimentalFeatureEnabled } from '../../../common/get_experimental_features'; import { betaBadgeProps } from './beta_badge_props'; import { IErrorObject, @@ -64,6 +67,7 @@ import { validateParamsForWarnings } from '../../lib/validate_params_for_warning import { ActionAlertsFilterTimeframe } from './action_alerts_filter_timeframe'; import { ActionAlertsFilterQuery } from './action_alerts_filter_query'; import { validateActionFilterQuery } from '../../lib/value_validators'; +import { useRuleTypeAadTemplateFields } from '../../hooks/use_rule_aad_template_fields'; export type ActionTypeFormProps = { actionItem: RuleAction; @@ -72,6 +76,7 @@ export type ActionTypeFormProps = { onAddConnector: () => void; onConnectorSelected: (id: string) => void; onDeleteAction: () => void; + setActionUseAlertDataForTemplate?: (enabled: boolean, index: number) => void; setActionParamsProperty: (key: string, value: RuleActionParam, index: number) => void; setActionFrequencyProperty: (key: string, value: RuleActionParam, index: number) => void; setActionAlertsFilterProperty: ( @@ -120,6 +125,7 @@ export const ActionTypeForm = ({ onAddConnector, onConnectorSelected, onDeleteAction, + setActionUseAlertDataForTemplate, setActionParamsProperty, setActionFrequencyProperty, setActionAlertsFilterProperty, @@ -148,7 +154,7 @@ export const ActionTypeForm = ({ }: ActionTypeFormProps) => { const { application: { capabilities }, - http: { basePath }, + http, } = useKibana().services; const { euiTheme } = useEuiTheme(); const [isOpen, setIsOpen] = useState(true); @@ -178,6 +184,53 @@ export const ActionTypeForm = ({ const isSummaryAction = actionItem.frequency?.summary; + const [useAadTemplateFields, setUseAadTemplateField] = useState( + actionItem?.useAlertDataForTemplate ?? false + ); + const [storedActionParamsForAadToggle, setStoredActionParamsForAadToggle] = useState< + Record + >({}); + + const { fields: aadTemplateFields } = useRuleTypeAadTemplateFields( + http, + ruleTypeId, + useAadTemplateFields + ); + + const templateFields = useMemo( + () => (useAadTemplateFields ? aadTemplateFields : availableActionVariables), + [aadTemplateFields, availableActionVariables, useAadTemplateFields] + ); + + let showMustacheAutocompleteSwitch; + try { + showMustacheAutocompleteSwitch = + getIsExperimentalFeatureEnabled('showMustacheAutocompleteSwitch') && ruleTypeId; + } catch (e) { + showMustacheAutocompleteSwitch = false; + } + + const handleUseAadTemplateFields = useCallback(() => { + setUseAadTemplateField((prevVal) => { + if (setActionUseAlertDataForTemplate) { + setActionUseAlertDataForTemplate(!prevVal, index); + } + return !prevVal; + }); + const currentActionParams = { ...actionItem.params }; + for (const key of Object.keys(currentActionParams)) { + setActionParamsProperty(key, storedActionParamsForAadToggle[key] ?? '', index); + } + setStoredActionParamsForAadToggle(currentActionParams); + }, [ + setActionUseAlertDataForTemplate, + storedActionParamsForAadToggle, + setStoredActionParamsForAadToggle, + setActionParamsProperty, + actionItem.params, + index, + ]); + const getDefaultParams = async () => { const connectorType = await actionTypeRegistry.get(actionItem.actionTypeId); let defaultParams; @@ -227,9 +280,15 @@ export const ActionTypeForm = ({ const defaultParams = await getDefaultParams(); if (defaultParams) { for (const [key, paramValue] of Object.entries(defaultParams)) { + const defaultAADParams: typeof defaultParams = {}; if (actionItem.params[key] === undefined || actionItem.params[key] === null) { setActionParamsProperty(key, paramValue, index); + // Add default param to AAD defaults only if it does not contain any template code + if (typeof paramValue !== 'string' || !paramValue.match(/{{.*?}}/g)) { + defaultAADParams[key] = paramValue; + } } + setStoredActionParamsForAadToggle(defaultAADParams); } } })(); @@ -240,9 +299,14 @@ export const ActionTypeForm = ({ (async () => { const defaultParams = await getDefaultParams(); if (defaultParams && actionGroup) { + const defaultAADParams: typeof defaultParams = {}; for (const [key, paramValue] of Object.entries(defaultParams)) { setActionParamsProperty(key, paramValue, index); + if (!paramValue.match(/{{.*?}}/g)) { + defaultAADParams[key] = paramValue; + } } + setStoredActionParamsForAadToggle(defaultAADParams); } })(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -273,6 +337,12 @@ export const ActionTypeForm = ({ })(); }, [actionItem, disableErrorMessages]); + useEffect(() => { + if (isEmpty(storedActionParamsForAadToggle) && actionItem.params.subAction) { + setStoredActionParamsForAadToggle(actionItem.params); + } + }, [actionItem.params, storedActionParamsForAadToggle]); + const canSave = hasSaveActionsCapability(capabilities); const actionGroupDisplay = ( @@ -463,39 +533,54 @@ export const ActionTypeForm = ({ {ParamsFieldsComponent ? ( - - { - setWarning( - validateParamsForWarnings( - value, - basePath.publicBaseUrl, - availableActionVariables - ) - ); - setActionParamsProperty(key, value, i); - }} - messageVariables={availableActionVariables} - defaultMessage={ - // if action is a summary action, show the default summary message - isSummaryAction - ? defaultSummaryMessage - : selectedActionGroup?.defaultActionMessage ?? defaultActionMessage - } - useDefaultMessage={useDefaultMessage} - actionConnector={actionConnector} - executionMode={ActionConnectorMode.ActionForm} - /> - {warning ? ( - <> - - - - ) : null} - + + {showMustacheAutocompleteSwitch && ( + + + + )} + + + { + setWarning( + validateParamsForWarnings( + value, + http.basePath.publicBaseUrl, + availableActionVariables + ) + ); + setActionParamsProperty(key, value, i); + }} + messageVariables={templateFields} + defaultMessage={ + // if action is a summary action, show the default summary message + isSummaryAction + ? defaultSummaryMessage + : selectedActionGroup?.defaultActionMessage ?? defaultActionMessage + } + useDefaultMessage={useDefaultMessage} + actionConnector={actionConnector} + executionMode={ActionConnectorMode.ActionForm} + ruleTypeId={ruleTypeId} + /> + {warning ? ( + <> + + + + ) : null} + + + ) : null} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx index c88e3beba0cadc..c321ba5a01d7af 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx @@ -839,6 +839,9 @@ export const RuleForm = ({ : { ...actionGroup, defaultActionMessage: ruleTypeModel?.defaultActionMessage } )} recoveryActionGroup={recoveryActionGroup} + setActionUseAlertDataForTemplate={(enabled: boolean, index: number) => { + setActionProperty('useAlertDataForTemplate', enabled, index); + }} setActionIdByIndex={(id: string, index: number) => setActionProperty('id', id, index)} setActionGroupIdByIndex={(group: string, index: number) => setActionProperty('group', group, index) diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index fbcfbc0d38297e..bc0ce10d461e6b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -217,6 +217,7 @@ export interface ActionParamsProps { index: number; editAction: (key: string, value: RuleActionParam, index: number) => void; errors: IErrorObject; + ruleTypeId?: string; messageVariables?: ActionVariable[]; defaultMessage?: string; useDefaultMessage?: boolean; diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index d2d91cca3486bf..600c5b6cdc7d53 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -55,7 +55,8 @@ "@kbn/dashboard-plugin", "@kbn/licensing-plugin", "@kbn/expressions-plugin", - "@kbn/serverless", + "@kbn/core-saved-objects-api-server", + "@kbn/serverless" ], "exclude": ["target/**/*"] } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts index b7cf41d07e8236..375eeaaffc44a3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/create.ts @@ -102,6 +102,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { group: 'default', params: {}, uuid: response.body.actions[0].uuid, + use_alert_data_for_template: false, }, ], enabled: true, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts index c948791e5ea49d..ffd11e3fabd38b 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group3/tests/alerting/bulk_edit.ts @@ -116,6 +116,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { params: {}, connector_type_id: 'test.noop', uuid: response.body.rules[0].actions[0].uuid, + use_alert_data_for_template: false, }, ]); expect(response.statusCode).to.eql(200); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts index eb9f90cb41f2a9..0577425103a8c3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/create.ts @@ -78,6 +78,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { group: 'default', params: {}, uuid: response.body.actions[0].uuid, + use_alert_data_for_template: false, }, ], enabled: true, @@ -181,6 +182,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { group: 'default', params: {}, uuid: response.body.actions[0].uuid, + use_alert_data_for_template: false, }, { id: 'my-slack1', @@ -190,6 +192,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { message: 'something important happened!', }, uuid: response.body.actions[1].uuid, + use_alert_data_for_template: false, }, { id: 'system-connector-test.system-action', @@ -197,6 +200,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { connector_type_id: 'test.system-action', params: {}, uuid: response.body.actions[2].uuid, + use_alert_data_for_template: false, }, ], enabled: true,