Skip to content

Commit

Permalink
[Response Ops][Rule Form V2] Rule Form V2: Rule Definition (#183325)
Browse files Browse the repository at this point in the history
## Summary
Issue: #179105
Related PR: #180539

Part 1 of 3 PRs of new rule form. This PR extracts the first section of
the rule form, the rule definition, from the original PR. The purpose is
to fix a few bugs (Such as improving the alert delay and the rule
schedule input validation), and also try to make the PR much smaller for
review. The design philosophy in the PR is to create components that are
devoid of any fetching or form logic. These are simply dumb components.

I have also created a example plugin to demonstrate this PR. To access: 

1. Run the branch with `yarn start --run-examples`
2. Navigate to
`http://localhost:5601/app/triggersActionsUiExample/rule_definition`

And you should be able to play around with the components in this PR:

<img width="1257" alt="Screenshot 2024-05-13 at 10 10 51 AM"
src="https://github.com/elastic/kibana/assets/74562234/a1ab6d96-946d-4bf6-94e2-6aa903d0b8f5">

### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Zacqary <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
3 people authored May 31, 2024
1 parent 37215fb commit dc3f76b
Show file tree
Hide file tree
Showing 38 changed files with 2,545 additions and 265 deletions.
5 changes: 4 additions & 1 deletion packages/kbn-alerting-types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
*/

export * from './builtin_action_groups_types';
export * from './rule_type';
export * from './rule_type_types';
export * from './action_group_types';
export * from './alert_type';
export * from './rule_notify_when_type';
export * from './r_rule_types';
export * from './rule_types';
19 changes: 19 additions & 0 deletions packages/kbn-alerting-types/r_rule_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { WeekdayStr, Options } from '@kbn/rrule';

export type RRuleParams = Partial<RRuleRecord> & Pick<RRuleRecord, 'dtstart' | 'tzid'>;

// An iCal RRULE to define a recurrence schedule, see https://github.com/jakubroztocil/rrule for the spec
export type RRuleRecord = Omit<Options, 'dtstart' | 'byweekday' | 'wkst' | 'until'> & {
dtstart: string;
byweekday?: Array<WeekdayStr | string | number>;
wkst?: WeekdayStr;
until?: string;
};
21 changes: 21 additions & 0 deletions packages/kbn-alerting-types/rule_notify_when_type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export const RuleNotifyWhenTypeValues = [
'onActionGroupChange',
'onActiveAlert',
'onThrottleInterval',
] as const;

export type RuleNotifyWhenType = typeof RuleNotifyWhenTypeValues[number];

export enum RuleNotifyWhen {
CHANGE = 'onActionGroupChange',
ACTIVE = 'onActiveAlert',
THROTTLE = 'onThrottleInterval',
}
File renamed without changes.
241 changes: 241 additions & 0 deletions packages/kbn-alerting-types/rule_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { SavedObjectAttributes } from '@kbn/core/server';
import type { Filter } from '@kbn/es-query';
import type { RuleNotifyWhenType, RRuleParams } from '.';

export type RuleTypeParams = Record<string, unknown>;
export type RuleActionParams = SavedObjectAttributes;

export const ISO_WEEKDAYS = [1, 2, 3, 4, 5, 6, 7] as const;
export type IsoWeekday = typeof ISO_WEEKDAYS[number];

export interface IntervalSchedule extends SavedObjectAttributes {
interval: string;
}

export interface RuleActionFrequency extends SavedObjectAttributes {
summary: boolean;
notifyWhen: RuleNotifyWhenType;
throttle: string | null;
}

export interface AlertsFilterTimeframe extends SavedObjectAttributes {
days: IsoWeekday[];
timezone: string;
hours: {
start: string;
end: string;
};
}

export interface AlertsFilter extends SavedObjectAttributes {
query?: {
kql: string;
filters: Filter[];
dsl?: string; // This fields is generated in the code by using "kql", therefore it's not optional but defined as optional to avoid modifying a lot of files in different plugins
};
timeframe?: AlertsFilterTimeframe;
}

export interface RuleAction {
uuid?: string;
group: string;
id: string;
actionTypeId: string;
params: RuleActionParams;
frequency?: RuleActionFrequency;
alertsFilter?: AlertsFilter;
useAlertDataForTemplate?: boolean;
}

export interface RuleSystemAction {
uuid?: string;
id: string;
actionTypeId: string;
params: RuleActionParams;
}

export interface MappedParamsProperties {
risk_score?: number;
severity?: string;
}

export type MappedParams = SavedObjectAttributes & MappedParamsProperties;

// for the `typeof ThingValues[number]` types below, become string types that
// only accept the values in the associated string arrays
export const RuleExecutionStatusValues = [
'ok',
'active',
'error',
'pending',
'unknown',
'warning',
] as const;

export const RuleLastRunOutcomeValues = ['succeeded', 'warning', 'failed'] as const;

export enum RuleExecutionStatusErrorReasons {
Read = 'read',
Decrypt = 'decrypt',
Execute = 'execute',
Unknown = 'unknown',
License = 'license',
Timeout = 'timeout',
Disabled = 'disabled',
Validate = 'validate',
}

export enum RuleExecutionStatusWarningReasons {
MAX_EXECUTABLE_ACTIONS = 'maxExecutableActions',
MAX_ALERTS = 'maxAlerts',
MAX_QUEUED_ACTIONS = 'maxQueuedActions',
}

export type RuleExecutionStatuses = typeof RuleExecutionStatusValues[number];
export type RuleLastRunOutcomes = typeof RuleLastRunOutcomeValues[number];

export interface RuleExecutionStatus {
status: RuleExecutionStatuses;
lastExecutionDate: Date;
lastDuration?: number;
error?: {
reason: RuleExecutionStatusErrorReasons;
message: string;
};
warning?: {
reason: RuleExecutionStatusWarningReasons;
message: string;
};
}

export interface RuleMonitoringHistory extends SavedObjectAttributes {
success: boolean;
timestamp: number;
duration?: number;
outcome?: RuleLastRunOutcomes;
}

export interface RuleMonitoringCalculatedMetrics extends SavedObjectAttributes {
p50?: number;
p95?: number;
p99?: number;
success_ratio: number;
}

export interface RuleMonitoringLastRunMetrics extends SavedObjectAttributes {
duration?: number;
total_search_duration_ms?: number | null;
total_indexing_duration_ms?: number | null;
total_alerts_detected?: number | null;
total_alerts_created?: number | null;
gap_duration_s?: number | null;
}

export interface RuleMonitoringLastRun extends SavedObjectAttributes {
timestamp: string;
metrics: RuleMonitoringLastRunMetrics;
}

export interface RuleMonitoring {
run: {
history: RuleMonitoringHistory[];
calculated_metrics: RuleMonitoringCalculatedMetrics;
last_run: RuleMonitoringLastRun;
};
}

export interface RuleSnoozeSchedule {
duration: number;
rRule: RRuleParams;
// For scheduled/recurring snoozes, `id` uniquely identifies them so that they can be displayed, modified, and deleted individually
id?: string;
skipRecurrences?: string[];
}

// Type signature of has to be repeated here to avoid issues with SavedObject compatibility
// RuleSnooze = RuleSnoozeSchedule[] throws typescript errors across the whole lib
export type RuleSnooze = Array<{
duration: number;
rRule: RRuleParams;
id?: string;
skipRecurrences?: string[];
}>;

export interface RuleLastRun {
outcome: RuleLastRunOutcomes;
outcomeOrder?: number;
warning?: RuleExecutionStatusErrorReasons | RuleExecutionStatusWarningReasons | null;
outcomeMsg?: string[] | null;
alertsCount: {
active?: number | null;
new?: number | null;
recovered?: number | null;
ignored?: number | null;
};
}

export interface AlertDelay extends SavedObjectAttributes {
active: number;
}

export interface SanitizedAlertsFilter extends AlertsFilter {
query?: {
kql: string;
filters: Filter[];
};
timeframe?: AlertsFilterTimeframe;
}

export type SanitizedRuleAction = Omit<RuleAction, 'alertsFilter'> & {
alertsFilter?: SanitizedAlertsFilter;
};

export interface Rule<Params extends RuleTypeParams = never> {
id: string;
enabled: boolean;
name: string;
tags: string[];
alertTypeId: string; // this is persisted in the Rule saved object so we would need a migration to change this to ruleTypeId
consumer: string;
schedule: IntervalSchedule;
actions: RuleAction[];
systemActions?: RuleSystemAction[];
params: Params;
mapped_params?: MappedParams;
scheduledTaskId?: string | null;
createdBy: string | null;
updatedBy: string | null;
createdAt: Date;
updatedAt: Date;
apiKey: string | null;
apiKeyOwner: string | null;
apiKeyCreatedByUser?: boolean | null;
throttle?: string | null;
muteAll: boolean;
notifyWhen?: RuleNotifyWhenType | null;
mutedInstanceIds: string[];
executionStatus: RuleExecutionStatus;
monitoring?: RuleMonitoring;
snoozeSchedule?: RuleSnooze; // Remove ? when this parameter is made available in the public API
activeSnoozes?: string[];
isSnoozedUntil?: Date | null;
lastRun?: RuleLastRun | null;
nextRun?: Date | null;
revision: number;
running?: boolean | null;
viewInAppRelativeUrl?: string;
alertDelay?: AlertDelay;
}

export type SanitizedRule<Params extends RuleTypeParams = never> = Omit<
Rule<Params>,
'apiKey' | 'actions'
> & { actions: SanitizedRuleAction[] };
5 changes: 4 additions & 1 deletion packages/kbn-alerting-types/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"kbn_references": [
"@kbn/i18n",
"@kbn/licensing-plugin",
"@kbn/rule-data-utils"
"@kbn/rule-data-utils",
"@kbn/rrule",
"@kbn/core",
"@kbn/es-query"
]
}
2 changes: 2 additions & 0 deletions packages/kbn-alerts-ui-shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ export * from './src/alert_fields_table';

export * from './src/rule_type_modal';
export * from './src/alert_filter_controls/types';
export * from './src/rule_form';
export * from './src/common/hooks';
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@ import { RuleTypeIndexWithDescriptions, RuleTypeWithDescription } from '../types
export interface UseRuleTypesProps {
http: HttpStart;
toasts: ToastsStart;
filteredRuleTypes: string[];
filteredRuleTypes?: string[];
registeredRuleTypes?: Array<{ id: string; description: string }>;
enabled?: boolean;
}

const getFilteredIndex = (
data: Array<RuleType<string, string>>,
filteredRuleTypes: string[],
registeredRuleTypes: UseRuleTypesProps['registeredRuleTypes']
) => {
const getFilteredIndex = ({
data,
filteredRuleTypes,
registeredRuleTypes,
}: {
data: Array<RuleType<string, string>>;
filteredRuleTypes?: string[];
registeredRuleTypes: UseRuleTypesProps['registeredRuleTypes'];
}) => {
const index: RuleTypeIndexWithDescriptions = new Map();
const registeredRuleTypesDictionary = registeredRuleTypes ? keyBy(registeredRuleTypes, 'id') : {};
for (const ruleType of data) {
Expand Down Expand Up @@ -88,7 +92,7 @@ export const useLoadRuleTypesQuery = ({
const filteredIndex = useMemo(
() =>
data
? getFilteredIndex(data, filteredRuleTypes, registeredRuleTypes)
? getFilteredIndex({ data, filteredRuleTypes, registeredRuleTypes })
: new Map<string, RuleTypeWithDescription>(),
[data, filteredRuleTypes, registeredRuleTypes]
);
Expand Down
11 changes: 11 additions & 0 deletions packages/kbn-alerts-ui-shared/src/rule_form/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export * from './rule_definition';
export * from './utils';
export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

export * from './rule_definition';
Loading

0 comments on commit dc3f76b

Please sign in to comment.