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] Enforces typing of Alert's ActionGroups #86761

Merged
merged 17 commits into from
Jan 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 7 additions & 6 deletions x-pack/examples/alerting_example/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ export const ALERTING_EXAMPLE_APP_ID = 'AlertingExample';

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

// Astros
export enum Craft {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,10 @@ export const AlwaysFiringExpression: React.FunctionComponent<
};

interface TShirtSelectorProps {
actionGroup?: ActionGroupWithCondition<number>;
setTShirtThreshold: (actionGroup: ActionGroupWithCondition<number>) => void;
actionGroup?: ActionGroupWithCondition<number, AlwaysFiringActionGroupIds>;
setTShirtThreshold: (
actionGroup: ActionGroupWithCondition<number, AlwaysFiringActionGroupIds>
) => void;
}
const TShirtSelector = ({ actionGroup, setTShirtThreshold }: TShirtSelectorProps) => {
const [isOpen, setIsOpen] = useState(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
DEFAULT_INSTANCES_TO_GENERATE,
ALERTING_EXAMPLE_APP_ID,
AlwaysFiringParams,
AlwaysFiringActionGroupIds,
} from '../../common/constants';

type ActionGroups = 'small' | 'medium' | 'large';
Expand Down Expand Up @@ -39,7 +40,8 @@ export const alertType: AlertType<
AlwaysFiringParams,
{ count?: number },
{ triggerdOnCycle: number },
never
never,
AlwaysFiringActionGroupIds
> = {
id: 'example.always-firing',
name: 'Always firing',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ function getCraftFilter(craft: string) {
export const alertType: AlertType<
{ outerSpaceCapacity: number; craft: string; op: string },
{ peopleInSpace: number },
{ craft: string }
{ craft: string },
never,
'default',
'hasLandedBackOnEarth'
> = {
id: 'example.people-in-space',
name: 'People In Space Right Now',
Expand Down
41 changes: 38 additions & 3 deletions x-pack/plugins/alerts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,41 @@ This example receives server and threshold as parameters. It will read the CPU u

```typescript
import { schema } from '@kbn/config-schema';
import {
Alert,
AlertTypeParams,
AlertTypeState,
AlertInstanceState,
AlertInstanceContext
} from 'x-pack/plugins/alerts/common';
...
server.newPlatform.setup.plugins.alerts.registerType({
interface MyAlertTypeParams extends AlertTypeParams {
server: string;
threshold: number;
}

interface MyAlertTypeState extends AlertTypeState {
lastChecked: number;
}

interface MyAlertTypeInstanceState extends AlertInstanceState {
cpuUsage: number;
}

interface MyAlertTypeInstanceContext extends AlertInstanceContext {
server: string;
hasCpuUsageIncreased: boolean;
}

type MyAlertTypeActionGroups = 'default' | 'warning';

const myAlertType: AlertType<
MyAlertTypeParams,
MyAlertTypeState,
MyAlertTypeInstanceState,
MyAlertTypeInstanceContext,
MyAlertTypeActionGroups
> = {
id: 'my-alert-type',
name: 'My alert type',
validate: {
Expand Down Expand Up @@ -180,7 +213,7 @@ server.newPlatform.setup.plugins.alerts.registerType({
services,
params,
state,
}: AlertExecutorOptions) {
}: AlertExecutorOptions<MyAlertTypeParams, MyAlertTypeState, MyAlertTypeInstanceState, MyAlertTypeInstanceContext, MyAlertTypeActionGroups>) {
// Let's assume params is { server: 'server_1', threshold: 0.8 }
const { server, threshold } = params;

Expand Down Expand Up @@ -219,7 +252,9 @@ server.newPlatform.setup.plugins.alerts.registerType({
};
},
producer: 'alerting',
});
};

server.newPlatform.setup.plugins.alerts.registerType(myAlertType);
```

This example only receives threshold as a parameter. It will read the CPU usage of all the servers and schedule individual actions if the reading for a server is greater than the threshold. This is a better implementation than above as only one query is performed for all the servers instead of one query per server.
Expand Down
22 changes: 16 additions & 6 deletions x-pack/plugins/alerts/common/alert_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,29 @@
*/

import { LicenseType } from '../../licensing/common/types';
import { RecoveredActionGroupId, DefaultActionGroupId } from './builtin_action_groups';

export interface AlertType {
export interface AlertType<
ActionGroupIds extends Exclude<string, RecoveredActionGroupId> = DefaultActionGroupId,
RecoveryActionGroupId extends string = RecoveredActionGroupId
> {
id: string;
name: string;
actionGroups: ActionGroup[];
recoveryActionGroup: ActionGroup;
actionGroups: Array<ActionGroup<ActionGroupIds>>;
recoveryActionGroup: ActionGroup<RecoveryActionGroupId>;
actionVariables: string[];
defaultActionGroupId: ActionGroup['id'];
defaultActionGroupId: ActionGroupIds;
producer: string;
minimumLicenseRequired: LicenseType;
}

export interface ActionGroup {
id: string;
export interface ActionGroup<ActionGroupIds extends string> {
id: ActionGroupIds;
name: string;
}

export type ActionGroupIdsOf<T> = T extends ActionGroup<infer groups>
? groups
: T extends Readonly<ActionGroup<infer groups>>
? groups
: never;
22 changes: 18 additions & 4 deletions x-pack/plugins/alerts/common/builtin_action_groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,27 @@
import { i18n } from '@kbn/i18n';
import { ActionGroup } from './alert_type';

export const RecoveredActionGroup: Readonly<ActionGroup> = {
export type DefaultActionGroupId = 'default';

export type RecoveredActionGroupId = typeof RecoveredActionGroup['id'];
export const RecoveredActionGroup: Readonly<ActionGroup<'recovered'>> = Object.freeze({
id: 'recovered',
name: i18n.translate('xpack.alerts.builtinActionGroups.recovered', {
defaultMessage: 'Recovered',
}),
};
});

export type ReservedActionGroups<RecoveryActionGroupId extends string> =
| RecoveryActionGroupId
| RecoveredActionGroupId;

export type WithoutReservedActionGroups<
ActionGroupIds extends string,
RecoveryActionGroupId extends string
> = ActionGroupIds extends ReservedActionGroups<RecoveryActionGroupId> ? never : ActionGroupIds;

export function getBuiltinActionGroups(customRecoveryGroup?: ActionGroup): ActionGroup[] {
return [customRecoveryGroup ?? Object.freeze(RecoveredActionGroup)];
export function getBuiltinActionGroups<RecoveryActionGroupId extends string>(
customRecoveryGroup?: ActionGroup<RecoveryActionGroupId>
): [ActionGroup<ReservedActionGroups<RecoveryActionGroupId>>] {
return [customRecoveryGroup ?? RecoveredActionGroup];
}
Loading