* Fixing bug * Updating functional test * Fixing functional test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> |
||
---|---|---|
.. | ||
common | ||
public | ||
server | ||
config.ts | ||
jest.config.js | ||
kibana.json | ||
README.md | ||
tsconfig.json |
Kibana Alerts and Actions UI
The Kibana alerts and actions UI plugin provides a user interface for managing alerts and actions. As a developer you can reuse and extend built-in alerts and actions UI functionality:
- Create and register a new Alert Type.
- Create and register a new Action Type.
- Embed the Create Alert flyout within any Kibana plugin.
Table of Contents
- Kibana Alerts and Actions UI
- Build and register Alert Types
- Build and register Action Types
Built-in Alert Types
Kibana ships with several built-in alert types:
Type | Id | Description |
---|---|---|
Index Threshold | threshold |
Index Threshold Alert |
Every alert type must be registered server side, and can optionally be registered client side.
Only alert types registered on both client and server will be displayed in the Create Alert flyout, as a part of the UI.
Built-in alert types UI are located under the folder x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types
and this is a file x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts
for client side registration.
Index Threshold Alert
ID: threshold
In the Kibana UI, this alert type is available as a select card on the Create Alert flyout:
AlertTypeModel:
export function getAlertType(): AlertTypeModel {
return {
id: '.index-threshold',
name: 'Index threshold',
iconClass: 'alert',
alertParamsExpression: lazy(() => import('./index_threshold_expression')),
validate: validateAlertType,
requiresAppContext: false,
};
}
alertParamsExpression should be a lazy loaded React component extending an expression using EuiExpression
components:
interface IndexThresholdProps {
alertParams: IndexThresholdAlertParams;
setAlertParams: (property: string, value: any) => void;
setAlertProperty: (key: string, value: any) => void;
errors: { [key: string]: string[] };
}
Property | Description |
---|---|
alertParams | Set of Alert params relevant for the index threshold alert type. |
setAlertParams | Alert reducer method, which is used to create a new copy of alert object with the changed params property any subproperty value. |
setAlertProperty | Alert reducer method, which is used to create a new copy of alert object with the changed any direct alert property value. |
errors | Alert level errors tracking object. |
Alert reducer is defined on the AlertAdd functional component level and passed down to the subcomponents to provide a new state of Alert object:
const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert });
...
const setAlertProperty = (key: string, value: any) => {
dispatch({ command: { type: 'setProperty' }, payload: { key, value } });
};
const setAlertParams = (key: string, value: any) => {
dispatch({ command: { type: 'setAlertParams' }, payload: { key, value } });
};
const setScheduleProperty = (key: string, value: any) => {
dispatch({ command: { type: 'setScheduleProperty' }, payload: { key, value } });
};
const setActionParamsProperty = (key: string, value: any, index: number) => {
dispatch({ command: { type: 'setAlertActionParams' }, payload: { key, value, index } });
};
const setActionProperty = (key: string, value: any, index: number) => {
dispatch({ command: { type: 'setAlertActionProperty' }, payload: { key, value, index } });
};
'x-pack/plugins/triggers_actions_ui/public/application/sections/alert_add/alert_reducer.ts' define the methods for changing different type of alert properties:
export const alertReducer = (state: any, action: AlertReducerAction) => {
const { command, payload } = action;
const { alert } = state;
switch (command.type) {
// create a new alert state with a new alert value
case 'setAlert': {
....
// create a new alert state with set new value to one alert property by key
case 'setProperty': {
....
// create a new alert state with set new value to any subproperty for a 'schedule' alert property
case 'setScheduleProperty': {
....
// create a new alert state with set new value to action subproperty by index from the array of alert actions
case 'setAlertActionParams': { //
....
// create a new alert state with set new value to any subproperty for a 'params' alert property
case 'setAlertParams': {
const { key, value } = payload;
if (isEqual(alert.params[key], value)) {
return state;
} else {
return {
...state,
alert: {
...alert,
params: {
...alert.params,
[key]: value,
},
},
};
}
}
// create a new alert state with add or remove action from alert actions array
case 'setAlertActionProperty': {
....
}
}
The Expression component should be lazy loaded which means it'll have to be the default export in index_threshold_expression.ts
:
export const IndexThresholdAlertTypeExpression: React.FunctionComponent<IndexThresholdProps> = ({
alertParams,
setAlertParams,
setAlertProperty,
errors,
}) => {
....
// expression validation
const hasExpressionErrors = !!Object.keys(errors).find(
errorKey =>
expressionFieldsWithValidation.includes(errorKey) &&
errors[errorKey].length >= 1 &&
(alertParams as { [key: string]: any })[errorKey] !== undefined
);
....
// loading indeces and set default expression values
useEffect(() => {
getIndexPatterns();
}, []);
useEffect(() => {
setDefaultExpressionValues();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
....
return (
<Fragment>
{hasExpressionErrors ? (
<Fragment>
<EuiSpacer />
<EuiCallOut color="danger" size="s" title={expressionErrorMessage} />
<EuiSpacer />
</Fragment>
) : null}
<EuiSpacer size="l" />
<EuiFormLabel>
<FormattedMessage
defaultMessage="Select Index to query:"
id="xpack.stackAlerts.threshold.ui.selectIndex"
/>
....
</Fragment>
);
};
// Export as default in order to support lazy loading
export {IndexThresholdAlertTypeExpression as default};
Index Threshold Alert form with validation:
Alert type model definition
Each alert type should be defined as AlertTypeModel
object with the these properties:
id: string;
name: string;
iconClass: string;
validate: (alertParams: any) => ValidationResult;
alertParamsExpression: React.LazyExoticComponent<
ComponentType<AlertTypeParamsExpressionProps<AlertParamsType>>
>;
defaultActionMessage?: string;
Property | Description |
---|---|
id | Alert type id. Should be the same as on the server side. |
name | Name of the alert type that will be displayed on the select card in the UI. |
iconClass | Icon of the alert type that will be displayed on the select card in the UI. |
validate | Validation function for the alert params. |
alertParamsExpression | A lazy loaded React component for building UI of the current alert type params. |
defaultActionMessage | Optional property for providing default message for all added actions with message property. |
requiresAppContext | Define if alert type is enabled for create and edit in the alerting management UI. |
IMPORTANT: The current UI supports a single action group only. Action groups are mapped from the server API result for GET /api/alerts/list_alert_types: List alert types. Server side alert type model:
export interface AlertType {
id: string;
name: string;
validate?: {
params?: { validate: (object: any) => any };
};
actionGroups: string[];
executor: ({ services, params, state }: AlertExecutorOptions) => Promise<State | void>;
requiresAppContext: boolean;
}
Only the default (which means first item of the array) action group is displayed in the current UI. Design of user interface and server API for multiple action groups is under discussion and development.
Register alert type model
There are two ways of registering a new alert type:
-
Directly in the
triggers_actions_ui
plugin. In this case, the alert type will be available in the Create Alert flyout of the Alerts and Actions management section. Registration code for a new alert type model should be added to the filex-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts
Only registered alert types are available in UI. -
Register the alert type in another plugin. In this case, the alert type will be available only in the current plugin UI. It should be done by importing dependency
TriggersAndActionsUIPublicPluginSetup
and adding the next code on plugin setup:
function getSomeNewAlertType() {
return { ... } as AlertTypeModel;
}
triggersActionsUi.alertTypeRegistry.register(getSomeNewAlertType());
Create and register new alert type UI example
Before registering a UI for a new Alert Type, you should first register the type on the server-side by following the Alerting guide: https://github.com/elastic/kibana/tree/master/x-pack/plugins/alerts#example
Alert type UI is expected to be defined as AlertTypeModel
object.
Below is a list of steps that should be done to build and register a new alert type with the name Example Alert Type
:
- At any suitable place in Kibana, create a file, which will expose an object implementing interface AlertTypeModel. Example:
import { lazy } from 'react';
import { AlertTypeModel } from '../../../../types';
import { validateExampleAlertType } from './validation';
export function getAlertType(): AlertTypeModel {
return {
id: 'example',
name: 'Example Alert Type',
iconClass: 'bell',
alertParamsExpression: lazy(() => import('./expression')),
validate: validateExampleAlertType,
defaultActionMessage: 'Alert [{{ctx.metadata.name}}] has exceeded the threshold',
requiresAppContext: false,
};
}
Fields of this object AlertTypeModel
will be mapped properly in the UI below.
- Define
alertParamsExpression
asReact.FunctionComponent
- this is the form for filling Alert params based on the current Alert type.
import React, { Fragment, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { WhenExpression, OfExpression } from '../../../../common/expression_items';
import { builtInAggregationTypes } from '../../../../common/constants';
interface ExampleProps {
testAggType?: string;
testAggField?: string;
errors: { [key: string]: string[] };
}
export const ExampleExpression: React.FunctionComponent<ExampleProps> = ({
testAggType,
testAggField,
errors,
}) => {
const [aggType, setAggType] = useState<string>('count');
return (
<Fragment>
<EuiFlexGroup gutterSize="s" wrap>
<EuiFlexItem grow={false}>
<WhenExpression
aggType={testAggType ?? 'count'} // defult is 'count'
onChangeSelectedAggType={(selectedAggType: string) => {
console.log(`Set alert type params field "aggType" value as ${selectedAggType}`);
setAggType(selectedAggType);
}}
/>
</EuiFlexItem>
{aggType && builtInAggregationTypes[aggType].fieldRequired ? (
<EuiFlexItem grow={false}>
<OfExpression
aggField={testAggField}
fields={[{ normalizedType: 'number', name: 'test' }]} // can be some data from server API
aggType={aggType}
errors={errors}
onChangeSelectedAggField={(selectedAggField?: string) =>
console.log(`Set alert type params field "aggField" value as ${selectedAggField}`)
}
/>
</EuiFlexItem>
) : null}
</EuiFlexGroup>
</Fragment>
);
};
// Export as default in order to support lazy loading
export {ExampleExpression as default};
This alert type form becomes available, when the card of Example Alert Type
is selected.
Each expression word here is EuiExpression
component and implements the basic aggregation, grouping and comparison methods.
Expression components, which can be embedded to different alert types, are described here Common expression components.
- Define alert type params validation using the property of
AlertTypeModel
validate
:
import { i18n } from '@kbn/i18n';
import { ValidationResult } from '../../../../types';
export function validateExampleAlertType({
testAggField,
}: {
testAggField: string;
}): ValidationResult {
const validationResult = { errors: {} };
const errors = {
aggField: new Array<string>(),
};
validationResult.errors = errors;
if (!testAggField) {
errors.aggField.push(
i18n.translate('xpack.triggersActionsUI.components.example.error.requiredTestAggFieldText', {
defaultMessage: 'Test aggregation field is required.',
})
);
}
return validationResult;
}
- Extend registration code with the new alert type register in the file
x-pack/plugins/triggers_actions_ui/public/application/components/builtin_alert_types/index.ts
import { getAlertType as getExampledAlertType } from './example';
...
...
alertTypeRegistry.register(getExampledAlertType());
After these four steps, the new Example Alert Type
is available in UI of Create flyout:
Click on the select card for Example Alert Type
to open the expression form that was created in step 2:
Common expression components
WHEN expression component
<WhenExpression
aggType={aggType ?? DEFAULT_VALUES.AGGREGATION_TYPE}
onChangeSelectedAggType={(selectedAggType: string) =>
setAlertParams('aggType', selectedAggType)
}
/>
Props definition:
interface WhenExpressionProps {
aggType: string;
customAggTypesOptions?: { [key: string]: AggregationType };
onChangeSelectedAggType: (selectedAggType: string) => void;
popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
| 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
}
Property | Description |
---|---|
aggType | Selected aggregation type that will be set as the alert type property. |
customAggTypesOptions | (Optional) List of aggregation types that replaces the default options defined in constants x-pack/plugins/triggers_actions_ui/public/common/constants/aggregation_types.ts . |
onChangeSelectedAggType | event handler that will be executed when selected aggregation type is changed. |
popupPosition | (Optional) expression popup position. Default is downLeft . Recommend changing it for a small parent window space. |
OF expression component
OF expression is available, if aggregation type requires selecting data fields for aggregating.
<OfExpression
aggField={aggField}
fields={esFields}
aggType={aggType}
errors={errors}
onChangeSelectedAggField={(selectedAggField?: string) =>
setAlertParams('aggField', selectedAggField)
}
/>
Props definition:
interface OfExpressionProps {
aggType: string;
aggField?: string;
errors: { [key: string]: string[] };
onChangeSelectedAggField: (selectedAggType?: string) => void;
fields: Record<string, any>;
customAggTypesOptions?: {
[key: string]: AggregationType;
};
popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
| 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
}
Property | Description |
---|---|
aggType | Selected aggregation type that will be set as the alert type property. |
aggField | Selected aggregation field that will be set as the alert type property. |
errors | List of errors with proper messages for the alert params that should be validated. In current component is validated aggField . |
onChangeSelectedAggField | Event handler that will be excuted if selected aggregation field is changed. |
fields | Fields list that will be available in the OF Select a field dropdown. |
customAggTypesOptions | (Optional) List of aggregation types that replaces the default options defined in constants x-pack/plugins/triggers_actions_ui/public/common/constants/aggregation_types.ts . |
popupPosition | (Optional) expression popup position. Default is downRight . Recommend changing it for a small parent window space. |
GROUPED BY expression component
<GroupByExpression
groupBy={groupBy || DEFAULT_VALUES.GROUP_BY}
termField={termField}
termSize={termSize}
errors={errors}
fields={esFields}
onChangeSelectedGroupBy={selectedGroupBy => setAlertParams('groupBy', selectedGroupBy)}
onChangeSelectedTermField={selectedTermField =>
setAlertParams('termField', selectedTermField)
}
onChangeSelectedTermSize={selectedTermSize =>
setAlertParams('termSize', selectedTermSize)
}
/>
Props definition:
interface GroupByExpressionProps {
groupBy: string;
termSize?: number;
termField?: string;
errors: { [key: string]: string[] };
onChangeSelectedTermSize: (selectedTermSize?: number) => void;
onChangeSelectedTermField: (selectedTermField?: string) => void;
onChangeSelectedGroupBy: (selectedGroupBy?: string) => void;
fields: Record<string, any>;
customGroupByTypes?: {
[key: string]: GroupByType;
};
popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
| 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
}
Property | Description |
---|---|
groupBy | Selected group by type that will be set as the alert type property. |
termSize | Selected term size that will be set as the alert type property. |
termField | Selected term field that will be set as the alert type property. |
errors | List of errors with proper messages for the alert params that should be validated. In current component is validated termSize and termField . |
onChangeSelectedTermSize | Event handler that will be excuted if selected term size is changed. |
onChangeSelectedTermField | Event handler that will be excuted if selected term field is changed. |
onChangeSelectedGroupBy | Event handler that will be excuted if selected group by is changed. |
fields | Fields list with options for the termField dropdown. |
customGroupByTypes | (Optional) List of group by types that replaces the default options defined in constants x-pack/plugins/triggers_actions_ui/public/common/constants/group_by_types.ts . |
popupPosition | (Optional) expression popup position. Default is downLeft . Recommend changing it for a small parent window space. |
FOR THE LAST expression component
<ForLastExpression
timeWindowSize={timeWindowSize || 1}
timeWindowUnit={timeWindowUnit || ''}
errors={errors}
onChangeWindowSize={(selectedWindowSize: any) =>
setAlertParams('timeWindowSize', selectedWindowSize)
}
onChangeWindowUnit={(selectedWindowUnit: any) =>
setAlertParams('timeWindowUnit', selectedWindowUnit)
}
/>
Props definition:
interface ForLastExpressionProps {
timeWindowSize?: number;
timeWindowUnit?: string;
errors: { [key: string]: string[] };
onChangeWindowSize: (selectedWindowSize: number | '') => void;
onChangeWindowUnit: (selectedWindowUnit: string) => void;
popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
| 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
}
Property | Description |
---|---|
timeWindowSize | Selected time window size that will be set as the alert type property. |
timeWindowUnit | Selected time window unit that will be set as the alert type property. |
errors | List of errors with proper messages for the alert params that should be validated. In current component is validated termWindowSize . |
onChangeWindowSize | Event handler that will be excuted if selected window size is changed. |
onChangeWindowUnit | Event handler that will be excuted if selected window unit is changed. |
popupPosition | (Optional) expression popup position. Default is downLeft . Recommend changing it for a small parent window space. |
THRESHOLD expression component
<ThresholdExpression
thresholdComparator={thresholdComparator ?? DEFAULT_VALUES.THRESHOLD_COMPARATOR}
threshold={threshold}
errors={errors}
onChangeSelectedThreshold={selectedThresholds =>
setAlertParams('threshold', selectedThresholds)
}
onChangeSelectedThresholdComparator={selectedThresholdComparator =>
setAlertParams('thresholdComparator', selectedThresholdComparator)
}
/>
Props definition:
interface ThresholdExpressionProps {
thresholdComparator: string;
errors: { [key: string]: string[] };
onChangeSelectedThresholdComparator: (selectedThresholdComparator?: string) => void;
onChangeSelectedThreshold: (selectedThreshold?: number[]) => void;
customComparators?: {
[key: string]: Comparator;
};
threshold?: number[];
popupPosition?: 'upCenter' | 'upLeft' | 'upRight' | 'downCenter' | 'downLeft'
| 'downRight' | 'leftCenter' | 'leftUp' | 'leftDown' | 'rightCenter' | 'rightUp' | 'rightDown';
}
Property | Description |
---|---|
thresholdComparator | Selected time window size that will be set as the alert type property. |
threshold | Selected time window size that will be set as the alert type property. |
errors | List of errors with proper messages for the alert params that should be validated. In current component is validated threshold0 and threshold1 . |
onChangeSelectedThresholdComparator | Event handler that will be excuted if selected threshold comparator is changed. |
onChangeSelectedThreshold | Event handler that will be excuted if selected threshold is changed. |
customComparators | (Optional) List of comparators that replaces the default options defined in constants x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts . |
popupPosition | (Optional) expression popup position. Default is downLeft . Recommend changing it for a small parent window space. |
Alert Conditions Components
To aid in creating a uniform UX across Alert Types, we provide two components for specifying the conditions for detection of a certain alert under within any specific Action Groups:
AlertConditions
: A component that generates a container which renders custom component for each Action Group which has had its conditions specified.AlertConditionsGroup
: A component that provides a unified container for the Action Group with its name and a button for resetting its condition.
These can be used by any Alert Type to easily create the UI for adding action groups along with an Alert Type specific component.
For Example:
Given an Alert Type which requires different thresholds for each detected Action Group (for example), you might have a ThresholdSpecifier
component for specifying the threshold for a specific Action Group.
const ThresholdSpecifier = (
{
actionGroup,
setThreshold
} : {
actionGroup?: ActionGroupWithCondition<number>;
setThreshold: (actionGroup: ActionGroupWithCondition<number>) => void;
}) => {
if (!actionGroup) {
// render empty if no condition action group is specified
return <Fragment />;
}
return (
<EuiFieldNumber
value={actionGroup.conditions}
onChange={(e) => {
const conditions = parseInt(e.target.value, 10);
if (e.target.value && !isNaN(conditions)) {
setThreshold({
...actionGroup,
conditions,
});
}
}}
/>
);
};
This component takes two props, one which is required (actionGroup
) and one which is alert type specific (setThreshold
).
The actionGroup
will be populated by the AlertConditions
component, but setThreshold
will have to be provided by the AlertType itself.
To understand how this is used, lets take a closer look at actionGroup
:
type ActionGroupWithCondition<T> = ActionGroup &
(
| // allow isRequired=false with or without conditions
{
conditions?: T;
isRequired?: false;
}
// but if isRequired=true then conditions must be specified
| {
conditions: T;
isRequired: true;
}
)
The condition
field is Alert Type specific, and holds whichever type an Alert Type needs for specifying the condition under which a certain detection falls under that specific Action Group.
In our example, this is a number
as that's all we need to speciufy the threshold which dictates whether an alert falls into one actio ngroup rather than another.
The isRequired
field specifies whether this specific action group is required, that is, you can't reset its condition and have to specify a some condition for it.
Using this ThresholdSpecifier
component, we can now use AlertConditionsGroup
& AlertConditions
to enable the user to specify these thresholds for each action group in the alert type.
Like so:
interface ThresholdAlertTypeParams {
thresholds?: {
alert?: number;
warning?: number;
error?: number;
};
}
const DEFAULT_THRESHOLDS: ThresholdAlertTypeParams['threshold] = {
alert: 50,
warning: 80,
error: 90,
};
<AlertConditions
headline={'Set different thresholds for each level'}
actionGroups={[
{
id: 'alert',
name: 'Alert',
condition: DEFAULT_THRESHOLD
},
{
id: 'warning',
name: 'Warning',
},
{
id: 'error',
name: 'Error',
},
]}
onInitializeConditionsFor={(actionGroup) => {
setAlertParams('thresholds', {
...thresholds,
...pick(DEFAULT_THRESHOLDS, actionGroup.id),
});
}}
>
<AlertConditionsGroup
onResetConditionsFor={(actionGroup) => {
setAlertParams('thresholds', omit(thresholds, actionGroup.id));
}}
>
<TShirtSelector
setTShirtThreshold={(actionGroup) => {
setAlertParams('thresholds', {
...thresholds,
[actionGroup.id]: actionGroup.conditions,
});
}}
/>
</AlertConditionsGroup>
</AlertConditions>
The AlertConditions component
This component will render the Conditions
header & headline, along with the selectors for adding every Action Group you specity.
Additionally it will clone its children
for each action group which has a condition
specified for it, passing in the appropriate actionGroup
prop for each one.
Property | Description |
---|---|
headline | The headline title displayed above the fields |
actionGroups | A list of ActionGroupWithCondition which includes all the action group you wish to offer the user and what conditions they are already configured to follow |
onInitializeConditionsFor | A callback which is called when the user ask for a certain actionGroup to be initialized with an initial default condition. If you have no specific default, that's fine, as the component will render the action group's field even if the condition is empty (using a null or an undefined ) and determines whether to render these fields by the very presence of a condition field |
The AlertConditionsGroup component
This component renders a standard EuiTitle foe each action group, wrapping the Alert Type specific component, in addition to a "reset" button which allows the user to reset the condition for that action group. The definition of what a reset actually means is Alert Type specific, and up to the implementor to decide. In some case it might mean removing the condition, in others it might mean to reset it to some default value on the server side. In either case, it should delete the condition
field from the appropriate actionGroup
as per the above example.
Property | Description |
---|---|
onResetConditionsFor | A callback which is called when the user clicks the reset button besides the action group's title. The implementor should use this to remove the condition from the specified actionGroup |
Embed the Create Alert flyout within any Kibana plugin
Follow the instructions bellow to embed the Create Alert flyout within any Kibana plugin:
- Add TriggersAndActionsUIPublicPluginStart to Kibana plugin setup dependencies:
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
Then this dependency will be used to embed Create Alert flyout.
- Add Create Alert flyout to React component using triggersActionsUi start contract:
// in the component state definition section
const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState<boolean>(false);
// UI control item for open flyout
<EuiButton
fill
iconType="plusInCircle"
iconSide="left"
onClick={() => setAlertFlyoutVisibility(true)}
>
<FormattedMessage
id="emptyButton"
defaultMessage="Create alert"
/>
</EuiButton>
const AddAlertFlyout = useMemo(
() =>
triggersActionsUi.getAddAlertFlyout({
consumer: ALERTING_EXAMPLE_APP_ID,
addFlyoutVisible: alertFlyoutVisible,
setAddFlyoutVisibility: setAlertFlyoutVisibility,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[alertFlyoutVisible]
);
// in render section of component
return <>{AddAlertFlyout}</>;
getAddAlertFlyout variables definition:
interface AlertAddProps {
consumer: string;
addFlyoutVisible: boolean;
setAddFlyoutVisibility: React.Dispatch<React.SetStateAction<boolean>>;
alertTypeId?: string;
canChangeTrigger?: boolean;
initialValues?: Partial<Alert>;
reloadAlerts?: () => Promise<void>;
metadata?: MetaData;
}
Property | Description |
---|---|
consumer | Name of the plugin that creates an alert. |
addFlyoutVisible | Visibility state of the Create Alert flyout. |
setAddFlyoutVisibility | Function for changing visibility state of the Create Alert flyout. |
alertTypeId | Optional property to preselect alert type. |
canChangeTrigger | Optional property, that hides change alert type possibility. |
reloadAlerts | Optional function, which will be executed if alert was saved sucsessfuly. |
initialValues | Default values for Alert properties. |
metadata | Optional generic property, which allows to define component specific metadata. This metadata can be used for passing down preloaded data for Alert type expression component. |
Build and register Action Types
Kibana ships with a set of built-in action types UI:
Type | Id | Description |
---|---|---|
Server log | .log |
Logs messages to the Kibana log using server.log() |
.email |
Sends an email using SMTP | |
Slack | .slack |
Posts a message to a Slack channel |
Index | .index |
Indexes document(s) into Elasticsearch |
Webhook | .webhook |
Sends a payload to a web service using HTTP POST or PUT |
PagerDuty | .pagerduty |
Triggers, resolves, or acknowledges an incident to a PagerDuty service |
Every action type should be registered server side, and can be optionally registered client side.
Only action types registered on both client and server will be displayed in the Alerts and Actions UI.
Built-in action types UI is located under the folder x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types
and this is a file x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts
for client side registration.
Server log
Action type model definition:
export function getActionType(): ActionTypeModel {
return {
id: '.server-log',
iconClass: 'logsApp',
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.selectMessageText',
{
defaultMessage: 'Add a message to a Kibana log.',
}
),
actionTypeTitle: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.serverLogAction.actionTypeTitle',
{
defaultMessage: 'Send to Server log',
}
),
validateConnector: (): ValidationResult => {
return { errors: {} };
},
validateParams: (actionParams: ServerLogActionParams): ValidationResult => {
// validation of action params implementation
},
actionConnectorFields: null,
actionParamsFields: ServerLogParamsFields,
};
}
Server log has a connector UI:
and action params form available in Create Alert form:
Action type model definition:
export function getActionType(): ActionTypeModel {
const mailformat = /^[^@\s]+@[^@\s]+$/;
return {
id: '.email',
iconClass: 'email',
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText',
{
defaultMessage: 'Send email from your server.',
}
),
actionTypeTitle: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle',
{
defaultMessage: 'Send to email',
}
),
validateConnector: (action: EmailActionConnector): ValidationResult => {
// validation of connector properties implementation
},
validateParams: (actionParams: EmailActionParams): ValidationResult => {
// validation of action params implementation
},
actionConnectorFields: EmailActionConnectorFields,
actionParamsFields: EmailParamsFields,
};
}
and action params form available in Create Alert form:
Slack
Action type model definition:
export function getActionType(): ActionTypeModel {
return {
id: '.slack',
iconClass: 'logoSlack',
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.selectMessageText',
{
defaultMessage: 'Send a message to a Slack channel or user.',
}
),
actionTypeTitle: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.slackAction.actionTypeTitle',
{
defaultMessage: 'Send to Slack',
}
),
validateConnector: (action: SlackActionConnector): ValidationResult => {
// validation of connector properties implementation
},
validateParams: (actionParams: SlackActionParams): ValidationResult => {
// validation of action params implementation
},
actionConnectorFields: SlackActionFields,
actionParamsFields: SlackParamsFields,
};
}
and action params form available in Create Alert form:
Index
Action type model definition:
export function getActionType(): ActionTypeModel {
return {
id: '.index',
iconClass: 'indexOpen',
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.selectMessageText',
{
defaultMessage: 'Index data into Elasticsearch.',
}
),
validateConnector: (): ValidationResult => {
return { errors: {} };
},
actionConnectorFields: IndexActionConnectorFields,
actionParamsFields: IndexParamsFields,
validateParams: (): ValidationResult => {
return { errors: {} };
},
};
}
and action params form available in Create Alert form:
Example of the index document for Index Threshold alert:
{
"alert_id": "{{alertId}}",
"alert_name": "{{alertName}}",
"alert_instance_id": "{{alertInstanceId}}",
"context_title": "{{context.title}}",
"context_value": "{{context.value}}",
"context_message": "{{context.message}}"
}
Webhook
Action type model definition:
export function getActionType(): ActionTypeModel {
return {
id: '.webhook',
iconClass: 'logoWebhook',
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.selectMessageText',
{
defaultMessage: 'Send a request to a web service.',
}
),
validateConnector: (action: WebhookActionConnector): ValidationResult => {
// validation of connector properties implementation
},
validateParams: (actionParams: WebhookActionParams): ValidationResult => {
// validation of action params implementation
},
actionConnectorFields: WebhookActionConnectorFields,
actionParamsFields: WebhookParamsFields,
};
}
and action params form available in Create Alert form:
PagerDuty
Action type model definition:
export function getActionType(): ActionTypeModel {
return {
id: '.pagerduty',
iconClass: 'apps',
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.selectMessageText',
{
defaultMessage: 'Send an event in PagerDuty.',
}
),
actionTypeTitle: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.actionTypeTitle',
{
defaultMessage: 'Send to PagerDuty',
}
),
validateConnector: (action: PagerDutyActionConnector): ValidationResult => {
// validation of connector properties implementation
},
validateParams: (actionParams: PagerDutyActionParams): ValidationResult => {
// validation of action params implementation
},
actionConnectorFields: PagerDutyActionConnectorFields,
actionParamsFields: PagerDutyParamsFields,
};
}
and action params form available in Create Alert form:
Action type model definition
Each action type should be defined as an ActionTypeModel
object with the following properties:
id: string;
iconClass: string;
selectMessage: string;
actionTypeTitle?: string;
validateConnector: (connector: any) => ValidationResult;
validateParams: (actionParams: any) => ValidationResult;
actionConnectorFields: React.FunctionComponent<any> | null;
actionParamsFields: React.LazyExoticComponent<ComponentType<ActionParamsProps<ActionParams>>>;
Property | Description |
---|---|
id | Action type id. Should be the same as on server side. |
iconClass | Icon of action type, that will be displayed on the select card in UI. |
selectMessage | Short description of action type responsibility, that will be displayed on the select card in UI. |
validateConnector | Validation function for action connector. |
validateParams | Validation function for action params. |
actionConnectorFields | A lazy loaded React component for building UI of current action type connector. |
actionParamsFields | A lazy loaded React component for building UI of current action type params. Displayed as a part of Create Alert flyout. |
Register action type model
There are two ways to register a new action type UI:
-
Directly in
triggers_actions_ui
plugin. In this case, the action type will be available in the Alerts and Actions management section. Registration code for a new action type model should be added to the filex-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts
Only registered action types are available in UI. -
Register action type in another plugin. In this case, the action type will be available only in the current plugin UI. It should be done by importing dependency
TriggersAndActionsUIPublicPluginSetup
and adding the next code on plugin setup:
function getSomeNewActionType() {
return { ... } as ActionTypeModel;
}
triggersActionsUi.actionTypeRegistry.register(getSomeNewActionType());
Create and register new action type UI
Before starting the UI implementation, the server side registration should be done first.
Action type UI is expected to be defined as ActionTypeModel
object.
Below is a list of steps that should be done to build and register a new action type with the name Example Action Type
:
- At any suitable place in Kibana, create a file, which will expose an object implementing interface [ActionTypeModel]:
import React, { Fragment, lazy } from 'react';
import { i18n } from '@kbn/i18n';
import {
ActionTypeModel,
ValidationResult,
ActionConnectorFieldsProps,
ActionParamsProps,
} from '../../../types';
interface ExampleActionParams {
message: string;
}
export function getActionType(): ActionTypeModel {
return {
id: '.example-action',
iconClass: 'logoGmail',
selectMessage: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.exampleAction.selectMessageText',
{
defaultMessage: 'Example Action is used to show how to create new action type UI.',
}
),
actionTypeTitle: i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.exampleAction.actionTypeTitle',
{
defaultMessage: 'Example Action',
}
),
validateConnector: (action: ExampleActionConnector): ValidationResult => {
const validationResult = { errors: {} };
const errors = {
someConnectorField: new Array<string>(),
};
validationResult.errors = errors;
if (!action.config.someConnectorField) {
errors.someConnectorField.push(
i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredSomeConnectorFieldeText',
{
defaultMessage: 'SomeConnectorField is required.',
}
)
);
}
return validationResult;
},
validateParams: (actionParams: ExampleActionParams): ValidationResult => {
const validationResult = { errors: {} };
const errors = {
message: new Array<string>(),
};
validationResult.errors = errors;
if (!actionParams.message?.length) {
errors.message.push(
i18n.translate(
'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredExampleMessageText',
{
defaultMessage: 'Message is required.',
}
)
);
}
return validationResult;
},
actionConnectorFields: lazy(() => import('./example_connector_fields')),
actionParamsFields: lazy(() => import('./example_params_fields')),
};
}
- Define
actionConnectorFields
asReact.FunctionComponent
- this is the form for action connector.
import React, { Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFieldText } from '@elastic/eui';
import { EuiTextArea } from '@elastic/eui';
import {
ActionTypeModel,
ValidationResult,
ActionConnectorFieldsProps,
ActionParamsProps,
} from '../../../types';
interface ExampleActionConnector {
config: {
someConnectorField: string;
};
}
const ExampleConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps<
ExampleActionConnector
>> = ({ action, editActionConfig, errors }) => {
const { someConnectorField } = action.config;
return (
<Fragment>
<EuiFieldText
fullWidth
isInvalid={errors.someConnectorField.length > 0 && someConnectorField !== undefined}
name="someConnectorField"
value={someConnectorField || ''}
onChange={e => {
editActionConfig('someConnectorField', e.target.value);
}}
onBlur={() => {
if (!someConnectorField) {
editActionConfig('someConnectorField', '');
}
}}
/>
</Fragment>
);
};
// Export as default in order to support lazy loading
export {ExampleConnectorFields as default};
- Define action type params fields using the property of
ActionTypeModel
actionParamsFields
:
import React, { Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiFieldText } from '@elastic/eui';
import { EuiTextArea } from '@elastic/eui';
import {
ActionTypeModel,
ValidationResult,
ActionConnectorFieldsProps,
ActionParamsProps,
} from '../../../types';
interface ExampleActionParams {
message: string;
}
const ExampleParamsFields: React.FunctionComponent<ActionParamsProps<ExampleActionParams>> = ({
actionParams,
editAction,
index,
errors,
}) => {
const { message } = actionParams;
return (
<Fragment>
<EuiTextArea
fullWidth
isInvalid={errors.message.length > 0 && message !== undefined}
name="message"
value={message || ''}
onChange={e => {
editAction('message', e.target.value, index);
}}
onBlur={() => {
if (!message) {
editAction('message', '', index);
}
}}
/>
</Fragment>
);
};
// Export as default in order to support lazy loading
export {ExampleParamsFields as default};
- Extend registration code with the new action type register in the file
x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts
import { getActionType as getExampledActionType } from './example';
...
...
actionTypeRegistry.register(getExampledActionType());
After these four steps, the new Example Action Type
is available in UI of Create connector:
Clicking on the select card for Example Action Type
will open the connector form that was created in step 2:
Example Action Type is in the select cards list of Create Alert flyout:
Clicking on the select card for Example Action Type
will open the action type Add Action form:
Embed the Alert Actions form within any Kibana plugin
Follow the instructions bellow to embed the Alert Actions form within any Kibana plugin:
- Add TriggersAndActionsUIPublicPluginSetup and TriggersAndActionsUIPublicPluginStart to Kibana plugin setup dependencies:
import {
TriggersAndActionsUIPublicPluginSetup,
TriggersAndActionsUIPublicPluginStart,
} from '../../../../../x-pack/plugins/triggers_actions_ui/public';
triggersActionsUi: TriggersAndActionsUIPublicPluginSetup;
...
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
Then this dependencies will be used to embed Actions form or register your own action type.
- Add Actions form to React component:
import React, { useCallback } from 'react';
import { ActionForm } from '../../../../../../../../../plugins/triggers_actions_ui/public';
import { AlertAction } from '../../../../../../../../../plugins/triggers_actions_ui/public/types';
const ALOWED_BY_PLUGIN_ACTION_TYPES = [
{ id: '.email', name: 'Email', enabled: true },
{ id: '.index', name: 'Index', enabled: false },
{ id: '.example-action', name: 'Example Action', enabled: false },
];
export const ComponentWithActionsForm: () => {
const { http, triggersActionsUi, notifications } = useKibana().services;
const actionTypeRegistry = triggersActionsUi.actionTypeRegistry;
const initialAlert = ({
name: 'test',
params: {},
consumer: 'alerts',
alertTypeId: '.index-threshold',
schedule: {
interval: '1m',
},
actions: [
{
group: 'default',
id: 'test',
actionTypeId: '.index',
params: {
message: '',
},
},
],
tags: [],
muteAll: false,
enabled: false,
mutedInstanceIds: [],
} as unknown) as Alert;
return (
<ActionForm
actions={initialAlert.actions}
messageVariables={[ { name: 'testVar1', description: 'test var1' } ]}
defaultActionGroupId={'default'}
setActionIdByIndex={(id: string, index: number) => {
initialAlert.actions[index].id = id;
}}
setAlertProperty={(_updatedActions: AlertAction[]) => {}}
setActionParamsProperty={(key: string, value: any, index: number) =>
(initialAlert.actions[index] = { ...initialAlert.actions[index], [key]: value })
}
http={http}
actionTypeRegistry={actionTypeRegistry}
defaultActionMessage={'Alert [{{ctx.metadata.name}}] has exceeded the threshold'}
actionTypes={ALOWED_BY_PLUGIN_ACTION_TYPES}
toastNotifications={notifications.toasts}
consumer={initialAlert.consumer}
/>
);
};
ActionForm Props definition:
interface ActionAccordionFormProps {
actions: AlertAction[];
defaultActionGroupId: string;
actionGroups?: ActionGroup[];
setActionIdByIndex: (id: string, index: number) => void;
setActionGroupIdByIndex?: (group: string, index: number) => void;
setAlertProperty: (actions: AlertAction[]) => void;
setActionParamsProperty: (key: string, value: any, index: number) => void;
http: HttpSetup;
actionTypeRegistry: ActionTypeRegistryContract;
toastNotifications: ToastsSetup;
docLinks: DocLinksStart;
actionTypes?: ActionType[];
messageVariables?: ActionVariable[];
defaultActionMessage?: string;
capabilities: ApplicationStart['capabilities'];
}
Property | Description |
---|---|
actions | List of actions comes from alert.actions property. |
defaultActionGroupId | Default action group id to which each new action will belong by default. |
actionGroups | Optional. List of action groups to which new action can be assigned. The RunWhen field is only displayed when these action groups are specified |
setActionIdByIndex | Function for changing action 'id' by the proper index in alert.actions array. |
setActionGroupIdByIndex | Function for changing action 'group' by the proper index in alert.actions array. |
setAlertProperty | Function for changing alert property 'actions'. Used when deleting action from the array to reset it. |
setActionParamsProperty | Function for changing action key/value property by index in alert.actions array. |
http | HttpSetup needed for executing API calls. |
actionTypeRegistry | Registry for action types. |
toastNotifications | Toast messages Plugin Setup Contract. |
docLinks | Documentation links Plugin Start Contract. |
actionTypes | Optional property, which allows to define a list of available actions specific for a current plugin. |
messageVariables | Optional property, which allows to define a list of variables for action 'message' property. Set useWithTripleBracesInTemplates to true if you don't want the variable escaped when rendering. |
defaultActionMessage | Optional property, which allows to define a message value for action with 'message' property. |
capabilities | Kibana core's Capabilities ApplicationStart['capabilities']. |
Property | Description |
---|---|
reloadAlerts | Optional function, which will be executed if alert was saved sucsessfuly. |
http | HttpSetup needed for executing API calls. |
alertTypeRegistry | Registry for alert types. |
actionTypeRegistry | Registry for action types. |
uiSettings | Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring. |
docLinks | Documentation Links, needed to link to the documentation from informational callouts. |
toastNotifications | Toast messages. |
charts | Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring. |
dataFieldsFormats | Optional property, which is needed to display visualization of alert type expression. Will be changed after visualization refactoring. |
Embed the Create Connector flyout within any Kibana plugin
Follow the instructions bellow to embed the Create Connector flyout within any Kibana plugin:
- Add TriggersAndActionsUIPublicPluginSetup and TriggersAndActionsUIPublicPluginStart to Kibana plugin setup dependencies:
import {
TriggersAndActionsUIPublicPluginSetup,
TriggersAndActionsUIPublicPluginStart,
} from '../../../../../x-pack/plugins/triggers_actions_ui/public';
triggersActionsUi: TriggersAndActionsUIPublicPluginSetup;
...
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
Then this dependency will be used to embed Create Connector flyout or register new action type.
- Add Create Connector flyout to React component:
// import section
import { ActionsConnectorsContextProvider, ConnectorAddFlyout } from '../../../../../../../triggers_actions_ui/public';
// in the component state definition section
const [addFlyoutVisible, setAddFlyoutVisibility] = useState<boolean>(false);
// load required dependancied
const { http, triggersActionsUi, notifications, application, docLinks } = useKibana().services;
const connector = {
secrets: {},
id: 'test',
actionTypeId: '.index',
actionType: 'Index',
name: 'action-connector',
referencedByCount: 0,
config: {},
};
// UI control item for open flyout
<EuiButton
fill
iconType="plusInCircle"
iconSide="left"
onClick={() => setAddFlyoutVisibility(true)}
>
<FormattedMessage
id="emptyButton"
defaultMessage="Create connector"
/>
</EuiButton>
// in render section of component
<ConnectorAddFlyout
addFlyoutVisible={addFlyoutVisible}
setAddFlyoutVisibility={setAddFlyoutVisibility}
actionTypes={[
{
id: '.index',
enabled: true,
name: 'Index',
},
]}
reloadConnectors={reloadConnectors}
consumer={'alerts'}
/>
ConnectorAddFlyout Props definition:
export interface ConnectorAddFlyoutProps {
addFlyoutVisible: boolean;
setAddFlyoutVisibility: React.Dispatch<React.SetStateAction<boolean>>;
actionTypes?: ActionType[];
}
Property | Description |
---|---|
addFlyoutVisible | Visibility state of the Create Connector flyout. |
setAddFlyoutVisibility | Function for changing visibility state of the Create Connector flyout. |
actionTypes | Optional property, that allows to define only specific action types list which is available for a current plugin. |
Embed the Edit Connector flyout within any Kibana plugin
Follow the instructions bellow to embed the Edit Connector flyout within any Kibana plugin:
- Add TriggersAndActionsUIPublicPluginSetup and TriggersAndActionsUIPublicPluginStart to Kibana plugin setup dependencies:
import {
TriggersAndActionsUIPublicPluginSetup,
TriggersAndActionsUIPublicPluginStart,
} from '../../../../../x-pack/plugins/triggers_actions_ui/public';
triggersActionsUi: TriggersAndActionsUIPublicPluginSetup;
...
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
Then this dependency will be used to embed Edit Connector flyout.
- Add Create Connector flyout to React component:
// import section
import { ActionsConnectorsContextProvider, ConnectorEditFlyout } from '../../../../../../../triggers_actions_ui/public';
// in the component state definition section
const [editFlyoutVisible, setEditFlyoutVisibility] = useState<boolean>(false);
// load required dependancied
const { http, triggersActionsUi, notifications, application } = useKibana().services;
// UI control item for open flyout
<EuiButton
fill
iconType="plusInCircle"
iconSide="left"
onClick={() => setEditFlyoutVisibility(true)}
>
<FormattedMessage
id="emptyButton"
defaultMessage="Edit connector"
/>
</EuiButton>
// in render section of component
<ConnectorEditFlyout
initialConnector={editedConnectorItem}
onClose={onCloseEditFlyout}
reloadConnectors={reloadConnectors}
consumer={'alerts'}
/>
ConnectorEditFlyout Props definition:
export interface ConnectorEditProps {
initialConnector: ActionConnector;
onClose: () => void;
tab?: EditConectorTabs;
reloadConnectors?: () => Promise<ActionConnector[] | void>;
consumer?: string;
}
Property | Description |
---|---|
initialConnector | Property, that allows to define the initial state of edited connector. |
editFlyoutVisible | Visibility state of the Edit Connector flyout. |
setEditFlyoutVisibility | Function for changing visibility state of the Edit Connector flyout. |
ActionsConnectorsContextValue options:
export interface ActionsConnectorsContextValue {
http: HttpSetup;
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
toastNotifications: Pick<
ToastsApi,
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
>;
capabilities: ApplicationStart['capabilities'];
reloadConnectors?: () => Promise<void>;
}
Property | Description |
---|---|
http | HttpSetup needed for executing API calls. |
actionTypeRegistry | Registry for action types. |
capabilities | Property, which is defining action current user usage capabilities like canSave or canDelete. |
toastNotifications | Toast messages. |
reloadConnectors | Optional function, which will be executed if connector was saved sucsessfuly, like reload list of connecotrs. |