Skip to content

Commit

Permalink
Add custom action to registry and show actions list in siem (elastic#…
Browse files Browse the repository at this point in the history
…58395)

* Add custom action to registry and show actions list in siem

* Exposed action form as reusable component

* Fixed few small bugs

* Fixed red ci

* Fixed type checks

* Fixed failed tests

* Fixed due to comments

* Fixed type check errors

* Fixed plugin check

* Rebalancing CI groups according to elastic#58930

* Fixed merge issues
  • Loading branch information
YulNaumenko authored Mar 6, 2020
1 parent c4b385d commit 5ff13ad
Show file tree
Hide file tree
Showing 15 changed files with 830 additions and 511 deletions.
165 changes: 155 additions & 10 deletions x-pack/plugins/triggers_actions_ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Table of Contents
- [Action type model definition](#action-type-model-definition)
- [Register action type model](#register-action-type-model)
- [Create and register new action type UI example](#reate-and-register-new-action-type-ui-example)
- [Embed the Alert Actions form within any Kibana plugin](#embed-the-alert-actions-form-within-any-kibana-plugin)
- [Embed the Create Connector flyout within any Kibana plugin](#embed-the-create-connector-flyout-within-any-kibana-plugin)
- [Embed the Edit Connector flyout within any Kibana plugin](#embed-the-edit-connector-flyout-within-any-kibana-plugin)

Expand Down Expand Up @@ -71,7 +72,7 @@ AlertTypeModel:
```
export function getAlertType(): AlertTypeModel {
return {
id: 'threshold',
id: '.index-threshold',
name: 'Index Threshold',
iconClass: 'alert',
alertParamsExpression: IndexThresholdAlertTypeExpression,
Expand Down Expand Up @@ -660,8 +661,6 @@ const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState<boolean>(false);
// in render section of component
<AlertsContextProvider
value={{
addFlyoutVisible: alertFlyoutVisible,
setAddFlyoutVisibility: setAlertFlyoutVisibility,
http,
actionTypeRegistry: triggers_actions_ui.actionTypeRegistry,
alertTypeRegistry: triggers_actions_ui.alertTypeRegistry,
Expand All @@ -672,14 +671,17 @@ const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState<boolean>(false);
metadata: { test: 'some value', fields: ['test'] },
}}
>
<AlertAdd consumer={'watcher'} />
<AlertAdd consumer={'watcher'} addFlyoutVisible={alertFlyoutVisible}
setAddFlyoutVisibility={setAlertFlyoutVisibility} />
</AlertsContextProvider>
```

AlertAdd Props definition:
```
interface AlertAddProps {
consumer: string;
addFlyoutVisible: boolean;
setAddFlyoutVisibility: React.Dispatch<React.SetStateAction<boolean>>;
alertTypeId?: string;
canChangeTrigger?: boolean;
}
Expand All @@ -688,20 +690,20 @@ interface AlertAddProps {
|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.|

AlertsContextProvider value options:
```
export interface AlertsContextValue<MetaData = Record<string, any>> {
addFlyoutVisible: boolean;
setAddFlyoutVisibility: React.Dispatch<React.SetStateAction<boolean>>;
reloadAlerts?: () => Promise<void>;
http: HttpSetup;
alertTypeRegistry: TypeRegistry<AlertTypeModel>;
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
uiSettings?: IUiSettingsClient;
toastNotifications?: Pick<
toastNotifications: Pick<
ToastsApi,
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
>;
Expand All @@ -713,14 +715,12 @@ export interface AlertsContextValue<MetaData = Record<string, any>> {

|Property|Description|
|---|---|
|addFlyoutVisible|Visibility state of the Create Alert flyout.|
|setAddFlyoutVisibility|Function for changing visibility state of the Create Alert flyout.|
|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.|
|toastNotifications|Optional toast messages.|
|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.|
|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.|
Expand Down Expand Up @@ -1204,6 +1204,150 @@ Clicking on the select card for `Example Action Type` will open the action type
or create a new connector:
![Example Action Type with empty connectors list](https://i.imgur.com/EamA9Xv.png)

## Embed the Alert Actions form within any Kibana plugin

Follow the instructions bellow to embed the Alert Actions form within any Kibana plugin:
1. Add TriggersAndActionsUIPublicPluginSetup and TriggersAndActionsUIPublicPluginStart to Kibana plugin setup dependencies:

```
import {
TriggersAndActionsUIPublicPluginSetup,
TriggersAndActionsUIPublicPluginStart,
} from '../../../../../x-pack/plugins/triggers_actions_ui/public';
triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup;
...
triggers_actions_ui: TriggersAndActionsUIPublicPluginStart;
```
Then this dependencies will be used to embed Actions form or register your own action type.

2. 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, triggers_actions_ui, toastNotifications } = useKibana().services;
const actionTypeRegistry = triggers_actions_ui.actionTypeRegistry;
const initialAlert = ({
name: 'test',
params: {},
consumer: 'alerting',
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={['test var1', 'test var2']}
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={toastNotifications}
/>
);
};
```

ActionForm Props definition:
```
interface ActionAccordionFormProps {
actions: AlertAction[];
defaultActionGroupId: string;
setActionIdByIndex: (id: string, index: number) => void;
setAlertProperty: (actions: AlertAction[]) => void;
setActionParamsProperty: (key: string, value: any, index: number) => void;
http: HttpSetup;
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
toastNotifications: Pick<
ToastsApi,
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
>;
actionTypes?: ActionType[];
messageVariables?: string[];
defaultActionMessage?: string;
}
```

|Property|Description|
|---|---|
|actions|List of actions comes from alert.actions property.|
|defaultActionGroupId|Default action group id to which each new action will belong to.|
|setActionIdByIndex|Function for changing action 'id' 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.|
|actionTypes|Optional property, which allowes to define a list of available actions specific for a current plugin.|
|actionTypes|Optional property, which allowes to define a list of variables for action 'message' property.|
|defaultActionMessage|Optional property, which allowes to define a message value for action with 'message' property.|


AlertsContextProvider value options:
```
export interface AlertsContextValue {
reloadAlerts?: () => Promise<void>;
http: HttpSetup;
alertTypeRegistry: TypeRegistry<AlertTypeModel>;
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
uiSettings?: IUiSettingsClient;
toastNotifications: Pick<
ToastsApi,
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
>;
charts?: ChartsPluginSetup;
dataFieldsFormats?: Pick<FieldFormatsRegistry, 'register'>;
}
```

|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.|
|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:
Expand Down Expand Up @@ -1413,3 +1557,4 @@ export interface ActionsConnectorsContextValue {
|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.|

Original file line number Diff line number Diff line change
Expand Up @@ -263,14 +263,14 @@ const EmailActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsP
isInvalid={errors.port.length > 0 && port !== undefined}
fullWidth
name="port"
value={port}
value={port || ''}
data-test-subj="emailPortInput"
onChange={e => {
editActionConfig('port', parseInt(e.target.value, 10));
}}
onBlur={() => {
if (!port) {
editActionConfig('port', '');
editActionConfig('port', 0);
}
}}
/>
Expand Down Expand Up @@ -380,7 +380,7 @@ const EmailParamsFields: React.FunctionComponent<ActionParamsProps<EmailActionPa

const [isVariablesPopoverOpen, setIsVariablesPopoverOpen] = useState<boolean>(false);
useEffect(() => {
if (defaultMessage && defaultMessage.length > 0) {
if (!message && defaultMessage && defaultMessage.length > 0) {
editAction('message', defaultMessage, index);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const ServerLogParamsFields: React.FunctionComponent<ActionParamsProps<

useEffect(() => {
editAction('level', 'info', index);
if (defaultMessage && defaultMessage.length > 0) {
if (!message && defaultMessage && defaultMessage.length > 0) {
editAction('message', defaultMessage, index);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const SlackParamsFields: React.FunctionComponent<ActionParamsProps<SlackActionPa
const { message } = actionParams;
const [isVariablesPopoverOpen, setIsVariablesPopoverOpen] = useState<boolean>(false);
useEffect(() => {
if (defaultMessage && defaultMessage.length > 0) {
if (!message && defaultMessage && defaultMessage.length > 0) {
editAction('message', defaultMessage, index);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export interface AlertsContextValue<MetaData = Record<string, any>> {
http: HttpSetup;
alertTypeRegistry: TypeRegistry<AlertTypeModel>;
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
uiSettings?: IUiSettingsClient;
toastNotifications?: Pick<
toastNotifications: Pick<
ToastsApi,
'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError'
>;
uiSettings?: IUiSettingsClient;
charts?: ChartsPluginSetup;
dataFieldsFormats?: DataPublicPluginSetup['fieldFormats'];
metadata?: MetaData;
Expand Down
Loading

0 comments on commit 5ff13ad

Please sign in to comment.