mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[RAM] Add data model for scheduled and recurring snoozes (#131019)
* [RAM] Add data model for scheduled and recurring snoozes * Update migration tests * Make snoozeIndefinitely required * Fix remaining muteAlls * Replace snoozeEndTime with snoozeSchedule * Fix typecheck * Fix typecheck * Revert muteAll => snoozeIndefinitely rename * Revert more snoozeIndefinitely refs * Revert README * Restore updated taskrunner test * Fix RuleStatusDropdown test * Add timeZone to SO * Update timezone usage * Implement RRule * Fix task runner test * Add rrule types * Push snoozeEndTime from server and fix unsnooze * Fix Jest Tests 5 * Fix rulestatusdropdown test * Fix jest tests 1 * Fix snooze_end_time refs in functional tests * Fix snooze API integration tests * Move isRuleSnoozed to server * Update x-pack/plugins/alerting/server/lib/is_rule_snoozed.ts Co-authored-by: Patrick Mueller <pmuellr@gmail.com> * Require timeZone in rulesnooze * Add automatic isSnoozedUntil savedobject flag * Check isSnoozedUntil against now * Fix jest * Fix typecheck * Fix jest * Fix snoozedUntil date parsing * Fix rewriterule * Add error handling for RRule * Fix re-snoozing * Add comments to rulesnoozetype * Restructure data model to use rRule for everything * Fix functional tests * Fix jest * Fix functional tests * Fix functional tests * Fix functional tests * Clarify isRuleSnoozed Co-authored-by: Patrick Mueller <pmuellr@gmail.com>
This commit is contained in:
parent
fa7df7983c
commit
64689c0f9e
48 changed files with 878 additions and 144 deletions
|
@ -220,6 +220,7 @@
|
|||
"@types/mapbox__vector-tile": "1.3.0",
|
||||
"@types/moment-duration-format": "^2.2.3",
|
||||
"@types/react-is": "^16.7.1",
|
||||
"@types/rrule": "^2.2.9",
|
||||
"JSONStream": "1.3.5",
|
||||
"abort-controller": "^3.0.0",
|
||||
"antlr4ts": "^0.5.0-alpha.3",
|
||||
|
@ -309,6 +310,7 @@
|
|||
"loader-utils": "^1.2.3",
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^4.1.5",
|
||||
"luxon": "^2.3.2",
|
||||
"lz-string": "^1.4.4",
|
||||
"mapbox-gl-draw-rectangle-mode": "1.0.4",
|
||||
"maplibre-gl": "2.1.9",
|
||||
|
@ -405,6 +407,7 @@
|
|||
"reselect": "^4.0.0",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"rison-node": "1.0.2",
|
||||
"rrule": "2.6.4",
|
||||
"rxjs": "^7.5.5",
|
||||
"safe-squel": "^5.12.5",
|
||||
"seedrandom": "^3.0.5",
|
||||
|
|
|
@ -21,6 +21,7 @@ export * from './disabled_action_groups';
|
|||
export * from './rule_notify_when_type';
|
||||
export * from './parse_duration';
|
||||
export * from './execution_log_types';
|
||||
export * from './rule_snooze_type';
|
||||
|
||||
export interface AlertingFrameworkHealth {
|
||||
isSufficientlySecure: boolean;
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
} from '@kbn/core/server';
|
||||
import { RuleNotifyWhenType } from './rule_notify_when_type';
|
||||
import { RuleSnooze } from './rule_snooze_type';
|
||||
|
||||
export type RuleTypeState = Record<string, unknown>;
|
||||
export type RuleTypeParams = Record<string, unknown>;
|
||||
|
@ -104,12 +105,13 @@ export interface Rule<Params extends RuleTypeParams = never> {
|
|||
apiKey: string | null;
|
||||
apiKeyOwner: string | null;
|
||||
throttle: string | null;
|
||||
notifyWhen: RuleNotifyWhenType | null;
|
||||
muteAll: boolean;
|
||||
notifyWhen: RuleNotifyWhenType | null;
|
||||
mutedInstanceIds: string[];
|
||||
executionStatus: RuleExecutionStatus;
|
||||
monitoring?: RuleMonitoring;
|
||||
snoozeEndTime?: Date | null; // Remove ? when this parameter is made available in the public API
|
||||
snoozeSchedule?: RuleSnooze; // Remove ? when this parameter is made available in the public API
|
||||
isSnoozedUntil?: Date | null;
|
||||
}
|
||||
|
||||
export type SanitizedRule<Params extends RuleTypeParams = never> = Omit<Rule<Params>, 'apiKey'>;
|
||||
|
|
35
x-pack/plugins/alerting/common/rule_snooze_type.ts
Normal file
35
x-pack/plugins/alerting/common/rule_snooze_type.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { WeekdayStr } from 'rrule';
|
||||
|
||||
export type RuleSnooze = Array<{
|
||||
duration: number;
|
||||
rRule: Partial<RRuleRecord> & Pick<RRuleRecord, 'dtstart' | 'tzid'>;
|
||||
// For scheduled/recurring snoozes, `id` uniquely identifies them so that they can be displayed, modified, and deleted individually
|
||||
id?: string;
|
||||
}>;
|
||||
|
||||
// An iCal RRULE to define a recurrence schedule, see https://github.com/jakubroztocil/rrule for the spec
|
||||
export interface RRuleRecord {
|
||||
dtstart: string;
|
||||
tzid: string;
|
||||
freq?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||
until?: string;
|
||||
count?: number;
|
||||
interval?: number;
|
||||
wkst?: WeekdayStr;
|
||||
byweekday?: Array<string | number>;
|
||||
bymonth?: number[];
|
||||
bysetpos?: number[];
|
||||
bymonthday: number;
|
||||
byyearday: number[];
|
||||
byweekno: number[];
|
||||
byhour: number[];
|
||||
byminute: number[];
|
||||
bysecond: number[];
|
||||
}
|
|
@ -27,4 +27,5 @@ export {
|
|||
} from './rule_execution_status';
|
||||
export { getRecoveredAlerts } from './get_recovered_alerts';
|
||||
export { createWrappedScopedClusterClientFactory } from './wrap_scoped_cluster_client';
|
||||
export { isRuleSnoozed, getRuleSnoozeEndTime } from './is_rule_snoozed';
|
||||
export { convertRuleIdsToKueryNode } from './convert_rule_ids_to_kuery_node';
|
||||
|
|
319
x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts
Normal file
319
x-pack/plugins/alerting/server/lib/is_rule_snoozed.test.ts
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import sinon from 'sinon';
|
||||
import { RRule } from 'rrule';
|
||||
import { isRuleSnoozed } from './is_rule_snoozed';
|
||||
import { RRuleRecord } from '../types';
|
||||
|
||||
const DATE_9999 = '9999-12-31T12:34:56.789Z';
|
||||
const DATE_1970 = '1970-01-01T00:00:00.000Z';
|
||||
const DATE_2019 = '2019-01-01T00:00:00.000Z';
|
||||
const DATE_2019_PLUS_6_HOURS = '2019-01-01T06:00:00.000Z';
|
||||
const DATE_2020 = '2020-01-01T00:00:00.000Z';
|
||||
const DATE_2020_MINUS_1_HOUR = '2019-12-31T23:00:00.000Z';
|
||||
const DATE_2020_MINUS_1_MONTH = '2019-12-01T00:00:00.000Z';
|
||||
|
||||
const NOW = DATE_2020;
|
||||
|
||||
let fakeTimer: sinon.SinonFakeTimers;
|
||||
|
||||
describe('isRuleSnoozed', () => {
|
||||
beforeAll(() => {
|
||||
fakeTimer = sinon.useFakeTimers(new Date(NOW));
|
||||
});
|
||||
|
||||
afterAll(() => fakeTimer.restore());
|
||||
|
||||
test('returns false when snooze has not yet started', () => {
|
||||
const snoozeSchedule = [
|
||||
{
|
||||
duration: 100000000,
|
||||
rRule: {
|
||||
dtstart: DATE_9999,
|
||||
tzid: 'UTC',
|
||||
count: 1,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule, muteAll: false })).toBe(false);
|
||||
});
|
||||
|
||||
test('returns true when snooze has started', () => {
|
||||
const snoozeSchedule = [
|
||||
{
|
||||
duration: 100000000,
|
||||
rRule: {
|
||||
dtstart: NOW,
|
||||
tzid: 'UTC',
|
||||
count: 1,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule, muteAll: false })).toBe(true);
|
||||
});
|
||||
|
||||
test('returns false when snooze has ended', () => {
|
||||
const snoozeSchedule = [
|
||||
{
|
||||
duration: 100000000,
|
||||
|
||||
rRule: {
|
||||
dtstart: DATE_2019,
|
||||
tzid: 'UTC',
|
||||
count: 1,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule, muteAll: false })).toBe(false);
|
||||
});
|
||||
|
||||
test('returns true when snooze is indefinite', () => {
|
||||
const snoozeSchedule = [
|
||||
{
|
||||
duration: 100000000,
|
||||
|
||||
rRule: {
|
||||
dtstart: DATE_9999,
|
||||
tzid: 'UTC',
|
||||
count: 1,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule, muteAll: true })).toBe(true);
|
||||
});
|
||||
|
||||
test('returns as expected for an indefinitely recurring snooze', () => {
|
||||
const snoozeScheduleA = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
dtstart: DATE_2019,
|
||||
tzid: 'UTC',
|
||||
freq: RRule.DAILY,
|
||||
interval: 1,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(true);
|
||||
const snoozeScheduleB = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
dtstart: DATE_2019_PLUS_6_HOURS,
|
||||
tzid: 'UTC',
|
||||
freq: RRule.DAILY,
|
||||
interval: 1,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleB, muteAll: false })).toBe(false);
|
||||
const snoozeScheduleC = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
dtstart: DATE_2020_MINUS_1_HOUR,
|
||||
tzid: 'UTC',
|
||||
freq: RRule.HOURLY,
|
||||
interval: 1,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleC, muteAll: false })).toBe(true);
|
||||
});
|
||||
|
||||
test('returns as expected for a recurring snooze with limited occurrences', () => {
|
||||
const snoozeScheduleA = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.HOURLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
count: 8761,
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(true);
|
||||
const snoozeScheduleB = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
|
||||
rRule: {
|
||||
freq: RRule.HOURLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
count: 25,
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleB, muteAll: false })).toBe(false);
|
||||
const snoozeScheduleC = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
|
||||
rRule: {
|
||||
freq: RRule.YEARLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
count: 60,
|
||||
dtstart: DATE_1970,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleC, muteAll: false })).toBe(true);
|
||||
});
|
||||
|
||||
test('returns as expected for a recurring snooze with an end date', () => {
|
||||
const snoozeScheduleA = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.HOURLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
until: DATE_9999,
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(true);
|
||||
const snoozeScheduleB = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.HOURLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
until: DATE_2020_MINUS_1_HOUR,
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleB, muteAll: false })).toBe(false);
|
||||
});
|
||||
|
||||
test('returns as expected for a recurring snooze on a day of the week', () => {
|
||||
const snoozeScheduleA = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.WEEKLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
byweekday: ['MO', 'WE', 'FR'], // Jan 1 2020 was a Wednesday
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(true);
|
||||
const snoozeScheduleB = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.WEEKLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
byweekday: ['TU', 'TH', 'SA', 'SU'],
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleB, muteAll: false })).toBe(false);
|
||||
const snoozeScheduleC = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.WEEKLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
byweekday: ['MO', 'WE', 'FR'],
|
||||
count: 12,
|
||||
dtstart: DATE_2020_MINUS_1_MONTH,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleC, muteAll: false })).toBe(false);
|
||||
const snoozeScheduleD = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.WEEKLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
byweekday: ['MO', 'WE', 'FR'],
|
||||
count: 15,
|
||||
dtstart: DATE_2020_MINUS_1_MONTH,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleD, muteAll: false })).toBe(true);
|
||||
});
|
||||
|
||||
test('returns as expected for a recurring snooze on an nth day of the week of a month', () => {
|
||||
const snoozeScheduleA = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.MONTHLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
byweekday: ['+1WE'], // Jan 1 2020 was the first Wednesday of the month
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(true);
|
||||
const snoozeScheduleB = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.MONTHLY,
|
||||
interval: 1,
|
||||
tzid: 'UTC',
|
||||
byweekday: ['+2WE'],
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleB, muteAll: false })).toBe(false);
|
||||
});
|
||||
|
||||
test('using a timezone, returns as expected for a recurring snooze on a day of the week', () => {
|
||||
const snoozeScheduleA = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.WEEKLY,
|
||||
interval: 1,
|
||||
byweekday: ['WE'],
|
||||
tzid: 'Asia/Taipei',
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleA, muteAll: false })).toBe(false);
|
||||
const snoozeScheduleB = [
|
||||
{
|
||||
duration: 60 * 1000,
|
||||
rRule: {
|
||||
freq: RRule.WEEKLY,
|
||||
interval: 1,
|
||||
byweekday: ['WE'],
|
||||
byhour: [0],
|
||||
byminute: [0],
|
||||
tzid: 'UTC',
|
||||
dtstart: DATE_2019,
|
||||
} as RRuleRecord,
|
||||
},
|
||||
];
|
||||
expect(isRuleSnoozed({ snoozeSchedule: snoozeScheduleB, muteAll: false })).toBe(true);
|
||||
});
|
||||
});
|
63
x-pack/plugins/alerting/server/lib/is_rule_snoozed.ts
Normal file
63
x-pack/plugins/alerting/server/lib/is_rule_snoozed.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RRule, ByWeekday, Weekday, rrulestr } from 'rrule';
|
||||
import { SanitizedRule, RuleTypeParams } from '../../common/rule';
|
||||
|
||||
type RuleSnoozeProps = Pick<SanitizedRule<RuleTypeParams>, 'muteAll' | 'snoozeSchedule'>;
|
||||
|
||||
export function getRuleSnoozeEndTime(rule: RuleSnoozeProps): Date | null {
|
||||
if (rule.snoozeSchedule == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
for (const snooze of rule.snoozeSchedule) {
|
||||
const { duration, rRule } = snooze;
|
||||
const startTimeMS = Date.parse(rRule.dtstart);
|
||||
const initialEndTime = startTimeMS + duration;
|
||||
// If now is during the first occurrence of the snooze
|
||||
|
||||
if (now >= startTimeMS && now < initialEndTime) return new Date(initialEndTime);
|
||||
|
||||
// Check to see if now is during a recurrence of the snooze
|
||||
if (rRule) {
|
||||
try {
|
||||
const rRuleOptions = {
|
||||
...rRule,
|
||||
dtstart: new Date(rRule.dtstart),
|
||||
until: rRule.until ? new Date(rRule.until) : null,
|
||||
wkst: rRule.wkst ? Weekday.fromStr(rRule.wkst) : null,
|
||||
byweekday: rRule.byweekday ? parseByWeekday(rRule.byweekday) : null,
|
||||
};
|
||||
|
||||
const recurrenceRule = new RRule(rRuleOptions);
|
||||
const lastOccurrence = recurrenceRule.before(new Date(now), true);
|
||||
if (!lastOccurrence) continue;
|
||||
const lastOccurrenceEndTime = lastOccurrence.getTime() + duration;
|
||||
if (now < lastOccurrenceEndTime) return new Date(lastOccurrenceEndTime);
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to process RRule ${rRule}: ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isRuleSnoozed(rule: RuleSnoozeProps) {
|
||||
if (rule.muteAll) {
|
||||
return true;
|
||||
}
|
||||
return Boolean(getRuleSnoozeEndTime(rule));
|
||||
}
|
||||
|
||||
function parseByWeekday(byweekday: Array<string | number>): ByWeekday[] {
|
||||
const rRuleString = `RRULE:BYDAY=${byweekday.join(',')}`;
|
||||
const parsedRRule = rrulestr(rRuleString);
|
||||
return parsedRRule.origOptions.byweekday as ByWeekday[];
|
||||
}
|
|
@ -67,12 +67,14 @@ const rewriteBodyRes: RewriteResponseCase<SanitizedRule<RuleTypeParams>> = ({
|
|||
notifyWhen,
|
||||
muteAll,
|
||||
mutedInstanceIds,
|
||||
snoozeSchedule,
|
||||
executionStatus: { lastExecutionDate, lastDuration, ...executionStatus },
|
||||
...rest
|
||||
}) => ({
|
||||
...rest,
|
||||
rule_type_id: alertTypeId,
|
||||
scheduled_task_id: scheduledTaskId,
|
||||
snooze_schedule: snoozeSchedule,
|
||||
created_by: createdBy,
|
||||
updated_by: updatedBy,
|
||||
created_at: createdAt,
|
||||
|
|
|
@ -35,7 +35,8 @@ const rewriteBodyRes: RewriteResponseCase<SanitizedRule<RuleTypeParams>> = ({
|
|||
executionStatus,
|
||||
actions,
|
||||
scheduledTaskId,
|
||||
snoozeEndTime,
|
||||
snoozeSchedule,
|
||||
isSnoozedUntil,
|
||||
...rest
|
||||
}) => ({
|
||||
...rest,
|
||||
|
@ -46,10 +47,10 @@ const rewriteBodyRes: RewriteResponseCase<SanitizedRule<RuleTypeParams>> = ({
|
|||
updated_at: updatedAt,
|
||||
api_key_owner: apiKeyOwner,
|
||||
notify_when: notifyWhen,
|
||||
mute_all: muteAll,
|
||||
muted_alert_ids: mutedInstanceIds,
|
||||
// Remove this object spread boolean check after snoozeEndTime is added to the public API
|
||||
...(snoozeEndTime !== undefined ? { snooze_end_time: snoozeEndTime } : {}),
|
||||
mute_all: muteAll,
|
||||
...(isSnoozedUntil !== undefined ? { is_snoozed_until: isSnoozedUntil } : {}),
|
||||
snooze_schedule: snoozeSchedule,
|
||||
scheduled_task_id: scheduledTaskId,
|
||||
execution_status: executionStatus && {
|
||||
...omit(executionStatus, 'lastExecutionDate', 'lastDuration'),
|
||||
|
|
|
@ -21,7 +21,8 @@ export const rewriteRule = ({
|
|||
executionStatus,
|
||||
actions,
|
||||
scheduledTaskId,
|
||||
snoozeEndTime,
|
||||
snoozeSchedule,
|
||||
isSnoozedUntil,
|
||||
...rest
|
||||
}: SanitizedRule<RuleTypeParams>) => ({
|
||||
...rest,
|
||||
|
@ -35,8 +36,8 @@ export const rewriteRule = ({
|
|||
mute_all: muteAll,
|
||||
muted_alert_ids: mutedInstanceIds,
|
||||
scheduled_task_id: scheduledTaskId,
|
||||
// Remove this object spread boolean check after snoozeEndTime is added to the public API
|
||||
...(snoozeEndTime !== undefined ? { snooze_end_time: snoozeEndTime } : {}),
|
||||
snooze_schedule: snoozeSchedule,
|
||||
...(isSnoozedUntil != null ? { is_snoozed_until: isSnoozedUntil } : {}),
|
||||
execution_status: executionStatus && {
|
||||
...omit(executionStatus, 'lastExecutionDate', 'lastDuration'),
|
||||
last_execution_date: executionStatus.lastExecutionDate,
|
||||
|
|
|
@ -70,12 +70,16 @@ const rewriteBodyRes: RewriteResponseCase<PartialRule<RuleTypeParams>> = ({
|
|||
muteAll,
|
||||
mutedInstanceIds,
|
||||
executionStatus,
|
||||
snoozeSchedule,
|
||||
isSnoozedUntil,
|
||||
...rest
|
||||
}) => ({
|
||||
...rest,
|
||||
api_key_owner: apiKeyOwner,
|
||||
created_by: createdBy,
|
||||
updated_by: updatedBy,
|
||||
snooze_schedule: snoozeSchedule,
|
||||
...(isSnoozedUntil ? { is_snoozed_until: isSnoozedUntil } : {}),
|
||||
...(alertTypeId ? { rule_type_id: alertTypeId } : {}),
|
||||
...(scheduledTaskId ? { scheduled_task_id: scheduledTaskId } : {}),
|
||||
...(createdAt ? { created_at: createdAt } : {}),
|
||||
|
|
|
@ -35,6 +35,7 @@ const createRulesClientMock = () => {
|
|||
bulkEdit: jest.fn(),
|
||||
snooze: jest.fn(),
|
||||
unsnooze: jest.fn(),
|
||||
updateSnoozedUntilTime: jest.fn(),
|
||||
};
|
||||
return mocked;
|
||||
};
|
||||
|
|
|
@ -54,6 +54,7 @@ import {
|
|||
RuleWithLegacyId,
|
||||
SanitizedRuleWithLegacyId,
|
||||
PartialRuleWithLegacyId,
|
||||
RuleSnooze,
|
||||
RawAlertInstance as RawAlert,
|
||||
} from '../types';
|
||||
import {
|
||||
|
@ -62,6 +63,7 @@ import {
|
|||
getRuleNotifyWhenType,
|
||||
validateMutatedRuleTypeParams,
|
||||
convertRuleIdsToKueryNode,
|
||||
getRuleSnoozeEndTime,
|
||||
} from '../lib';
|
||||
import { taskInstanceToAlertTaskInstance } from '../task_runner/alert_task_instance';
|
||||
import { RegistryRuleType, UntypedNormalizedRuleType } from '../rule_type_registry';
|
||||
|
@ -310,7 +312,8 @@ export interface CreateOptions<Params extends RuleTypeParams> {
|
|||
| 'mutedInstanceIds'
|
||||
| 'actions'
|
||||
| 'executionStatus'
|
||||
| 'snoozeEndTime'
|
||||
| 'snoozeSchedule'
|
||||
| 'isSnoozedUntil'
|
||||
> & { actions: NormalizedAlertAction[] };
|
||||
options?: {
|
||||
id?: string;
|
||||
|
@ -391,7 +394,7 @@ export class RulesClient {
|
|||
private readonly fieldsToExcludeFromPublicApi: Array<keyof SanitizedRule> = [
|
||||
'monitoring',
|
||||
'mapped_params',
|
||||
'snoozeEndTime',
|
||||
'snoozeSchedule',
|
||||
];
|
||||
|
||||
constructor({
|
||||
|
@ -504,7 +507,8 @@ export class RulesClient {
|
|||
updatedBy: username,
|
||||
createdAt: new Date(createTime).toISOString(),
|
||||
updatedAt: new Date(createTime).toISOString(),
|
||||
snoozeEndTime: null,
|
||||
isSnoozedUntil: null,
|
||||
snoozeSchedule: [],
|
||||
params: updatedParams as RawRule['params'],
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
|
@ -1018,7 +1022,7 @@ export class RulesClient {
|
|||
},
|
||||
snoozed: {
|
||||
date_range: {
|
||||
field: 'alert.attributes.snoozeEndTime',
|
||||
field: 'alert.attributes.snoozeSchedule.rRule.dtstart',
|
||||
format: 'strict_date_time',
|
||||
ranges: [{ from: 'now' }],
|
||||
},
|
||||
|
@ -2120,8 +2124,21 @@ export class RulesClient {
|
|||
// If snoozeEndTime is -1, instead mute all
|
||||
const newAttrs =
|
||||
snoozeEndTime === -1
|
||||
? { muteAll: true, snoozeEndTime: null }
|
||||
: { snoozeEndTime: new Date(snoozeEndTime).toISOString(), muteAll: false };
|
||||
? {
|
||||
muteAll: true,
|
||||
snoozeSchedule: clearUnscheduledSnooze(attributes),
|
||||
}
|
||||
: {
|
||||
snoozeSchedule: clearUnscheduledSnooze(attributes).concat({
|
||||
duration: Date.parse(snoozeEndTime) - Date.now(),
|
||||
rRule: {
|
||||
dtstart: new Date().toISOString(),
|
||||
tzid: 'UTC',
|
||||
count: 1,
|
||||
},
|
||||
}),
|
||||
muteAll: false,
|
||||
};
|
||||
|
||||
const updateAttributes = this.updateMeta({
|
||||
...newAttrs,
|
||||
|
@ -2135,7 +2152,7 @@ export class RulesClient {
|
|||
id,
|
||||
updateAttributes,
|
||||
updateOptions
|
||||
);
|
||||
).then(() => this.updateSnoozedUntilTime({ id }));
|
||||
}
|
||||
|
||||
public async unsnooze({ id }: { id: string }): Promise<void> {
|
||||
|
@ -2185,7 +2202,7 @@ export class RulesClient {
|
|||
this.ruleTypeRegistry.ensureRuleTypeEnabled(attributes.alertTypeId);
|
||||
|
||||
const updateAttributes = this.updateMeta({
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: clearUnscheduledSnooze(attributes),
|
||||
muteAll: false,
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
|
@ -2200,6 +2217,30 @@ export class RulesClient {
|
|||
);
|
||||
}
|
||||
|
||||
public async updateSnoozedUntilTime({ id }: { id: string }): Promise<void> {
|
||||
const { attributes, version } = await this.unsecuredSavedObjectsClient.get<RawRule>(
|
||||
'alert',
|
||||
id
|
||||
);
|
||||
|
||||
const isSnoozedUntil = getRuleSnoozeEndTime(attributes);
|
||||
if (!isSnoozedUntil) return;
|
||||
|
||||
const updateAttributes = this.updateMeta({
|
||||
isSnoozedUntil: isSnoozedUntil.toISOString(),
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
const updateOptions = { version };
|
||||
|
||||
await partiallyUpdateAlert(
|
||||
this.unsecuredSavedObjectsClient,
|
||||
id,
|
||||
updateAttributes,
|
||||
updateOptions
|
||||
);
|
||||
}
|
||||
|
||||
public async muteAll({ id }: { id: string }): Promise<void> {
|
||||
return await retryIfConflicts(
|
||||
this.logger,
|
||||
|
@ -2249,7 +2290,7 @@ export class RulesClient {
|
|||
const updateAttributes = this.updateMeta({
|
||||
muteAll: true,
|
||||
mutedInstanceIds: [],
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: clearUnscheduledSnooze(attributes),
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
@ -2312,7 +2353,7 @@ export class RulesClient {
|
|||
const updateAttributes = this.updateMeta({
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: clearUnscheduledSnooze(attributes),
|
||||
updatedBy: await this.getUserName(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
@ -2560,15 +2601,23 @@ export class RulesClient {
|
|||
executionStatus,
|
||||
schedule,
|
||||
actions,
|
||||
snoozeEndTime,
|
||||
snoozeSchedule,
|
||||
isSnoozedUntil,
|
||||
...partialRawRule
|
||||
}: Partial<RawRule>,
|
||||
references: SavedObjectReference[] | undefined,
|
||||
includeLegacyId: boolean = false,
|
||||
excludeFromPublicApi: boolean = false
|
||||
): PartialRule<Params> | PartialRuleWithLegacyId<Params> {
|
||||
const snoozeEndTimeDate = snoozeEndTime != null ? new Date(snoozeEndTime) : snoozeEndTime;
|
||||
const includeSnoozeEndTime = snoozeEndTimeDate !== undefined && !excludeFromPublicApi;
|
||||
const snoozeScheduleDates = snoozeSchedule?.map((s) => ({
|
||||
...s,
|
||||
rRule: {
|
||||
...s.rRule,
|
||||
dtstart: new Date(s.rRule.dtstart),
|
||||
...(s.rRule.until ? { until: new Date(s.rRule.until) } : {}),
|
||||
},
|
||||
}));
|
||||
const includeSnoozeSchedule = snoozeSchedule !== undefined;
|
||||
const rule = {
|
||||
id,
|
||||
notifyWhen,
|
||||
|
@ -2578,9 +2627,10 @@ export class RulesClient {
|
|||
schedule: schedule as IntervalSchedule,
|
||||
actions: actions ? this.injectReferencesIntoActions(id, actions, references || []) : [],
|
||||
params: this.injectReferencesIntoParams(id, ruleType, params, references || []) as Params,
|
||||
...(includeSnoozeEndTime ? { snoozeEndTime: snoozeEndTimeDate } : {}),
|
||||
...(includeSnoozeSchedule ? { snoozeSchedule: snoozeScheduleDates } : {}),
|
||||
...(updatedAt ? { updatedAt: new Date(updatedAt) } : {}),
|
||||
...(createdAt ? { createdAt: new Date(createdAt) } : {}),
|
||||
...(isSnoozedUntil ? { isSnoozedUntil: new Date(isSnoozedUntil) } : {}),
|
||||
...(scheduledTaskId ? { scheduledTaskId } : {}),
|
||||
...(executionStatus
|
||||
? { executionStatus: ruleExecutionStatusFromRaw(this.logger, id, executionStatus) }
|
||||
|
@ -2795,3 +2845,9 @@ function parseDate(dateString: string | undefined, propertyName: string, default
|
|||
|
||||
return parsedDate;
|
||||
}
|
||||
|
||||
function clearUnscheduledSnooze(attributes: { snoozeSchedule?: RuleSnooze }) {
|
||||
return attributes.snoozeSchedule
|
||||
? attributes.snoozeSchedule.filter((s) => typeof s.id !== 'undefined')
|
||||
: [];
|
||||
}
|
||||
|
|
|
@ -203,7 +203,7 @@ describe('aggregate()', () => {
|
|||
},
|
||||
snoozed: {
|
||||
date_range: {
|
||||
field: 'alert.attributes.snoozeEndTime',
|
||||
field: 'alert.attributes.snoozeSchedule.rRule.dtstart',
|
||||
format: 'strict_date_time',
|
||||
ranges: [{ from: 'now' }],
|
||||
},
|
||||
|
@ -240,7 +240,7 @@ describe('aggregate()', () => {
|
|||
},
|
||||
snoozed: {
|
||||
date_range: {
|
||||
field: 'alert.attributes.snoozeEndTime',
|
||||
field: 'alert.attributes.snoozeSchedule.rRule.dtstart',
|
||||
format: 'strict_date_time',
|
||||
ranges: [{ from: 'now' }],
|
||||
},
|
||||
|
|
|
@ -300,7 +300,7 @@ describe('create()', () => {
|
|||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
actions: [
|
||||
{
|
||||
|
@ -376,6 +376,7 @@ describe('create()', () => {
|
|||
"interval": "1m",
|
||||
},
|
||||
"scheduledTaskId": "task-123",
|
||||
"snoozeSchedule": Array [],
|
||||
"tags": Array [
|
||||
"foo",
|
||||
],
|
||||
|
@ -412,6 +413,7 @@ describe('create()', () => {
|
|||
"status": "pending",
|
||||
"warning": null,
|
||||
},
|
||||
"isSnoozedUntil": null,
|
||||
"legacyId": null,
|
||||
"meta": Object {
|
||||
"versionApiKeyLastmodified": "v8.0.0",
|
||||
|
@ -434,7 +436,7 @@ describe('create()', () => {
|
|||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
"snoozeEndTime": null,
|
||||
"snoozeSchedule": Array [],
|
||||
"tags": Array [
|
||||
"foo",
|
||||
],
|
||||
|
@ -506,7 +508,7 @@ describe('create()', () => {
|
|||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
actions: [
|
||||
{
|
||||
|
@ -566,7 +568,7 @@ describe('create()', () => {
|
|||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
actions: [
|
||||
{
|
||||
|
@ -618,6 +620,7 @@ describe('create()', () => {
|
|||
"status": "pending",
|
||||
"warning": null,
|
||||
},
|
||||
"isSnoozedUntil": null,
|
||||
"legacyId": "123",
|
||||
"meta": Object {
|
||||
"versionApiKeyLastmodified": "v7.10.0",
|
||||
|
@ -640,7 +643,7 @@ describe('create()', () => {
|
|||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
"snoozeEndTime": null,
|
||||
"snoozeSchedule": Array [],
|
||||
"tags": Array [
|
||||
"foo",
|
||||
],
|
||||
|
@ -1044,6 +1047,7 @@ describe('create()', () => {
|
|||
createdAt: '2019-02-12T21:01:22.479Z',
|
||||
createdBy: 'elastic',
|
||||
enabled: true,
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
executionStatus: {
|
||||
error: null,
|
||||
|
@ -1054,7 +1058,7 @@ describe('create()', () => {
|
|||
monitoring: getDefaultRuleMonitoring(),
|
||||
meta: { versionApiKeyLastmodified: kibanaVersion },
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
name: 'abc',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
|
@ -1243,6 +1247,7 @@ describe('create()', () => {
|
|||
createdAt: '2019-02-12T21:01:22.479Z',
|
||||
createdBy: 'elastic',
|
||||
enabled: true,
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
executionStatus: {
|
||||
error: null,
|
||||
|
@ -1253,7 +1258,7 @@ describe('create()', () => {
|
|||
monitoring: getDefaultRuleMonitoring(),
|
||||
meta: { versionApiKeyLastmodified: kibanaVersion },
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
name: 'abc',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
|
@ -1407,6 +1412,7 @@ describe('create()', () => {
|
|||
alertTypeId: '123',
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
consumer: 'bar',
|
||||
createdAt: '2019-02-12T21:01:22.479Z',
|
||||
|
@ -1421,7 +1427,7 @@ describe('create()', () => {
|
|||
monitoring: getDefaultRuleMonitoring(),
|
||||
meta: { versionApiKeyLastmodified: kibanaVersion },
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
name: 'abc',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
|
@ -1530,7 +1536,7 @@ describe('create()', () => {
|
|||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
notifyWhen: 'onActionGroupChange',
|
||||
actions: [
|
||||
|
@ -1571,6 +1577,7 @@ describe('create()', () => {
|
|||
alertTypeId: '123',
|
||||
consumer: 'bar',
|
||||
name: 'abc',
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
params: { bar: true },
|
||||
apiKey: null,
|
||||
|
@ -1587,7 +1594,7 @@ describe('create()', () => {
|
|||
throttle: '10m',
|
||||
notifyWhen: 'onActionGroupChange',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
tags: ['foo'],
|
||||
executionStatus: {
|
||||
|
@ -1638,6 +1645,7 @@ describe('create()', () => {
|
|||
"interval": "1m",
|
||||
},
|
||||
"scheduledTaskId": "task-123",
|
||||
"snoozeSchedule": Array [],
|
||||
"tags": Array [
|
||||
"foo",
|
||||
],
|
||||
|
@ -1662,7 +1670,7 @@ describe('create()', () => {
|
|||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
notifyWhen: 'onThrottleInterval',
|
||||
actions: [
|
||||
|
@ -1700,6 +1708,7 @@ describe('create()', () => {
|
|||
params: { foo: true },
|
||||
},
|
||||
],
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
alertTypeId: '123',
|
||||
consumer: 'bar',
|
||||
|
@ -1719,7 +1728,7 @@ describe('create()', () => {
|
|||
throttle: '10m',
|
||||
notifyWhen: 'onThrottleInterval',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
tags: ['foo'],
|
||||
executionStatus: {
|
||||
|
@ -1770,6 +1779,7 @@ describe('create()', () => {
|
|||
"interval": "1m",
|
||||
},
|
||||
"scheduledTaskId": "task-123",
|
||||
"snoozeSchedule": Array [],
|
||||
"tags": Array [
|
||||
"foo",
|
||||
],
|
||||
|
@ -1794,7 +1804,7 @@ describe('create()', () => {
|
|||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
notifyWhen: 'onActiveAlert',
|
||||
actions: [
|
||||
|
@ -1832,6 +1842,7 @@ describe('create()', () => {
|
|||
params: { foo: true },
|
||||
},
|
||||
],
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
alertTypeId: '123',
|
||||
consumer: 'bar',
|
||||
|
@ -1851,7 +1862,7 @@ describe('create()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: 'onActiveAlert',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
tags: ['foo'],
|
||||
executionStatus: {
|
||||
|
@ -1902,6 +1913,7 @@ describe('create()', () => {
|
|||
"interval": "1m",
|
||||
},
|
||||
"scheduledTaskId": "task-123",
|
||||
"snoozeSchedule": Array [],
|
||||
"tags": Array [
|
||||
"foo",
|
||||
],
|
||||
|
@ -1935,7 +1947,7 @@ describe('create()', () => {
|
|||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
actions: [
|
||||
{
|
||||
|
@ -1993,13 +2005,14 @@ describe('create()', () => {
|
|||
],
|
||||
apiKeyOwner: null,
|
||||
apiKey: null,
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
createdBy: 'elastic',
|
||||
updatedBy: 'elastic',
|
||||
createdAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
executionStatus: {
|
||||
status: 'pending',
|
||||
|
@ -2066,6 +2079,7 @@ describe('create()', () => {
|
|||
"interval": "10s",
|
||||
},
|
||||
"scheduledTaskId": "task-123",
|
||||
"snoozeSchedule": Array [],
|
||||
"tags": Array [
|
||||
"foo",
|
||||
],
|
||||
|
@ -2345,6 +2359,7 @@ describe('create()', () => {
|
|||
alertTypeId: '123',
|
||||
consumer: 'bar',
|
||||
name: 'abc',
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
params: { bar: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
|
@ -2361,7 +2376,7 @@ describe('create()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: 'onActiveAlert',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
tags: ['foo'],
|
||||
executionStatus: {
|
||||
|
@ -2444,6 +2459,7 @@ describe('create()', () => {
|
|||
params: { foo: true },
|
||||
},
|
||||
],
|
||||
isSnoozedUntil: null,
|
||||
legacyId: null,
|
||||
alertTypeId: '123',
|
||||
consumer: 'bar',
|
||||
|
@ -2463,7 +2479,7 @@ describe('create()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: 'onActiveAlert',
|
||||
muteAll: false,
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
mutedInstanceIds: [],
|
||||
tags: ['foo'],
|
||||
executionStatus: {
|
||||
|
|
|
@ -82,7 +82,7 @@ describe('muteAll()', () => {
|
|||
{
|
||||
muteAll: true,
|
||||
mutedInstanceIds: [],
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
},
|
||||
|
|
|
@ -82,7 +82,7 @@ describe('unmuteAll()', () => {
|
|||
{
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
},
|
||||
|
|
|
@ -30,7 +30,8 @@ export const AlertAttributesExcludedFromAAD = [
|
|||
'updatedAt',
|
||||
'executionStatus',
|
||||
'monitoring',
|
||||
'snoozeEndTime',
|
||||
'snoozeSchedule',
|
||||
'isSnoozedUntil',
|
||||
];
|
||||
|
||||
// useful for Pick<RawAlert, AlertAttributesExcludedFromAADType> which is a
|
||||
|
@ -45,7 +46,8 @@ export type AlertAttributesExcludedFromAADType =
|
|||
| 'updatedAt'
|
||||
| 'executionStatus'
|
||||
| 'monitoring'
|
||||
| 'snoozeEndTime';
|
||||
| 'snoozeSchedule'
|
||||
| 'isSnoozedUntil';
|
||||
|
||||
export function setupSavedObjects(
|
||||
savedObjects: SavedObjectsServiceSetup,
|
||||
|
|
|
@ -185,7 +185,73 @@ export const alertMappings: SavedObjectsTypeMappingDefinition = {
|
|||
},
|
||||
},
|
||||
},
|
||||
snoozeEndTime: {
|
||||
snoozeSchedule: {
|
||||
type: 'nested',
|
||||
properties: {
|
||||
id: {
|
||||
type: 'keyword',
|
||||
},
|
||||
duration: {
|
||||
type: 'long',
|
||||
},
|
||||
rRule: {
|
||||
type: 'nested',
|
||||
properties: {
|
||||
freq: {
|
||||
type: 'keyword',
|
||||
},
|
||||
dtstart: {
|
||||
type: 'date',
|
||||
format: 'strict_date_time',
|
||||
},
|
||||
tzid: {
|
||||
type: 'keyword',
|
||||
},
|
||||
until: {
|
||||
type: 'date',
|
||||
format: 'strict_date_time',
|
||||
},
|
||||
count: {
|
||||
type: 'long',
|
||||
},
|
||||
interval: {
|
||||
type: 'long',
|
||||
},
|
||||
wkst: {
|
||||
type: 'keyword',
|
||||
},
|
||||
byweekday: {
|
||||
type: 'keyword',
|
||||
},
|
||||
bymonth: {
|
||||
type: 'short',
|
||||
},
|
||||
bysetpos: {
|
||||
type: 'long',
|
||||
},
|
||||
bymonthday: {
|
||||
type: 'short',
|
||||
},
|
||||
byyearday: {
|
||||
type: 'short',
|
||||
},
|
||||
byweekno: {
|
||||
type: 'short',
|
||||
},
|
||||
byhour: {
|
||||
type: 'long',
|
||||
},
|
||||
byminute: {
|
||||
type: 'long',
|
||||
},
|
||||
bysecond: {
|
||||
type: 'long',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isSnoozedUntil: {
|
||||
type: 'date',
|
||||
format: 'strict_date_time',
|
||||
},
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import sinon from 'sinon';
|
||||
import uuid from 'uuid';
|
||||
import { getMigrations, isAnyActionSupportIncidents } from './migrations';
|
||||
import { RawRule } from '../types';
|
||||
|
@ -2318,6 +2319,27 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
describe('8.3.0', () => {
|
||||
test('migrates snoozed rules to the new data model', () => {
|
||||
const fakeTimer = sinon.useFakeTimers();
|
||||
const migration830 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
|
||||
'8.3.0'
|
||||
];
|
||||
const mutedAlert = getMockData(
|
||||
{
|
||||
snoozeEndTime: '1970-01-02T00:00:00.000Z',
|
||||
},
|
||||
true
|
||||
);
|
||||
const migratedMutedAlert830 = migration830(mutedAlert, migrationContext);
|
||||
|
||||
expect(migratedMutedAlert830.attributes.snoozeSchedule.length).toEqual(1);
|
||||
expect(migratedMutedAlert830.attributes.snoozeSchedule[0].rRule.dtstart).toEqual(
|
||||
'1970-01-01T00:00:00.000Z'
|
||||
);
|
||||
expect(migratedMutedAlert830.attributes.snoozeSchedule[0].duration).toEqual(86400000);
|
||||
fakeTimer.restore();
|
||||
});
|
||||
|
||||
test('migrates es_query alert params', () => {
|
||||
const migration830 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[
|
||||
'8.3.0'
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
import { isRuleType, ruleTypeMappings } from '@kbn/securitysolution-rules';
|
||||
import { isString } from 'lodash/fp';
|
||||
import { omit } from 'lodash';
|
||||
import moment from 'moment-timezone';
|
||||
import { gte } from 'semver';
|
||||
import {
|
||||
LogMeta,
|
||||
|
@ -164,7 +166,7 @@ export function getMigrations(
|
|||
const migrationRules830 = createEsoMigration(
|
||||
encryptedSavedObjects,
|
||||
(doc: SavedObjectUnsanitizedDoc<RawRule>): doc is SavedObjectUnsanitizedDoc<RawRule> => true,
|
||||
pipeMigrations(addSearchType, removeInternalTags)
|
||||
pipeMigrations(addSearchType, removeInternalTags, convertSnoozes)
|
||||
);
|
||||
|
||||
return mergeSavedObjectMigrationMaps(
|
||||
|
@ -888,6 +890,33 @@ function addMappedParams(
|
|||
return doc;
|
||||
}
|
||||
|
||||
function convertSnoozes(
|
||||
doc: SavedObjectUnsanitizedDoc<RawRule>
|
||||
): SavedObjectUnsanitizedDoc<RawRule> {
|
||||
const {
|
||||
attributes: { snoozeEndTime },
|
||||
} = doc;
|
||||
|
||||
return {
|
||||
...doc,
|
||||
attributes: {
|
||||
...(omit(doc.attributes, ['snoozeEndTime']) as RawRule),
|
||||
snoozeSchedule: snoozeEndTime
|
||||
? [
|
||||
{
|
||||
duration: Date.parse(snoozeEndTime as string) - Date.now(),
|
||||
rRule: {
|
||||
dtstart: new Date().toISOString(),
|
||||
tzid: moment.tz.guess(),
|
||||
count: 1,
|
||||
},
|
||||
},
|
||||
]
|
||||
: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getCorrespondingAction(
|
||||
actions: SavedObjectAttribute,
|
||||
connectorRef: string
|
||||
|
|
|
@ -470,17 +470,42 @@ describe('Task Runner', () => {
|
|||
const snoozeTestParams: SnoozeTestParams[] = [
|
||||
[false, null, false],
|
||||
[false, undefined, false],
|
||||
[false, DATE_1970, false],
|
||||
[false, DATE_9999, true],
|
||||
// Stringify the snooze schedules for better failure reporting
|
||||
[
|
||||
false,
|
||||
JSON.stringify([
|
||||
{ rRule: { dtstart: DATE_9999, tzid: 'UTC', count: 1 }, duration: 100000000 },
|
||||
]),
|
||||
false,
|
||||
],
|
||||
[
|
||||
false,
|
||||
JSON.stringify([
|
||||
{ rRule: { dtstart: DATE_1970, tzid: 'UTC', count: 1 }, duration: 100000000 },
|
||||
]),
|
||||
true,
|
||||
],
|
||||
[true, null, true],
|
||||
[true, undefined, true],
|
||||
[true, DATE_1970, true],
|
||||
[true, DATE_9999, true],
|
||||
[
|
||||
true,
|
||||
JSON.stringify([
|
||||
{ rRule: { dtstart: DATE_9999, tzid: 'UTC', count: 1 }, duration: 100000000 },
|
||||
]),
|
||||
true,
|
||||
],
|
||||
[
|
||||
true,
|
||||
JSON.stringify([
|
||||
{ rRule: { dtstart: DATE_1970, tzid: 'UTC', count: 1 }, duration: 100000000 },
|
||||
]),
|
||||
true,
|
||||
],
|
||||
];
|
||||
|
||||
test.each(snoozeTestParams)(
|
||||
'snoozing works as expected with muteAll: %s; snoozeEndTime: %s',
|
||||
async (muteAll, snoozeEndTime, shouldBeSnoozed) => {
|
||||
'snoozing works as expected with muteAll: %s; snoozeSchedule: %s',
|
||||
async (muteAll, snoozeSchedule, shouldBeSnoozed) => {
|
||||
taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true);
|
||||
taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true);
|
||||
ruleType.executor.mockImplementation(
|
||||
|
@ -507,7 +532,7 @@ describe('Task Runner', () => {
|
|||
rulesClient.get.mockResolvedValue({
|
||||
...mockedRuleTypeSavedObject,
|
||||
muteAll,
|
||||
snoozeEndTime: snoozeEndTime != null ? new Date(snoozeEndTime) : snoozeEndTime,
|
||||
snoozeSchedule: snoozeSchedule != null ? JSON.parse(snoozeSchedule) : [],
|
||||
});
|
||||
encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT);
|
||||
await taskRunner.run();
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
getRecoveredAlerts,
|
||||
ruleExecutionStatusToRaw,
|
||||
validateRuleTypeParams,
|
||||
isRuleSnoozed,
|
||||
} from '../lib';
|
||||
import {
|
||||
Rule,
|
||||
|
@ -247,18 +248,6 @@ export class TaskRunner<
|
|||
}
|
||||
}
|
||||
|
||||
private isRuleSnoozed(rule: SanitizedRule<Params>): boolean {
|
||||
if (rule.muteAll) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rule.snoozeEndTime == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Date.now() < rule.snoozeEndTime.getTime();
|
||||
}
|
||||
|
||||
private shouldLogAndScheduleActionsForAlerts() {
|
||||
// if execution hasn't been cancelled, return true
|
||||
if (!this.cancelled) {
|
||||
|
@ -477,7 +466,10 @@ export class TaskRunner<
|
|||
});
|
||||
}
|
||||
|
||||
const ruleIsSnoozed = this.isRuleSnoozed(rule);
|
||||
const ruleIsSnoozed = isRuleSnoozed(rule);
|
||||
if (ruleIsSnoozed) {
|
||||
this.markRuleAsSnoozed(rule.id);
|
||||
}
|
||||
if (!ruleIsSnoozed && this.shouldLogAndScheduleActionsForAlerts()) {
|
||||
const mutedAlertIdsSet = new Set(mutedInstanceIds);
|
||||
|
||||
|
@ -580,6 +572,23 @@ export class TaskRunner<
|
|||
return this.executeRule(fakeRequest, rule, validatedParams, executionHandler, spaceId);
|
||||
}
|
||||
|
||||
private async markRuleAsSnoozed(id: string) {
|
||||
let apiKey: string | null;
|
||||
|
||||
const {
|
||||
params: { alertId: ruleId, spaceId },
|
||||
} = this.taskInstance;
|
||||
try {
|
||||
const decryptedAttributes = await this.getDecryptedAttributes(ruleId, spaceId);
|
||||
apiKey = decryptedAttributes.apiKey;
|
||||
} catch (err) {
|
||||
throw new ErrorWithReason(RuleExecutionStatusErrorReasons.Decrypt, err);
|
||||
}
|
||||
const fakeRequest = this.getFakeKibanaRequest(spaceId, apiKey);
|
||||
const rulesClient = this.context.getRulesClientWithRequest(fakeRequest);
|
||||
await rulesClient.updateSnoozedUntilTime({ id });
|
||||
}
|
||||
|
||||
private async loadRuleAttributesAndRun(): Promise<Resultable<RuleRunResult, Error>> {
|
||||
const {
|
||||
params: { alertId: ruleId, spaceId },
|
||||
|
|
|
@ -40,6 +40,7 @@ import {
|
|||
SanitizedRuleConfig,
|
||||
RuleMonitoring,
|
||||
MappedParams,
|
||||
RuleSnooze,
|
||||
} from '../common';
|
||||
export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>;
|
||||
export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined;
|
||||
|
@ -249,7 +250,8 @@ export interface RawRule extends SavedObjectAttributes {
|
|||
meta?: RuleMeta;
|
||||
executionStatus: RawRuleExecutionStatus;
|
||||
monitoring?: RuleMonitoring;
|
||||
snoozeEndTime?: string | null; // Remove ? when this parameter is made available in the public API
|
||||
snoozeSchedule?: RuleSnooze; // Remove ? when this parameter is made available in the public API
|
||||
isSnoozedUntil?: string | null;
|
||||
}
|
||||
|
||||
export interface AlertingPlugin {
|
||||
|
|
|
@ -10,33 +10,33 @@ import { getRuleStatusDropdownLazy } from '../../../common/get_rule_status_dropd
|
|||
|
||||
export const RuleStatusDropdownSandbox: React.FC<{}> = () => {
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
const [snoozeEndTime, setSnoozeEndTime] = useState<Date | null>(null);
|
||||
const [isSnoozedUntil, setIsSnoozedUntil] = useState<Date | null>(null);
|
||||
const [muteAll, setMuteAll] = useState(false);
|
||||
|
||||
return getRuleStatusDropdownLazy({
|
||||
rule: {
|
||||
enabled,
|
||||
snoozeEndTime,
|
||||
isSnoozedUntil,
|
||||
muteAll,
|
||||
},
|
||||
enableRule: async () => {
|
||||
setEnabled(true);
|
||||
setMuteAll(false);
|
||||
setSnoozeEndTime(null);
|
||||
setIsSnoozedUntil(null);
|
||||
},
|
||||
disableRule: async () => setEnabled(false),
|
||||
snoozeRule: async (time) => {
|
||||
if (time === -1) {
|
||||
setSnoozeEndTime(null);
|
||||
setIsSnoozedUntil(null);
|
||||
setMuteAll(true);
|
||||
} else {
|
||||
setSnoozeEndTime(new Date(time));
|
||||
setIsSnoozedUntil(new Date(time));
|
||||
setMuteAll(false);
|
||||
}
|
||||
},
|
||||
unsnoozeRule: async () => {
|
||||
setMuteAll(false);
|
||||
setSnoozeEndTime(null);
|
||||
setIsSnoozedUntil(null);
|
||||
},
|
||||
onRuleChanged: () => {},
|
||||
isEditable: true,
|
||||
|
|
|
@ -243,7 +243,7 @@ describe('loadRuleAggregations', () => {
|
|||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.enabled:(true) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
|
||||
"filter": "alert.attributes.enabled:(true) and not (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)",
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
},
|
||||
|
@ -262,7 +262,7 @@ describe('loadRuleAggregations', () => {
|
|||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
|
||||
"filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)",
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
},
|
||||
|
@ -281,7 +281,7 @@ describe('loadRuleAggregations', () => {
|
|||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
|
||||
"filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)",
|
||||
"search": undefined,
|
||||
"search_fields": undefined,
|
||||
},
|
||||
|
|
|
@ -43,7 +43,8 @@ export const transformRule: RewriteRequestCase<Rule> = ({
|
|||
scheduled_task_id: scheduledTaskId,
|
||||
execution_status: executionStatus,
|
||||
actions: actions,
|
||||
snooze_end_time: snoozeEndTime,
|
||||
snooze_schedule: snoozeSchedule,
|
||||
is_snoozed_until: isSnoozedUntil,
|
||||
...rest
|
||||
}: any) => ({
|
||||
ruleTypeId,
|
||||
|
@ -55,12 +56,13 @@ export const transformRule: RewriteRequestCase<Rule> = ({
|
|||
notifyWhen,
|
||||
muteAll,
|
||||
mutedInstanceIds,
|
||||
snoozeEndTime,
|
||||
snoozeSchedule,
|
||||
executionStatus: executionStatus ? transformExecutionStatus(executionStatus) : undefined,
|
||||
actions: actions
|
||||
? actions.map((action: AsApiContract<RuleAction>) => transformAction(action))
|
||||
: [],
|
||||
scheduledTaskId,
|
||||
isSnoozedUntil,
|
||||
...rest,
|
||||
});
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ describe('mapFiltersToKql', () => {
|
|||
ruleStatusesFilter: ['enabled'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.enabled:(true) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
|
||||
'alert.attributes.enabled:(true) and not (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)',
|
||||
]);
|
||||
|
||||
expect(
|
||||
|
@ -54,21 +54,21 @@ describe('mapFiltersToKql', () => {
|
|||
ruleStatusesFilter: ['disabled'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.enabled:(false) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
|
||||
'alert.attributes.enabled:(false) and not (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)',
|
||||
]);
|
||||
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
ruleStatusesFilter: ['snoozed'],
|
||||
})
|
||||
).toEqual(['(alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)']);
|
||||
).toEqual(['(alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)']);
|
||||
|
||||
expect(
|
||||
mapFiltersToKql({
|
||||
ruleStatusesFilter: ['enabled', 'snoozed'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
|
||||
'alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)',
|
||||
]);
|
||||
|
||||
expect(
|
||||
|
@ -76,7 +76,7 @@ describe('mapFiltersToKql', () => {
|
|||
ruleStatusesFilter: ['disabled', 'snoozed'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.enabled:(false) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
|
||||
'alert.attributes.enabled:(false) or (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)',
|
||||
]);
|
||||
|
||||
expect(
|
||||
|
@ -84,7 +84,7 @@ describe('mapFiltersToKql', () => {
|
|||
ruleStatusesFilter: ['enabled', 'disabled', 'snoozed'],
|
||||
})
|
||||
).toEqual([
|
||||
'alert.attributes.enabled:(true or false) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)',
|
||||
'alert.attributes.enabled:(true or false) or (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)',
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ export const mapFiltersToKql = ({
|
|||
|
||||
if (ruleStatusesFilter && ruleStatusesFilter.length) {
|
||||
const enablementFilter = getEnablementFilter(ruleStatusesFilter);
|
||||
const snoozedFilter = `(alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)`;
|
||||
const snoozedFilter = `(alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)`;
|
||||
const hasEnablement =
|
||||
ruleStatusesFilter.includes('enabled') || ruleStatusesFilter.includes('disabled');
|
||||
const hasSnoozed = ruleStatusesFilter.includes('snoozed');
|
||||
|
|
|
@ -266,7 +266,7 @@ describe('loadRules', () => {
|
|||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
|
||||
"filter": "alert.attributes.enabled:(true) or (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": undefined,
|
||||
|
@ -295,7 +295,7 @@ describe('loadRules', () => {
|
|||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.enabled:(false) and not (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
|
||||
"filter": "alert.attributes.enabled:(false) and not (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": undefined,
|
||||
|
@ -324,7 +324,7 @@ describe('loadRules', () => {
|
|||
Object {
|
||||
"query": Object {
|
||||
"default_search_operator": "AND",
|
||||
"filter": "alert.attributes.enabled:(true or false) or (alert.attributes.muteAll:true OR alert.attributes.snoozeEndTime > now)",
|
||||
"filter": "alert.attributes.enabled:(true or false) or (alert.attributes.muteAll:true OR alert.attributes.isSnoozedUntil > now)",
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"search": undefined,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers';
|
|||
import { RuleStatusDropdown, ComponentOpts } from './rule_status_dropdown';
|
||||
|
||||
const NOW_STRING = '2020-03-01T00:00:00.000Z';
|
||||
const SNOOZE_END_TIME = new Date('2020-03-04T00:00:00.000Z');
|
||||
const SNOOZE_UNTIL = new Date('2020-03-04T00:00:00.000Z');
|
||||
|
||||
describe('RuleStatusDropdown', () => {
|
||||
const enableRule = jest.fn();
|
||||
|
@ -51,7 +51,7 @@ describe('RuleStatusDropdown', () => {
|
|||
notifyWhen: null,
|
||||
index: 0,
|
||||
updatedAt: new Date('2020-08-20T19:23:38Z'),
|
||||
snoozeEndTime: null,
|
||||
snoozeSchedule: [],
|
||||
} as ComponentOpts['rule'],
|
||||
onRuleChanged: jest.fn(),
|
||||
};
|
||||
|
@ -86,7 +86,7 @@ describe('RuleStatusDropdown', () => {
|
|||
|
||||
const wrapper = mountWithIntl(
|
||||
<RuleStatusDropdown
|
||||
{...{ ...props, rule: { ...props.rule, snoozeEndTime: SNOOZE_END_TIME } }}
|
||||
{...{ ...props, rule: { ...props.rule, isSnoozedUntil: SNOOZE_UNTIL } }}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="statusDropdown"]').first().props().title).toBe('Snoozed');
|
||||
|
@ -108,7 +108,7 @@ describe('RuleStatusDropdown', () => {
|
|||
test('renders status control as disabled when rule is snoozed but also disabled', () => {
|
||||
const wrapper = mountWithIntl(
|
||||
<RuleStatusDropdown
|
||||
{...{ ...props, rule: { ...props.rule, enabled: false, snoozeEndTime: SNOOZE_END_TIME } }}
|
||||
{...{ ...props, rule: { ...props.rule, enabled: false, isSnoozedUntil: SNOOZE_UNTIL } }}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find('[data-test-subj="statusDropdown"]').first().props().title).toBe(
|
||||
|
@ -121,7 +121,7 @@ describe('RuleStatusDropdown', () => {
|
|||
<RuleStatusDropdown
|
||||
{...{
|
||||
...props,
|
||||
rule: { ...props.rule, snoozeEndTime: SNOOZE_END_TIME },
|
||||
rule: { ...props.rule },
|
||||
}}
|
||||
isEditable={false}
|
||||
/>
|
||||
|
|
|
@ -36,7 +36,7 @@ import { Rule } from '../../../../types';
|
|||
type SnoozeUnit = 'm' | 'h' | 'd' | 'w' | 'M';
|
||||
const SNOOZE_END_TIME_FORMAT = 'LL @ LT';
|
||||
|
||||
type DropdownRuleRecord = Pick<Rule, 'enabled' | 'snoozeEndTime' | 'muteAll'>;
|
||||
type DropdownRuleRecord = Pick<Rule, 'enabled' | 'muteAll' | 'isSnoozedUntil'>;
|
||||
|
||||
export interface ComponentOpts {
|
||||
rule: DropdownRuleRecord;
|
||||
|
@ -74,6 +74,11 @@ const usePreviousSnoozeInterval: (p?: string | null) => [string | null, (n: stri
|
|||
return [previousSnoozeInterval, storeAndSetPreviousSnoozeInterval];
|
||||
};
|
||||
|
||||
const isRuleSnoozed = (rule: { isSnoozedUntil?: Date | null; muteAll: boolean }) =>
|
||||
Boolean(
|
||||
(rule.isSnoozedUntil && new Date(rule.isSnoozedUntil).getTime() > Date.now()) || rule.muteAll
|
||||
);
|
||||
|
||||
export const RuleStatusDropdown: React.FunctionComponent<ComponentOpts> = ({
|
||||
rule,
|
||||
onRuleChanged,
|
||||
|
@ -158,11 +163,13 @@ export const RuleStatusDropdown: React.FunctionComponent<ComponentOpts> = ({
|
|||
isEnabled && isSnoozed ? (
|
||||
<EuiToolTip
|
||||
content={
|
||||
rule.muteAll ? INDEFINITELY : moment(rule.snoozeEndTime).format(SNOOZE_END_TIME_FORMAT)
|
||||
rule.muteAll
|
||||
? INDEFINITELY
|
||||
: moment(new Date(rule.isSnoozedUntil!)).format(SNOOZE_END_TIME_FORMAT)
|
||||
}
|
||||
>
|
||||
<EuiText color="subdued" size="xs">
|
||||
{rule.muteAll ? INDEFINITELY : moment(rule.snoozeEndTime).fromNow(true)}
|
||||
{rule.muteAll ? INDEFINITELY : moment(new Date(rule.isSnoozedUntil!)).fromNow(true)}
|
||||
</EuiText>
|
||||
</EuiToolTip>
|
||||
) : null;
|
||||
|
@ -215,7 +222,7 @@ export const RuleStatusDropdown: React.FunctionComponent<ComponentOpts> = ({
|
|||
onChangeSnooze={onChangeSnooze}
|
||||
isEnabled={isEnabled}
|
||||
isSnoozed={isSnoozed}
|
||||
snoozeEndTime={rule.snoozeEndTime}
|
||||
snoozeEndTime={rule.isSnoozedUntil}
|
||||
previousSnoozeInterval={previousSnoozeInterval}
|
||||
/>
|
||||
</EuiPopover>
|
||||
|
@ -476,15 +483,6 @@ const SnoozePanel: React.FunctionComponent<SnoozePanelProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
const isRuleSnoozed = (rule: DropdownRuleRecord) => {
|
||||
const { snoozeEndTime, muteAll } = rule;
|
||||
if (muteAll) return true;
|
||||
if (!snoozeEndTime) {
|
||||
return false;
|
||||
}
|
||||
return moment(Date.now()).isBefore(snoozeEndTime);
|
||||
};
|
||||
|
||||
const futureTimeToInterval = (time?: Date | null) => {
|
||||
if (!time) return;
|
||||
const relativeTime = moment(time).locale('en').fromNow(true);
|
||||
|
|
|
@ -115,6 +115,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
created_by: user.username,
|
||||
schedule: { interval: '1m' },
|
||||
scheduled_task_id: response.body.scheduled_task_id,
|
||||
snooze_schedule: response.body.snooze_schedule,
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
throttle: '1m',
|
||||
|
|
|
@ -73,6 +73,7 @@ const findTestUtils = (
|
|||
params: {},
|
||||
created_by: 'elastic',
|
||||
scheduled_task_id: match.scheduled_task_id,
|
||||
snooze_schedule: match.snooze_schedule,
|
||||
created_at: match.created_at,
|
||||
updated_at: match.updated_at,
|
||||
throttle: '1m',
|
||||
|
@ -82,9 +83,7 @@ const findTestUtils = (
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
execution_status: match.execution_status,
|
||||
...(describeType === 'internal'
|
||||
? { monitoring: match.monitoring, snooze_end_time: match.snooze_end_time }
|
||||
: {}),
|
||||
...(describeType === 'internal' ? { monitoring: match.monitoring } : {}),
|
||||
});
|
||||
expect(Date.parse(match.created_at)).to.be.greaterThan(0);
|
||||
expect(Date.parse(match.updated_at)).to.be.greaterThan(0);
|
||||
|
@ -283,9 +282,8 @@ const findTestUtils = (
|
|||
created_at: match.created_at,
|
||||
updated_at: match.updated_at,
|
||||
execution_status: match.execution_status,
|
||||
...(describeType === 'internal'
|
||||
? { monitoring: match.monitoring, snooze_end_time: match.snooze_end_time }
|
||||
: {}),
|
||||
snooze_schedule: match.snooze_schedule,
|
||||
...(describeType === 'internal' ? { monitoring: match.monitoring } : {}),
|
||||
});
|
||||
expect(Date.parse(match.created_at)).to.be.greaterThan(0);
|
||||
expect(Date.parse(match.updated_at)).to.be.greaterThan(0);
|
||||
|
|
|
@ -72,6 +72,7 @@ const getTestUtils = (
|
|||
params: {},
|
||||
created_by: 'elastic',
|
||||
scheduled_task_id: response.body.scheduled_task_id,
|
||||
snooze_schedule: response.body.snooze_schedule,
|
||||
updated_at: response.body.updated_at,
|
||||
created_at: response.body.created_at,
|
||||
throttle: '1m',
|
||||
|
@ -84,7 +85,6 @@ const getTestUtils = (
|
|||
...(describeType === 'internal'
|
||||
? {
|
||||
monitoring: response.body.monitoring,
|
||||
snooze_end_time: response.body.snooze_end_time,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
|
|
|
@ -99,7 +99,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext)
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(true);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -156,7 +156,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext)
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(true);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -224,7 +224,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext)
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(true);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -292,7 +292,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext)
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(true);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
|
|
@ -97,12 +97,19 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext
|
|||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.statusCode).to.eql(204);
|
||||
expect(response.body).to.eql('');
|
||||
const now = Date.now();
|
||||
const { body: updatedAlert } = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(space.id)}/internal/alerting/rule/${createdAlert.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(FUTURE_SNOOZE_TIME);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(1);
|
||||
// Due to latency, test to make sure the returned rRule.dtstart is within 10 seconds of the current time
|
||||
const { rRule, duration } = updatedAlert.snooze_schedule[0];
|
||||
expect(Math.abs(Date.parse(rRule.dtstart) - now) < 10000).to.be(true);
|
||||
expect(Math.abs(duration - (Date.parse(FUTURE_SNOOZE_TIME) - now)) < 10000).to.be(
|
||||
true
|
||||
);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -156,12 +163,19 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext
|
|||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.statusCode).to.eql(204);
|
||||
expect(response.body).to.eql('');
|
||||
const now = Date.now();
|
||||
const { body: updatedAlert } = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(space.id)}/internal/alerting/rule/${createdAlert.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(FUTURE_SNOOZE_TIME);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(1);
|
||||
// Due to latency, test to make sure the returned rRule.dtstart is within 10 seconds of the current time
|
||||
const { rRule, duration } = updatedAlert.snooze_schedule[0];
|
||||
expect(Math.abs(Date.parse(rRule.dtstart) - now) < 10000).to.be(true);
|
||||
expect(Math.abs(duration - (Date.parse(FUTURE_SNOOZE_TIME) - now)) < 10000).to.be(
|
||||
true
|
||||
);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -226,12 +240,19 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext
|
|||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.statusCode).to.eql(204);
|
||||
expect(response.body).to.eql('');
|
||||
const now = Date.now();
|
||||
const { body: updatedAlert } = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(space.id)}/internal/alerting/rule/${createdAlert.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(FUTURE_SNOOZE_TIME);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(1);
|
||||
// Due to latency, test to make sure the returned rRule.dtstart is within 10 seconds of the current time
|
||||
const { rRule, duration } = updatedAlert.snooze_schedule[0];
|
||||
expect(Math.abs(Date.parse(rRule.dtstart) - now) < 10000).to.be(true);
|
||||
expect(Math.abs(duration - (Date.parse(FUTURE_SNOOZE_TIME) - now)) < 10000).to.be(
|
||||
true
|
||||
);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -296,12 +317,19 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext
|
|||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.statusCode).to.eql(204);
|
||||
expect(response.body).to.eql('');
|
||||
const now = Date.now();
|
||||
const { body: updatedAlert } = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(space.id)}/internal/alerting/rule/${createdAlert.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(FUTURE_SNOOZE_TIME);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(1);
|
||||
// Due to latency, test to make sure the returned rRule.dtstart is within 10 seconds of the current time
|
||||
const { rRule, duration } = updatedAlert.snooze_schedule[0];
|
||||
expect(Math.abs(Date.parse(rRule.dtstart) - now) < 10000).to.be(true);
|
||||
expect(Math.abs(duration - (Date.parse(FUTURE_SNOOZE_TIME) - now)) < 10000).to.be(
|
||||
true
|
||||
);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -383,7 +411,7 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(null);
|
||||
expect(updatedAlert.snooze_schedule).to.eql([]);
|
||||
expect(updatedAlert.mute_all).to.eql(true);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
|
|
@ -104,7 +104,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -166,7 +166,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -239,7 +239,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -312,7 +312,7 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
|
|
@ -98,7 +98,7 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(null);
|
||||
expect(updatedAlert.snooze_schedule).to.eql(null);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -155,7 +155,7 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(null);
|
||||
expect(updatedAlert.snooze_schedule).to.eql(null);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -223,7 +223,7 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(null);
|
||||
expect(updatedAlert.snooze_schedule).to.eql(null);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -291,7 +291,7 @@ export default function createUnsnoozeRuleTests({ getService }: FtrProviderConte
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(null);
|
||||
expect(updatedAlert.snooze_schedule).to.eql(null);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
|
|
@ -129,6 +129,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
},
|
||||
],
|
||||
scheduled_task_id: createdAlert.scheduled_task_id,
|
||||
snooze_schedule: createdAlert.snooze_schedule,
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
|
@ -213,6 +214,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
scheduled_task_id: createdAlert.scheduled_task_id,
|
||||
snooze_schedule: createdAlert.snooze_schedule,
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
|
@ -308,6 +310,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
scheduled_task_id: createdAlert.scheduled_task_id,
|
||||
snooze_schedule: createdAlert.snooze_schedule,
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
|
@ -403,6 +406,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
scheduled_task_id: createdAlert.scheduled_task_id,
|
||||
snooze_schedule: createdAlert.snooze_schedule,
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
|
@ -496,6 +500,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
scheduled_task_id: createdAlert.scheduled_task_id,
|
||||
snooze_schedule: createdAlert.snooze_schedule,
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
|
|
|
@ -85,6 +85,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
created_by: null,
|
||||
schedule: { interval: '1m' },
|
||||
scheduled_task_id: response.body.scheduled_task_id,
|
||||
snooze_schedule: response.body.snooze_schedule,
|
||||
updated_by: null,
|
||||
api_key_owner: null,
|
||||
throttle: '1m',
|
||||
|
@ -180,6 +181,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
created_by: null,
|
||||
schedule: { interval: '1m' },
|
||||
scheduled_task_id: response.body.scheduled_task_id,
|
||||
snooze_schedule: response.body.snooze_schedule,
|
||||
updated_by: null,
|
||||
api_key_owner: null,
|
||||
throttle: '1m',
|
||||
|
@ -475,6 +477,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
createdBy: null,
|
||||
schedule: { interval: '1m' },
|
||||
scheduledTaskId: response.body.scheduledTaskId,
|
||||
snoozeSchedule: response.body.snoozeSchedule,
|
||||
updatedBy: null,
|
||||
apiKeyOwner: null,
|
||||
throttle: '1m',
|
||||
|
|
|
@ -64,6 +64,7 @@ const findTestUtils = (
|
|||
created_by: null,
|
||||
api_key_owner: null,
|
||||
scheduled_task_id: match.scheduled_task_id,
|
||||
snooze_schedule: match.snooze_schedule,
|
||||
updated_by: null,
|
||||
throttle: '1m',
|
||||
notify_when: 'onThrottleInterval',
|
||||
|
@ -72,9 +73,7 @@ const findTestUtils = (
|
|||
created_at: match.created_at,
|
||||
updated_at: match.updated_at,
|
||||
execution_status: match.execution_status,
|
||||
...(describeType === 'internal'
|
||||
? { monitoring: match.monitoring, snooze_end_time: match.snooze_end_time }
|
||||
: {}),
|
||||
...(describeType === 'internal' ? { monitoring: match.monitoring } : {}),
|
||||
});
|
||||
expect(Date.parse(match.created_at)).to.be.greaterThan(0);
|
||||
expect(Date.parse(match.updated_at)).to.be.greaterThan(0);
|
||||
|
@ -296,6 +295,7 @@ export default function createFindTests({ getService }: FtrProviderContext) {
|
|||
createdBy: null,
|
||||
apiKeyOwner: null,
|
||||
scheduledTaskId: match.scheduledTaskId,
|
||||
snoozeSchedule: match.snoozeSchedule,
|
||||
updatedBy: null,
|
||||
throttle: '1m',
|
||||
notifyWhen: 'onThrottleInterval',
|
||||
|
|
|
@ -45,6 +45,7 @@ const getTestUtils = (
|
|||
params: {},
|
||||
created_by: null,
|
||||
scheduled_task_id: response.body.scheduled_task_id,
|
||||
snooze_schedule: response.body.snooze_schedule,
|
||||
updated_by: null,
|
||||
api_key_owner: null,
|
||||
throttle: '1m',
|
||||
|
@ -55,7 +56,7 @@ const getTestUtils = (
|
|||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
...(describeType === 'internal'
|
||||
? { monitoring: response.body.monitoring, snooze_end_time: response.body.snooze_end_time }
|
||||
? { monitoring: response.body.monitoring, snooze_schedule: response.body.snooze_schedule }
|
||||
: {}),
|
||||
});
|
||||
expect(Date.parse(response.body.created_at)).to.be.greaterThan(0);
|
||||
|
@ -136,6 +137,7 @@ export default function createGetTests({ getService }: FtrProviderContext) {
|
|||
params: {},
|
||||
createdBy: null,
|
||||
scheduledTaskId: response.body.scheduledTaskId,
|
||||
snoozeSchedule: response.body.snoozeSchedule,
|
||||
updatedBy: null,
|
||||
apiKeyOwner: null,
|
||||
throttle: '1m',
|
||||
|
|
|
@ -41,7 +41,7 @@ export default function createMuteTests({ getService }: FtrProviderContext) {
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(true);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest: supertestWithoutAuth,
|
||||
|
@ -70,7 +70,7 @@ export default function createMuteTests({ getService }: FtrProviderContext) {
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(true);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
|
|
@ -70,11 +70,16 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext
|
|||
|
||||
expect(response.statusCode).to.eql(204);
|
||||
expect(response.body).to.eql('');
|
||||
const now = Date.now();
|
||||
const { body: updatedAlert } = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdRule.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(FUTURE_SNOOZE_TIME);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(1);
|
||||
// Due to latency, test to make sure the returned rRule.dtstart is within 10 seconds of the current time
|
||||
const { rRule, duration } = updatedAlert.snooze_schedule[0];
|
||||
expect(Math.abs(Date.parse(rRule.dtstart) - now) < 10000).to.be(true);
|
||||
expect(Math.abs(duration - (Date.parse(FUTURE_SNOOZE_TIME) - now)) < 10000).to.be(true);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -126,7 +131,7 @@ export default function createSnoozeRuleTests({ getService }: FtrProviderContext
|
|||
.get(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdRule.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(null);
|
||||
expect(updatedAlert.snooze_schedule).to.eql([]);
|
||||
expect(updatedAlert.mute_all).to.eql(true);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
|
|
@ -42,7 +42,7 @@ export default function createUnmuteTests({ getService }: FtrProviderContext) {
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -76,7 +76,7 @@ export default function createUnmuteTests({ getService }: FtrProviderContext) {
|
|||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
expect(updatedAlert.mute_all).to.eql(false);
|
||||
expect(updatedAlert.snooze_end_time).to.eql(undefined);
|
||||
expect(updatedAlert.snooze_schedule.length).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
|
|
@ -60,6 +60,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
muted_alert_ids: [],
|
||||
notify_when: 'onThrottleInterval',
|
||||
scheduled_task_id: createdAlert.scheduled_task_id,
|
||||
snooze_schedule: createdAlert.snooze_schedule,
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
|
@ -160,6 +161,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
mutedInstanceIds: [],
|
||||
notifyWhen: 'onThrottleInterval',
|
||||
scheduledTaskId: createdAlert.scheduled_task_id,
|
||||
snoozeSchedule: createdAlert.snooze_schedule,
|
||||
createdAt: response.body.createdAt,
|
||||
updatedAt: response.body.updatedAt,
|
||||
executionStatus: response.body.executionStatus,
|
||||
|
|
32
yarn.lock
32
yarn.lock
|
@ -7006,6 +7006,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
||||
|
||||
"@types/rrule@^2.2.9":
|
||||
version "2.2.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/rrule/-/rrule-2.2.9.tgz#b25222b5057b9a9e6eea28ce9e94673a957c960f"
|
||||
integrity sha512-OWTezBoGwsL2nn9SFbLbiTrAic1hpxAIRqeF8QDB84iW6KBEAHM6Oj9T2BEokgeIDgT1q73sfD0gI1S2yElSFA==
|
||||
dependencies:
|
||||
rrule "*"
|
||||
|
||||
"@types/seedrandom@>=2.0.0 <4.0.0":
|
||||
version "2.4.28"
|
||||
resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f"
|
||||
|
@ -19469,11 +19476,16 @@ lru-queue@0.1:
|
|||
dependencies:
|
||||
es5-ext "~0.10.2"
|
||||
|
||||
luxon@^1.25.0:
|
||||
luxon@^1.21.3, luxon@^1.25.0:
|
||||
version "1.28.0"
|
||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf"
|
||||
integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==
|
||||
|
||||
luxon@^2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.3.2.tgz#5f2f3002b8c39b60a7b7ad24b2a85d90dc5db49c"
|
||||
integrity sha512-MlAQQVMFhGk4WUA6gpfsy0QycnKP0+NlCBJRVRNPxxSIbjrCbQ65nrpJD3FVyJNZLuJ0uoqL57ye6BmDYgHaSw==
|
||||
|
||||
lz-string@^1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
|
||||
|
@ -25234,6 +25246,24 @@ rollup@^0.25.8:
|
|||
minimist "^1.2.0"
|
||||
source-map-support "^0.3.2"
|
||||
|
||||
rrule@*:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/rrule/-/rrule-2.6.9.tgz#8ee4ee261451e84852741f92ded769245580744a"
|
||||
integrity sha512-PE4ErZDMfAcRnc1B35bZgPGS9mbn7Z9bKDgk6+XgrIwvBjeWk7JVEYsqKwHYTrDGzsHPtZTpaon8IyeKzAhj5w==
|
||||
dependencies:
|
||||
tslib "^1.10.0"
|
||||
optionalDependencies:
|
||||
luxon "^1.21.3"
|
||||
|
||||
rrule@2.6.4:
|
||||
version "2.6.4"
|
||||
resolved "https://registry.yarnpkg.com/rrule/-/rrule-2.6.4.tgz#7f4f31fda12bc7249bb176c891109a9bc448e035"
|
||||
integrity sha512-sLdnh4lmjUqq8liFiOUXD5kWp/FcnbDLPwq5YAc/RrN6120XOPb86Ae5zxF7ttBVq8O3LxjjORMEit1baluahA==
|
||||
dependencies:
|
||||
tslib "^1.10.0"
|
||||
optionalDependencies:
|
||||
luxon "^1.21.3"
|
||||
|
||||
rst-selector-parser@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue