Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Alerting] Adds generic UI for the definition of conditions for Action Groups #83278

Merged
merged 45 commits into from
Nov 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
188ddd8
extracted index-threshold apis into triggers actions ui
gmmorris Nov 9, 2020
d6459b1
fixed i18n
gmmorris Nov 9, 2020
e02056e
moved Threshold UIs into Stack Alerts
gmmorris Nov 9, 2020
c7ae42d
removed unused imports
gmmorris Nov 10, 2020
56b286d
fixed i18n in geo
gmmorris Nov 10, 2020
aadbe35
Merge branch 'master' into alerts/stack-alerts-public
gmmorris Nov 10, 2020
f3ca392
renamed index apis
gmmorris Nov 10, 2020
b6df73b
renamed index apis to data apis
gmmorris Nov 10, 2020
72f0816
fixed i18n
gmmorris Nov 10, 2020
b94d2c3
fixed import in test
gmmorris Nov 10, 2020
76be068
added basic conditions component
gmmorris Nov 10, 2020
a8d1a8f
updated i18n label
gmmorris Nov 10, 2020
0e3a745
fixed i18n translations
gmmorris Nov 10, 2020
a7f37a9
Merge branch 'master' into alerts/stack-alerts-public
kibanamachine Nov 10, 2020
2f616f1
added stackAlerts to limits
gmmorris Nov 10, 2020
0104cb6
Merge branch 'alerts/stack-alerts-public' of github.com:gmmorris/kiba…
gmmorris Nov 10, 2020
839463f
fixed import
gmmorris Nov 10, 2020
845f359
make defaulted field non maybe
gmmorris Nov 12, 2020
90bcf6d
Merge remote-tracking branch 'upstream/master' into alerts/stack-aler…
gmmorris Nov 12, 2020
74b779a
added generic action group UI
gmmorris Nov 12, 2020
9e2beac
removed import from plugin code as it causes FTR to fail
gmmorris Nov 12, 2020
84ecfdb
Merge branch 'master' into alerts/stack-alerts-public
gmmorris Nov 12, 2020
036889f
fixed typing
gmmorris Nov 12, 2020
2f576ab
refactor always-firing
gmmorris Nov 12, 2020
9730de8
bumped label of action group
gmmorris Nov 12, 2020
34b2c71
Merge remote-tracking branch 'origin/alerts/stack-alerts-public' into…
gmmorris Nov 12, 2020
18698b3
Merge branch 'master' into alerts/action-groups-as-conditions
gmmorris Nov 12, 2020
e3ac6a7
removed unused imports
gmmorris Nov 12, 2020
5307995
rge branch 'master' into alerts/action-groups-as-conditions
gmmorris Nov 12, 2020
142e4a9
Merge branch 'master' into alerts/action-groups-as-conditions
kibanamachine Nov 12, 2020
c20db06
Merge branch 'master' into alerts/action-groups-as-conditions
gmmorris Nov 13, 2020
f809c8b
Merge branch 'alerts/action-groups-as-conditions' of github.com:gmmor…
gmmorris Nov 16, 2020
c54458f
Merge branch 'master' into alerts/action-groups-as-conditions
gmmorris Nov 16, 2020
f2af615
hide uptime flyout if no alertTypeId is selected
gmmorris Nov 16, 2020
f658c78
hide built in action groups in conditions UI
gmmorris Nov 16, 2020
1b1c532
fixed typing
gmmorris Nov 16, 2020
158e1e3
fixed typing in alert flyouts and race condition on alertType id update
gmmorris Nov 17, 2020
2985ee2
Merge branch 'master' into alerts/action-groups-as-conditions
gmmorris Nov 17, 2020
988dc5f
added docs
gmmorris Nov 18, 2020
3bf8f60
Merge branch 'master' into alerts/action-groups-as-conditions
gmmorris Nov 19, 2020
1ec6e70
fixed typing
gmmorris Nov 19, 2020
781975e
use memo to avoid needless rerendering
gmmorris Nov 19, 2020
d799b95
fixed i18n
gmmorris Nov 19, 2020
703898f
Merge branch 'master' into alerts/action-groups-as-conditions
gmmorris Nov 19, 2020
3aceac3
fixed test
gmmorris Nov 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions x-pack/examples/alerting_example/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ export const ALERTING_EXAMPLE_APP_ID = 'AlertingExample';

// always firing
export const DEFAULT_INSTANCES_TO_GENERATE = 5;
export interface AlwaysFiringParams {
instances?: number;
thresholds?: {
small?: number;
medium?: number;
large?: number;
};
}
export type AlwaysFiringActionGroupIds = keyof AlwaysFiringParams['thresholds'];

// Astros
export enum Craft {
Expand Down
147 changes: 131 additions & 16 deletions x-pack/examples/alerting_example/public/alert_types/always_firing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,31 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { Fragment } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiFieldNumber, EuiFormRow } from '@elastic/eui';
import React, { Fragment, useState } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiFieldNumber,
EuiFormRow,
EuiPopover,
EuiExpression,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { AlertTypeModel } from '../../../../plugins/triggers_actions_ui/public';
import { DEFAULT_INSTANCES_TO_GENERATE } from '../../common/constants';

interface AlwaysFiringParamsProps {
alertParams: { instances?: number };
setAlertParams: (property: string, value: any) => void;
errors: { [key: string]: string[] };
}
import { omit, pick } from 'lodash';
import {
ActionGroupWithCondition,
AlertConditions,
AlertConditionsGroup,
AlertTypeModel,
AlertTypeParamsExpressionProps,
AlertsContextValue,
} from '../../../../plugins/triggers_actions_ui/public';
import {
AlwaysFiringParams,
AlwaysFiringActionGroupIds,
DEFAULT_INSTANCES_TO_GENERATE,
} from '../../common/constants';

export function getAlertType(): AlertTypeModel {
return {
Expand All @@ -24,7 +38,7 @@ export function getAlertType(): AlertTypeModel {
iconClass: 'bolt',
documentationUrl: null,
alertParamsExpression: AlwaysFiringExpression,
validate: (alertParams: AlwaysFiringParamsProps['alertParams']) => {
validate: (alertParams: AlwaysFiringParams) => {
const { instances } = alertParams;
const validationResult = {
errors: {
Expand All @@ -44,11 +58,30 @@ export function getAlertType(): AlertTypeModel {
};
}

export const AlwaysFiringExpression: React.FunctionComponent<AlwaysFiringParamsProps> = ({
alertParams,
setAlertParams,
}) => {
const { instances = DEFAULT_INSTANCES_TO_GENERATE } = alertParams;
const DEFAULT_THRESHOLDS: AlwaysFiringParams['thresholds'] = {
small: 0,
medium: 5000,
large: 10000,
};

export const AlwaysFiringExpression: React.FunctionComponent<AlertTypeParamsExpressionProps<
AlwaysFiringParams,
AlertsContextValue
>> = ({ alertParams, setAlertParams, actionGroups, defaultActionGroupId }) => {
const {
instances = DEFAULT_INSTANCES_TO_GENERATE,
thresholds = pick(DEFAULT_THRESHOLDS, defaultActionGroupId),
} = alertParams;

const actionGroupsWithConditions = actionGroups.map((actionGroup) =>
Number.isInteger(thresholds[actionGroup.id as AlwaysFiringActionGroupIds])
? {
...actionGroup,
conditions: thresholds[actionGroup.id as AlwaysFiringActionGroupIds]!,
}
: actionGroup
);

return (
<Fragment>
<EuiFlexGroup gutterSize="s" wrap direction="column">
Expand All @@ -67,6 +100,88 @@ export const AlwaysFiringExpression: React.FunctionComponent<AlwaysFiringParamsP
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexItem grow={true}>
<AlertConditions
headline={'Set different thresholds for randomly generated T-Shirt sizes'}
actionGroups={actionGroupsWithConditions}
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>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
</Fragment>
);
};

interface TShirtSelectorProps {
actionGroup?: ActionGroupWithCondition<number>;
setTShirtThreshold: (actionGroup: ActionGroupWithCondition<number>) => void;
}
const TShirtSelector = ({ actionGroup, setTShirtThreshold }: TShirtSelectorProps) => {
const [isOpen, setIsOpen] = useState(false);

if (!actionGroup) {
return null;
}

return (
<EuiPopover
panelPaddingSize="s"
button={
<EuiExpression
description={'Is Above'}
value={actionGroup.conditions}
isActive={isOpen}
onClick={() => setIsOpen(true)}
/>
}
isOpen={isOpen}
closePopover={() => setIsOpen(false)}
ownFocus
anchorPosition="downLeft"
>
<EuiFlexGroup>
<EuiFlexItem grow={false} style={{ width: 150 }}>
{'Is Above'}
</EuiFlexItem>
<EuiFlexItem grow={false} style={{ width: 100 }}>
<EuiFieldNumber
compressed
value={actionGroup.conditions}
onChange={(e) => {
const conditions = parseInt(e.target.value, 10);
if (e.target.value && !isNaN(conditions)) {
setTShirtThreshold({
...actionGroup,
conditions,
});
}
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPopover>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,56 @@
*/

import uuid from 'uuid';
import { range, random } from 'lodash';
import { range } from 'lodash';
import { AlertType } from '../../../../plugins/alerts/server';
import { DEFAULT_INSTANCES_TO_GENERATE, ALERTING_EXAMPLE_APP_ID } from '../../common/constants';
import {
DEFAULT_INSTANCES_TO_GENERATE,
ALERTING_EXAMPLE_APP_ID,
AlwaysFiringParams,
} from '../../common/constants';

const ACTION_GROUPS = [
{ id: 'small', name: 'small' },
{ id: 'medium', name: 'medium' },
{ id: 'large', name: 'large' },
{ id: 'small', name: 'Small t-shirt' },
{ id: 'medium', name: 'Medium t-shirt' },
{ id: 'large', name: 'Large t-shirt' },
];
const DEFAULT_ACTION_GROUP = 'small';

export const alertType: AlertType = {
function getTShirtSizeByIdAndThreshold(id: string, thresholds: AlwaysFiringParams['thresholds']) {
const idAsNumber = parseInt(id, 10);
if (!isNaN(idAsNumber)) {
if (thresholds?.large && thresholds.large < idAsNumber) {
return 'large';
}
if (thresholds?.medium && thresholds.medium < idAsNumber) {
return 'medium';
}
if (thresholds?.small && thresholds.small < idAsNumber) {
return 'small';
}
}
return DEFAULT_ACTION_GROUP;
}

export const alertType: AlertType<AlwaysFiringParams> = {
id: 'example.always-firing',
name: 'Always firing',
actionGroups: ACTION_GROUPS,
defaultActionGroupId: 'small',
async executor({ services, params: { instances = DEFAULT_INSTANCES_TO_GENERATE }, state }) {
defaultActionGroupId: DEFAULT_ACTION_GROUP,
async executor({
services,
params: { instances = DEFAULT_INSTANCES_TO_GENERATE, thresholds },
state,
}) {
const count = (state.count ?? 0) + 1;

range(instances)
.map(() => ({ id: uuid.v4(), tshirtSize: ACTION_GROUPS[random(0, 2)].id! }))
.forEach((instance: { id: string; tshirtSize: string }) => {
.map(() => uuid.v4())
.forEach((id: string) => {
services
.alertInstanceFactory(instance.id)
.alertInstanceFactory(id)
.replaceState({ triggerdOnCycle: count })
.scheduleActions(instance.tshirtSize);
.scheduleActions(getTShirtSizeByIdAndThreshold(id, thresholds));
});

return {
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/alerts/common/alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { SavedObjectAttributes } from 'kibana/server';
import { SavedObjectAttribute, SavedObjectAttributes } from 'kibana/server';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type AlertTypeState = Record<string, any>;
Expand Down Expand Up @@ -37,6 +37,7 @@ export interface AlertExecutionStatus {
}

export type AlertActionParams = SavedObjectAttributes;
export type AlertActionParam = SavedObjectAttribute;

export interface AlertAction {
group: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import React, { Fragment, FunctionComponent, useEffect, useRef } from 'react';
import { EuiFormRow } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { IErrorObject, AlertsContextValue } from '../../../../../../triggers_actions_ui/public';
import {
IErrorObject,
AlertsContextValue,
AlertTypeParamsExpressionProps,
} from '../../../../../../triggers_actions_ui/public';
import { ES_GEO_FIELD_TYPES } from '../../types';
import { GeoIndexPatternSelect } from '../util_components/geo_index_pattern_select';
import { SingleFieldSelect } from '../util_components/single_field_select';
Expand All @@ -23,7 +27,7 @@ interface Props {
errors: IErrorObject;
setAlertParamsDate: (date: string) => void;
setAlertParamsGeoField: (geoField: string) => void;
setAlertProperty: (alertProp: string, alertParams: unknown) => void;
setAlertProperty: AlertTypeParamsExpressionProps['setAlertProperty'];
setIndexPattern: (indexPattern: IIndexPattern) => void;
indexPattern: IIndexPattern;
isInvalid: boolean;
Expand Down
Loading