mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[RAM] Adds auto-incrementing revision field to rules (#147398)
## Summary Resolves https://github.com/elastic/kibana/issues/137164. This PR adds a new `revision` field (number) to Alerting Rules that auto-increments when relevant content changes have been made via the `Rules Client`. _Relevant content changes_ are defined as any content change that the user may either want to be notified about, or have the option to later revert to. This will include most all changes, except the enabling/disabling of the rule, or updates to metadata fields like `executionStatus` or `monitoring`. This `revision` field is not intended to be user editable, and should be ignored if ever provided via the API. See https://github.com/elastic/kibana/issues/136213 for additional details. To be followed-up by https://github.com/elastic/kibana/issues/137168, which will remove the version bump logic from security solution. ## Details ### Migrations Includes SO migration to default `revision` to `0` when upgrading a cluster, or when importing `pre-8.8.0` rules via the SO Management UI. For consistency, security rule import follows the same logic as SO Management and will not reset the `revision` to `0` when overriding or creating a new rule. ### Downstream Index Updates * EventLog _has not_ been updated to include `revision` along with basic rule fields currently being written. Should we? * AAD Schema will be updated in https://github.com/elastic/kibana/pull/151388 (as this one is getting pretty big) to include `revision` so alerts written will include which specific revision of the rule created the alert. ### Reference Fields Any creation of or modification to `actions` will result in a revision increment. More typical reference fields like `exception lists` on the security side will only result in a revision increment when the list is initially associated/deleted from the rule (as subsequent updates will be done directly against the list). ### RuleClient Updates The following methods within the RuleClient have been updated to support incrementing revision when relevant field changes have been detected: * `clone()` - resets to 0 currently (see open question) * `update()` - increments `revision` so long a change has been made to relevant fields (fields not in [ignore list](https://github.com/elastic/kibana/pull/147398/files#diff-6736e143ede2dc06e825bddcdc23b4d088a6620805751db4eddc5900d586c4dfR69-R85)) * `bulkEdit()` - increments `revision` for relevant fields (all current bulk edit fields minus api key/snooze/mute) Mutation methods not updated to include revision log: * `snooze()` * `unsnooze()` * `clearExpiredSnoozes()` * `muteAll()` * `unmuteAll()` * `muteInstance()` * `unmuteInstance()` * `updateApiKey()` - increments revision as rule functionality could be impacted ## Open questions: - [X] Should `clone()` in RulesClient reset revision to 0 as if it's a new rule, or keep the current value? (see [comment](https://github.com/elastic/kibana/pull/147398/files#r1106484105)) - [X] What about snooze/un-snooze, and mute/unmute? Should we update revision on these field changes as well? (see [comment](https://github.com/elastic/kibana/pull/147398/files#r1106431966)) - Discussed with @XavierM and determined to not update on snooze/mute/API key changes as this actions could be plentiful and don't necessarily represent a version of the rule a user would want to revert to, thus polluting the revision history. - [ ] Should we write `revision` to EventLog? --- ### Checklist Delete any items that are not applicable to this PR. - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] To work with docs team - [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 ### For maintainers - [X] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
This commit is contained in:
parent
a564ca5fe3
commit
15f1f64ace
112 changed files with 663 additions and 20 deletions
|
@ -57,7 +57,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
Object {
|
||||
"action": "6cfc277ed3211639e37546ac625f4a68f2494215",
|
||||
"action_task_params": "5f419caba96dd8c77d0f94013e71d43890e3d5d6",
|
||||
"alert": "785240e3137f5eb1a0f8986e5b8eff99780fc04f",
|
||||
"alert": "1e4cd6941f1eb39c729c646e91fbfb9700de84b9",
|
||||
"api_key_pending_invalidation": "16e7bcf8e78764102d7f525542d5b616809a21ee",
|
||||
"apm-indices": "d19dd7fb51f2d2cbc1f8769481721e0953f9a6d2",
|
||||
"apm-server-schema": "1d42f17eff9ec6c16d3a9324d9539e2d123d0a9a",
|
||||
|
|
|
@ -45,6 +45,7 @@ const mockRule: RuleTableItem = {
|
|||
ruleType: 'Test Rule Type',
|
||||
isEditable: true,
|
||||
enabledInLicense: true,
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
export const RulesListNotifyBadgeSandbox = ({ triggersActionsUi }: SandboxProps) => {
|
||||
|
|
|
@ -161,6 +161,7 @@ export interface Rule<Params extends RuleTypeParams = never> {
|
|||
isSnoozedUntil?: Date | null;
|
||||
lastRun?: RuleLastRun | null;
|
||||
nextRun?: Date | null;
|
||||
revision: number;
|
||||
running?: boolean | null;
|
||||
viewInAppRelativeUrl?: string;
|
||||
}
|
||||
|
|
|
@ -132,6 +132,7 @@ describe('loadRule', () => {
|
|||
"params": Object {
|
||||
"x": 42,
|
||||
},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1s",
|
||||
},
|
||||
|
@ -268,6 +269,7 @@ function getApiRule() {
|
|||
updated_by: '2889684073',
|
||||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
revision: 0,
|
||||
schedule: {
|
||||
interval: '1s',
|
||||
},
|
||||
|
@ -333,5 +335,6 @@ function getRule(): Rule<{ x: number }> {
|
|||
lastExecutionDate: RuleExecuteDate,
|
||||
lastDuration: 1194,
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ describe('common_transformations', () => {
|
|||
notify_when: 'onActiveAlert',
|
||||
mute_all: false,
|
||||
muted_alert_ids: ['bob', 'jim'],
|
||||
revision: 0,
|
||||
execution_status: {
|
||||
last_execution_date: dateExecuted.toISOString(),
|
||||
last_duration: 42,
|
||||
|
@ -185,6 +186,7 @@ describe('common_transformations', () => {
|
|||
],
|
||||
},
|
||||
},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1s",
|
||||
},
|
||||
|
@ -229,6 +231,7 @@ describe('common_transformations', () => {
|
|||
notify_when: 'onActiveAlert',
|
||||
mute_all: false,
|
||||
muted_alert_ids: ['bob', 'jim'],
|
||||
revision: 0,
|
||||
execution_status: {
|
||||
last_execution_date: dateExecuted.toISOString(),
|
||||
status: 'error',
|
||||
|
@ -344,6 +347,7 @@ describe('common_transformations', () => {
|
|||
"nextRun": 2021-12-15T12:34:55.789Z,
|
||||
"notifyWhen": "onActiveAlert",
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1s",
|
||||
},
|
||||
|
|
|
@ -784,4 +784,5 @@ const BaseRule: SanitizedRule<{ bar: boolean }> = {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
|
|
@ -59,6 +59,7 @@ describe('bulkEditInternalRulesRoute', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
const mockedAlerts: Array<SanitizedRule<{}>> = [mockedAlert];
|
||||
|
|
|
@ -65,6 +65,7 @@ describe('cloneRuleRoute', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
const ruleToClone: AsApiContract<CreateOptions<{ bar: boolean }>['data']> = {
|
||||
|
@ -90,6 +91,7 @@ describe('cloneRuleRoute', () => {
|
|||
created_at: mockedRule.createdAt,
|
||||
updated_at: mockedRule.updatedAt,
|
||||
id: mockedRule.id,
|
||||
revision: 0,
|
||||
execution_status: {
|
||||
status: mockedRule.executionStatus.status,
|
||||
last_execution_date: mockedRule.executionStatus.lastExecutionDate,
|
||||
|
|
|
@ -68,6 +68,7 @@ describe('createRuleRoute', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
const ruleToCreate: AsApiContract<CreateOptions<{ bar: boolean }>['data']> = {
|
||||
|
@ -93,6 +94,7 @@ describe('createRuleRoute', () => {
|
|||
created_at: mockedAlert.createdAt,
|
||||
updated_at: mockedAlert.updatedAt,
|
||||
id: mockedAlert.id,
|
||||
revision: mockedAlert.revision,
|
||||
execution_status: {
|
||||
status: mockedAlert.executionStatus.status,
|
||||
last_execution_date: mockedAlert.executionStatus.lastExecutionDate,
|
||||
|
|
|
@ -62,6 +62,7 @@ describe('getRuleRoute', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
const getResult: AsApiContract<SanitizedRule<{ bar: boolean }>> = {
|
||||
|
@ -76,6 +77,7 @@ describe('getRuleRoute', () => {
|
|||
created_at: mockedAlert.createdAt,
|
||||
updated_at: mockedAlert.updatedAt,
|
||||
id: mockedAlert.id,
|
||||
revision: mockedAlert.revision,
|
||||
execution_status: {
|
||||
status: mockedAlert.executionStatus.status,
|
||||
last_execution_date: mockedAlert.executionStatus.lastExecutionDate,
|
||||
|
|
|
@ -80,6 +80,7 @@ describe('createAlertRoute', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
it('creates an alert with proper parameters', async () => {
|
||||
|
|
|
@ -66,6 +66,7 @@ describe('getAlertRoute', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
it('gets an alert with proper parameters', async () => {
|
||||
|
|
|
@ -60,6 +60,7 @@ const sampleRule: SanitizedRule<RuleTypeParams> & { activeSnoozes?: string[] } =
|
|||
},
|
||||
},
|
||||
nextRun: DATE_2020,
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
describe('rewriteRule', () => {
|
||||
|
|
|
@ -64,6 +64,7 @@ describe('resolveRuleRoute', () => {
|
|||
},
|
||||
outcome: 'aliasMatch',
|
||||
alias_target_id: '2',
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
const resolveResult: AsApiContract<ResolvedSanitizedRule<{ bar: boolean }>> = {
|
||||
|
@ -88,6 +89,7 @@ describe('resolveRuleRoute', () => {
|
|||
created_at: mockedRule.createdAt,
|
||||
updated_at: mockedRule.updatedAt,
|
||||
id: mockedRule.id,
|
||||
revision: mockedRule.revision,
|
||||
execution_status: {
|
||||
status: mockedRule.executionStatus.status,
|
||||
last_execution_date: mockedRule.executionStatus.lastExecutionDate,
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 { UpdateOptions } from '..';
|
||||
import { mockedDateString } from '../tests/lib';
|
||||
import { incrementRevision } from './increment_revision';
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import { RawRule, RuleTypeParams } from '../../types';
|
||||
|
||||
describe('incrementRevision', () => {
|
||||
const currentRule: SavedObject<RawRule> = {
|
||||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: {
|
||||
enabled: true,
|
||||
name: 'rule-name',
|
||||
tags: ['tag-1', 'tag-2'],
|
||||
alertTypeId: '123',
|
||||
consumer: 'rule-consumer',
|
||||
legacyId: null,
|
||||
schedule: { interval: '1s' },
|
||||
actions: [],
|
||||
params: {},
|
||||
createdBy: null,
|
||||
updatedBy: null,
|
||||
createdAt: mockedDateString,
|
||||
updatedAt: mockedDateString,
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
throttle: null,
|
||||
notifyWhen: null,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
executionStatus: {
|
||||
status: 'unknown',
|
||||
lastExecutionDate: '2020-08-20T19:23:38Z',
|
||||
error: null,
|
||||
warning: null,
|
||||
},
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
};
|
||||
|
||||
const updateOptions: UpdateOptions<RuleTypeParams> = {
|
||||
id: '1',
|
||||
data: {
|
||||
schedule: {
|
||||
interval: '1m',
|
||||
},
|
||||
name: 'abc',
|
||||
tags: ['foo'],
|
||||
params: {
|
||||
bar: true,
|
||||
risk_score: 40,
|
||||
severity: 'low',
|
||||
},
|
||||
throttle: null,
|
||||
notifyWhen: 'onActiveAlert',
|
||||
actions: [],
|
||||
},
|
||||
};
|
||||
const updatedParams: RuleTypeParams = { bar: true, risk_score: 40, severity: 'low' };
|
||||
|
||||
it('should return the current revision if no attrs or params are updated', () => {
|
||||
// @ts-expect-error
|
||||
expect(incrementRevision(currentRule, { data: {} }, {})).toBe(0);
|
||||
});
|
||||
|
||||
it('should increment the revision if a root level attr is updated', () => {
|
||||
expect(incrementRevision(currentRule, updateOptions, {})).toBe(1);
|
||||
});
|
||||
|
||||
it('should increment the revision if a rule param is updated', () => {
|
||||
// @ts-expect-error
|
||||
expect(incrementRevision(currentRule, { data: {} }, updatedParams)).toBe(1);
|
||||
});
|
||||
|
||||
it('should not increment the revision if an excluded attr is updated', () => {
|
||||
// @ts-expect-error
|
||||
expect(incrementRevision(currentRule, { data: { activeSnoozes: 'excludedValue' } }, {})).toBe(
|
||||
0
|
||||
);
|
||||
});
|
||||
|
||||
it('should not increment the revision if an excluded param is updated', () => {
|
||||
expect(
|
||||
incrementRevision(
|
||||
currentRule,
|
||||
// @ts-expect-error
|
||||
{ data: {} },
|
||||
{ isSnoozedUntil: '1970-01-02T00:00:00.000Z' }
|
||||
)
|
||||
).toBe(0);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 { SavedObject } from '@kbn/core/server';
|
||||
import { get, isEqual } from 'lodash';
|
||||
import { RawRule, RuleTypeParams } from '../../types';
|
||||
import { fieldsToExcludeFromRevisionUpdates, UpdateOptions } from '..';
|
||||
|
||||
export function incrementRevision<Params extends RuleTypeParams>(
|
||||
currentRule: SavedObject<RawRule>,
|
||||
{ data }: UpdateOptions<Params>,
|
||||
updatedParams: RuleTypeParams
|
||||
): number {
|
||||
// Diff root level attrs
|
||||
for (const [field, value] of Object.entries(data).filter(([key]) => key !== 'params')) {
|
||||
if (
|
||||
!fieldsToExcludeFromRevisionUpdates.has(field) &&
|
||||
!isEqual(value, get(currentRule.attributes, field))
|
||||
) {
|
||||
return currentRule.attributes.revision + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Diff rule params
|
||||
for (const [field, value] of Object.entries(updatedParams)) {
|
||||
if (
|
||||
!fieldsToExcludeFromRevisionUpdates.has(field) &&
|
||||
!isEqual(value, get(currentRule.attributes.params, field))
|
||||
) {
|
||||
return currentRule.attributes.revision + 1;
|
||||
}
|
||||
}
|
||||
return currentRule.attributes.revision;
|
||||
}
|
|
@ -16,3 +16,4 @@ export { scheduleTask } from './schedule_task';
|
|||
export { createNewAPIKeySet } from './create_new_api_key_set';
|
||||
export { recoverRuleAlerts } from './recover_rule_alerts';
|
||||
export { addUuid } from './add_uuid';
|
||||
export { incrementRevision } from './increment_revision';
|
||||
|
|
|
@ -69,6 +69,9 @@ export type BulkEditFields = keyof Pick<
|
|||
'actions' | 'tags' | 'schedule' | 'throttle' | 'notifyWhen' | 'snoozeSchedule' | 'apiKey'
|
||||
>;
|
||||
|
||||
export const bulkEditFieldsToExcludeFromRevisionUpdates: ReadonlySet<BulkEditOperation['field']> =
|
||||
new Set(['snoozeSchedule', 'apiKey']);
|
||||
|
||||
export type BulkEditOperation =
|
||||
| {
|
||||
operation: 'add' | 'delete' | 'set';
|
||||
|
@ -126,16 +129,22 @@ export type RuleParamsModifier<Params extends RuleTypeParams> = (
|
|||
params: Params
|
||||
) => Promise<RuleParamsModifierResult<Params>>;
|
||||
|
||||
export type ShouldIncrementRevision<Params extends RuleTypeParams> = (
|
||||
params?: RuleTypeParams
|
||||
) => boolean;
|
||||
|
||||
export interface BulkEditOptionsFilter<Params extends RuleTypeParams> {
|
||||
filter?: string | KueryNode;
|
||||
operations: BulkEditOperation[];
|
||||
paramsModifier?: RuleParamsModifier<Params>;
|
||||
shouldIncrementRevision?: ShouldIncrementRevision<Params>;
|
||||
}
|
||||
|
||||
export interface BulkEditOptionsIds<Params extends RuleTypeParams> {
|
||||
ids: string[];
|
||||
operations: BulkEditOperation[];
|
||||
paramsModifier?: RuleParamsModifier<Params>;
|
||||
shouldIncrementRevision?: ShouldIncrementRevision<Params>;
|
||||
}
|
||||
|
||||
export type BulkEditOptions<Params extends RuleTypeParams> =
|
||||
|
@ -244,12 +253,13 @@ export async function bulkEdit<Params extends RuleTypeParams>(
|
|||
context.logger,
|
||||
`rulesClient.update('operations=${JSON.stringify(options.operations)}, paramsModifier=${
|
||||
options.paramsModifier ? '[Function]' : undefined
|
||||
}')`,
|
||||
}', shouldIncrementRevision=${options.shouldIncrementRevision ? '[Function]' : undefined}')`,
|
||||
(filterKueryNode: KueryNode | null) =>
|
||||
bulkEditOcc(context, {
|
||||
filter: filterKueryNode,
|
||||
operations: options.operations,
|
||||
paramsModifier: options.paramsModifier,
|
||||
shouldIncrementRevision: options.shouldIncrementRevision,
|
||||
}),
|
||||
qNodeFilterWithAuth
|
||||
);
|
||||
|
@ -284,10 +294,12 @@ async function bulkEditOcc<Params extends RuleTypeParams>(
|
|||
filter,
|
||||
operations,
|
||||
paramsModifier,
|
||||
shouldIncrementRevision,
|
||||
}: {
|
||||
filter: KueryNode | null;
|
||||
operations: BulkEditOptions<Params>['operations'];
|
||||
paramsModifier: BulkEditOptions<Params>['paramsModifier'];
|
||||
shouldIncrementRevision?: BulkEditOptions<Params>['shouldIncrementRevision'];
|
||||
}
|
||||
): Promise<{
|
||||
apiKeysToInvalidate: string[];
|
||||
|
@ -326,6 +338,7 @@ async function bulkEditOcc<Params extends RuleTypeParams>(
|
|||
skipped,
|
||||
errors,
|
||||
username,
|
||||
shouldIncrementRevision,
|
||||
}),
|
||||
{ concurrency: API_KEY_GENERATE_CONCURRENCY }
|
||||
);
|
||||
|
@ -395,6 +408,7 @@ async function updateRuleAttributesAndParamsInMemory<Params extends RuleTypePara
|
|||
skipped,
|
||||
errors,
|
||||
username,
|
||||
shouldIncrementRevision = () => true,
|
||||
}: {
|
||||
context: RulesClientContext;
|
||||
rule: SavedObjectsFindResult<RawRule>;
|
||||
|
@ -405,6 +419,7 @@ async function updateRuleAttributesAndParamsInMemory<Params extends RuleTypePara
|
|||
skipped: BulkActionSkipResult[];
|
||||
errors: BulkOperationError[];
|
||||
username: string | null;
|
||||
shouldIncrementRevision: BulkEditOptions<Params>['shouldIncrementRevision'];
|
||||
}): Promise<void> {
|
||||
try {
|
||||
if (rule.attributes.apiKey) {
|
||||
|
@ -427,6 +442,15 @@ async function updateRuleAttributesAndParamsInMemory<Params extends RuleTypePara
|
|||
isParamsUpdateSkipped: true,
|
||||
};
|
||||
|
||||
// Increment revision if params ended up being modified AND it wasn't already incremented as part of attribute update
|
||||
if (
|
||||
shouldIncrementRevision(ruleParams) &&
|
||||
!isParamsUpdateSkipped &&
|
||||
rule.attributes.revision === attributes.revision
|
||||
) {
|
||||
attributes.revision += 1;
|
||||
}
|
||||
|
||||
// If neither attributes nor parameters were updated, mark
|
||||
// the rule as skipped and continue to the next rule.
|
||||
if (isAttributesUpdateSkipped && isParamsUpdateSkipped) {
|
||||
|
@ -646,6 +670,14 @@ async function getUpdatedAttributesFromOperations(
|
|||
}
|
||||
}
|
||||
}
|
||||
// Only increment revision if update wasn't skipped and `operation.field` should result in a revision increment
|
||||
if (
|
||||
!isAttributesUpdateSkipped &&
|
||||
!bulkEditFieldsToExcludeFromRevisionUpdates.has(operation.field) &&
|
||||
rule.attributes.revision - attributes.revision === 0
|
||||
) {
|
||||
attributes.revision += 1;
|
||||
}
|
||||
}
|
||||
return {
|
||||
attributes,
|
||||
|
|
|
@ -117,6 +117,7 @@ export async function clone<Params extends RuleTypeParams = never>(
|
|||
mutedInstanceIds: [],
|
||||
executionStatus: getRuleExecutionStatusPending(lastRunTimestamp.toISOString()),
|
||||
monitoring: getDefaultMonitoring(lastRunTimestamp.toISOString()),
|
||||
revision: 0,
|
||||
scheduledTaskId: null,
|
||||
running: false,
|
||||
};
|
||||
|
|
|
@ -43,6 +43,7 @@ export interface CreateOptions<Params extends RuleTypeParams> {
|
|||
| 'isSnoozedUntil'
|
||||
| 'lastRun'
|
||||
| 'nextRun'
|
||||
| 'revision'
|
||||
> & { actions: NormalizedAlertAction[] };
|
||||
options?: SavedObjectOptions;
|
||||
allowMissingConnectorSecrets?: boolean;
|
||||
|
@ -153,6 +154,7 @@ export async function create<Params extends RuleTypeParams = never>(
|
|||
throttle,
|
||||
executionStatus: getRuleExecutionStatusPending(lastRunTimestamp.toISOString()),
|
||||
monitoring: getDefaultMonitoring(lastRunTimestamp.toISOString()),
|
||||
revision: 0,
|
||||
running: false,
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import Boom from '@hapi/boom';
|
|||
import { isEqual, omit } from 'lodash';
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import type { ShouldIncrementRevision } from './bulk_edit';
|
||||
import {
|
||||
PartialRule,
|
||||
RawRule,
|
||||
|
@ -30,6 +31,7 @@ import {
|
|||
updateMeta,
|
||||
getPartialRuleFromRaw,
|
||||
addUuid,
|
||||
incrementRevision,
|
||||
} from '../lib';
|
||||
import { generateAPIKeyName, apiKeyAsAlertAttributes } from '../common';
|
||||
|
||||
|
@ -45,22 +47,29 @@ export interface UpdateOptions<Params extends RuleTypeParams> {
|
|||
notifyWhen?: RuleNotifyWhenType | null;
|
||||
};
|
||||
allowMissingConnectorSecrets?: boolean;
|
||||
shouldIncrementRevision?: ShouldIncrementRevision<Params>;
|
||||
}
|
||||
|
||||
export async function update<Params extends RuleTypeParams = never>(
|
||||
context: RulesClientContext,
|
||||
{ id, data, allowMissingConnectorSecrets }: UpdateOptions<Params>
|
||||
{ id, data, allowMissingConnectorSecrets, shouldIncrementRevision }: UpdateOptions<Params>
|
||||
): Promise<PartialRule<Params>> {
|
||||
return await retryIfConflicts(
|
||||
context.logger,
|
||||
`rulesClient.update('${id}')`,
|
||||
async () => await updateWithOCC<Params>(context, { id, data, allowMissingConnectorSecrets })
|
||||
async () =>
|
||||
await updateWithOCC<Params>(context, {
|
||||
id,
|
||||
data,
|
||||
allowMissingConnectorSecrets,
|
||||
shouldIncrementRevision,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function updateWithOCC<Params extends RuleTypeParams>(
|
||||
context: RulesClientContext,
|
||||
{ id, data, allowMissingConnectorSecrets }: UpdateOptions<Params>
|
||||
{ id, data, allowMissingConnectorSecrets, shouldIncrementRevision }: UpdateOptions<Params>
|
||||
): Promise<PartialRule<Params>> {
|
||||
let alertSavedObject: SavedObject<RawRule>;
|
||||
|
||||
|
@ -108,7 +117,7 @@ async function updateWithOCC<Params extends RuleTypeParams>(
|
|||
|
||||
const updateResult = await updateAlert<Params>(
|
||||
context,
|
||||
{ id, data, allowMissingConnectorSecrets },
|
||||
{ id, data, allowMissingConnectorSecrets, shouldIncrementRevision },
|
||||
alertSavedObject
|
||||
);
|
||||
|
||||
|
@ -149,9 +158,15 @@ async function updateWithOCC<Params extends RuleTypeParams>(
|
|||
|
||||
async function updateAlert<Params extends RuleTypeParams>(
|
||||
context: RulesClientContext,
|
||||
{ id, data: initialData, allowMissingConnectorSecrets }: UpdateOptions<Params>,
|
||||
{ attributes, version }: SavedObject<RawRule>
|
||||
{
|
||||
id,
|
||||
data: initialData,
|
||||
allowMissingConnectorSecrets,
|
||||
shouldIncrementRevision = () => true,
|
||||
}: UpdateOptions<Params>,
|
||||
currentRule: SavedObject<RawRule>
|
||||
): Promise<PartialRule<Params>> {
|
||||
const { attributes, version } = currentRule;
|
||||
const data = { ...initialData, actions: addUuid(initialData.actions) };
|
||||
|
||||
const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId);
|
||||
|
@ -203,6 +218,15 @@ async function updateAlert<Params extends RuleTypeParams>(
|
|||
const apiKeyAttributes = apiKeyAsAlertAttributes(createdAPIKey, username);
|
||||
const notifyWhen = getRuleNotifyWhenType(data.notifyWhen ?? null, data.throttle ?? null);
|
||||
|
||||
// Increment revision if applicable field has changed
|
||||
const revision = shouldIncrementRevision(updatedParams)
|
||||
? incrementRevision<Params>(
|
||||
currentRule,
|
||||
{ id, data, allowMissingConnectorSecrets },
|
||||
updatedParams
|
||||
)
|
||||
: currentRule.attributes.revision;
|
||||
|
||||
let updatedObject: SavedObject<RawRule>;
|
||||
const createAttributes = updateMeta(context, {
|
||||
...attributes,
|
||||
|
@ -211,6 +235,7 @@ async function updateAlert<Params extends RuleTypeParams>(
|
|||
params: updatedParams as RawRule['params'],
|
||||
actions,
|
||||
notifyWhen,
|
||||
revision,
|
||||
updatedBy: username,
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
|
|
@ -66,6 +66,30 @@ const fieldsToExcludeFromPublicApi: Array<keyof SanitizedRule> = [
|
|||
'activeSnoozes',
|
||||
];
|
||||
|
||||
export const fieldsToExcludeFromRevisionUpdates: ReadonlySet<keyof RuleTypeParams> = new Set([
|
||||
'activeSnoozes',
|
||||
'alertTypeId',
|
||||
'apiKey',
|
||||
'apiKeyOwner',
|
||||
'consumer',
|
||||
'createdAt',
|
||||
'createdBy',
|
||||
'enabled',
|
||||
'executionStatus',
|
||||
'id',
|
||||
'isSnoozedUntil',
|
||||
'lastRun',
|
||||
'monitoring',
|
||||
'muteAll',
|
||||
'mutedInstanceIds',
|
||||
'nextRun',
|
||||
'revision',
|
||||
'running',
|
||||
'snoozeSchedule',
|
||||
'updatedBy',
|
||||
'updatedAt',
|
||||
]);
|
||||
|
||||
export class RulesClient {
|
||||
private readonly context: RulesClientContext;
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ describe('bulkEdit()', () => {
|
|||
notifyWhen: null,
|
||||
actions: [],
|
||||
name: 'my rule name',
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -220,6 +221,7 @@ describe('bulkEdit()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: null,
|
||||
actions: [],
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -251,6 +253,7 @@ describe('bulkEdit()', () => {
|
|||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
tags: ['foo', 'test-1'],
|
||||
revision: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
@ -275,6 +278,7 @@ describe('bulkEdit()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: null,
|
||||
actions: [],
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -302,6 +306,7 @@ describe('bulkEdit()', () => {
|
|||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
tags: [],
|
||||
revision: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
@ -326,6 +331,7 @@ describe('bulkEdit()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: null,
|
||||
actions: [],
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -354,6 +360,7 @@ describe('bulkEdit()', () => {
|
|||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
tags: ['test-1', 'test-2'],
|
||||
revision: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
@ -579,6 +586,7 @@ describe('bulkEdit()', () => {
|
|||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
tags: ['foo'],
|
||||
revision: 1,
|
||||
},
|
||||
references: [{ id: '1', name: 'action_0', type: 'action' }],
|
||||
},
|
||||
|
@ -592,6 +600,50 @@ describe('bulkEdit()', () => {
|
|||
snoozeSchedule: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('should only increment revision once for multiple operations', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
...existingRule,
|
||||
attributes: {
|
||||
...existingRule.attributes,
|
||||
revision: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const result = await rulesClient.bulkEdit({
|
||||
filter: '',
|
||||
operations: [
|
||||
{
|
||||
field: 'actions',
|
||||
operation: 'add',
|
||||
value: [
|
||||
{
|
||||
id: '687300e0-b882-11ed-ad70-c74a8cf8f386',
|
||||
group: 'default',
|
||||
params: {
|
||||
message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
field: 'throttle',
|
||||
operation: 'set',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
field: 'notifyWhen',
|
||||
operation: 'set',
|
||||
value: 'onActiveAlert',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(result.rules[0]).toHaveProperty('revision', 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('index pattern operations', () => {
|
||||
|
@ -628,6 +680,7 @@ describe('bulkEdit()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: null,
|
||||
actions: [],
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -665,6 +718,7 @@ describe('bulkEdit()', () => {
|
|||
params: expect.objectContaining({
|
||||
index: ['test-1', 'test-2', 'test-4', 'test-5'],
|
||||
}),
|
||||
revision: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
@ -691,6 +745,7 @@ describe('bulkEdit()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: null,
|
||||
actions: [],
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -723,6 +778,7 @@ describe('bulkEdit()', () => {
|
|||
params: expect.objectContaining({
|
||||
index: ['test-1'],
|
||||
}),
|
||||
revision: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
@ -819,6 +875,7 @@ describe('bulkEdit()', () => {
|
|||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
snoozeSchedule: [snoozePayload],
|
||||
revision: 0,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
@ -848,6 +905,7 @@ describe('bulkEdit()', () => {
|
|||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
revision: 0,
|
||||
snoozeSchedule: [snoozePayload],
|
||||
}),
|
||||
}),
|
||||
|
@ -893,6 +951,7 @@ describe('bulkEdit()', () => {
|
|||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
revision: 0,
|
||||
snoozeSchedule: [...existingSnooze, snoozePayload],
|
||||
}),
|
||||
}),
|
||||
|
@ -938,6 +997,7 @@ describe('bulkEdit()', () => {
|
|||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
muteAll: true,
|
||||
revision: 0,
|
||||
snoozeSchedule: [snoozePayload],
|
||||
}),
|
||||
}),
|
||||
|
@ -981,6 +1041,7 @@ describe('bulkEdit()', () => {
|
|||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
revision: 0,
|
||||
snoozeSchedule: [existingSnooze[1], existingSnooze[2]],
|
||||
}),
|
||||
}),
|
||||
|
@ -1025,6 +1086,7 @@ describe('bulkEdit()', () => {
|
|||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
revision: 0,
|
||||
snoozeSchedule: [],
|
||||
}),
|
||||
}),
|
||||
|
@ -1069,6 +1131,7 @@ describe('bulkEdit()', () => {
|
|||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: expect.objectContaining({
|
||||
revision: 0,
|
||||
snoozeSchedule: [existingSnooze[0]],
|
||||
}),
|
||||
}),
|
||||
|
@ -1181,7 +1244,7 @@ describe('bulkEdit()', () => {
|
|||
expect(createAPIKeyMock).not.toHaveBeenCalled();
|
||||
|
||||
// Explicitly bulk editing the apiKey will set the api key, even if the rule is disabled
|
||||
await rulesClient.bulkEdit({
|
||||
const result = await rulesClient.bulkEdit({
|
||||
filter: 'alert.attributes.tags: "APM"',
|
||||
operations: [
|
||||
{
|
||||
|
@ -1192,6 +1255,9 @@ describe('bulkEdit()', () => {
|
|||
});
|
||||
|
||||
expect(createAPIKeyMock).toHaveBeenCalled();
|
||||
|
||||
// Just API key updates do not result in an increment to revision
|
||||
expect(result.rules[0]).toHaveProperty('revision', 0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1211,7 +1277,7 @@ describe('bulkEdit()', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should succesfully update tags and index patterns and return updated rule', async () => {
|
||||
it('should successfully update tags and index patterns and return updated rule', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
|
@ -1230,6 +1296,7 @@ describe('bulkEdit()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: null,
|
||||
actions: [],
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -1270,6 +1337,7 @@ describe('bulkEdit()', () => {
|
|||
params: {
|
||||
index: ['index-1', 'index-2', 'index-3'],
|
||||
},
|
||||
revision: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
@ -1277,7 +1345,7 @@ describe('bulkEdit()', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should succesfully update rule if tags are updated but index patterns are not', async () => {
|
||||
it('should successfully update rule if tags are updated but index patterns are not', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
|
@ -1296,6 +1364,7 @@ describe('bulkEdit()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: null,
|
||||
actions: [],
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -1337,6 +1406,7 @@ describe('bulkEdit()', () => {
|
|||
params: {
|
||||
index: ['index-1', 'index-2'],
|
||||
},
|
||||
revision: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
@ -1344,7 +1414,7 @@ describe('bulkEdit()', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should succesfully update rule if index patterns are updated but tags are not', async () => {
|
||||
it('should successfully update rule if index patterns are updated but tags are not', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
|
@ -1363,6 +1433,7 @@ describe('bulkEdit()', () => {
|
|||
throttle: null,
|
||||
notifyWhen: null,
|
||||
actions: [],
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
version: '123',
|
||||
|
@ -1404,6 +1475,7 @@ describe('bulkEdit()', () => {
|
|||
params: {
|
||||
index: ['index-1', 'index-2', 'index-3'],
|
||||
},
|
||||
revision: 1,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
|
|
@ -457,6 +457,7 @@ describe('create()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 0,
|
||||
"running": false,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
|
@ -677,6 +678,7 @@ describe('create()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 0,
|
||||
"running": false,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
|
@ -1105,6 +1107,7 @@ describe('create()', () => {
|
|||
name: 'abc',
|
||||
notifyWhen: null,
|
||||
params: { bar: true },
|
||||
revision: 0,
|
||||
running: false,
|
||||
schedule: { interval: '1m' },
|
||||
tags: ['foo'],
|
||||
|
@ -1314,6 +1317,7 @@ describe('create()', () => {
|
|||
name: 'abc',
|
||||
notifyWhen: null,
|
||||
params: { bar: true, parameterThatIsSavedObjectRef: 'soRef_0' },
|
||||
revision: 0,
|
||||
running: false,
|
||||
schedule: { interval: '1m' },
|
||||
tags: ['foo'],
|
||||
|
@ -1493,6 +1497,7 @@ describe('create()', () => {
|
|||
name: 'abc',
|
||||
notifyWhen: null,
|
||||
params: { bar: true, parameterThatIsSavedObjectRef: 'action_0' },
|
||||
revision: 0,
|
||||
running: false,
|
||||
schedule: { interval: '1m' },
|
||||
tags: ['foo'],
|
||||
|
@ -1668,6 +1673,7 @@ describe('create()', () => {
|
|||
warning: null,
|
||||
},
|
||||
monitoring: getDefaultMonitoring('2019-02-12T21:01:22.479Z'),
|
||||
revision: 0,
|
||||
running: false,
|
||||
},
|
||||
{
|
||||
|
@ -1804,6 +1810,7 @@ describe('create()', () => {
|
|||
warning: null,
|
||||
},
|
||||
monitoring: getDefaultMonitoring('2019-02-12T21:01:22.479Z'),
|
||||
revision: 0,
|
||||
running: false,
|
||||
},
|
||||
{
|
||||
|
@ -1940,6 +1947,7 @@ describe('create()', () => {
|
|||
warning: null,
|
||||
},
|
||||
monitoring: getDefaultMonitoring('2019-02-12T21:01:22.479Z'),
|
||||
revision: 0,
|
||||
running: false,
|
||||
},
|
||||
{
|
||||
|
@ -2116,6 +2124,7 @@ describe('create()', () => {
|
|||
meta: {
|
||||
versionApiKeyLastmodified: 'v8.0.0',
|
||||
},
|
||||
revision: 0,
|
||||
running: false,
|
||||
},
|
||||
{
|
||||
|
@ -2470,6 +2479,7 @@ describe('create()', () => {
|
|||
warning: null,
|
||||
},
|
||||
monitoring: getDefaultMonitoring('2019-02-12T21:01:22.479Z'),
|
||||
revision: 0,
|
||||
running: false,
|
||||
},
|
||||
{
|
||||
|
@ -2575,6 +2585,7 @@ describe('create()', () => {
|
|||
warning: null,
|
||||
},
|
||||
monitoring: getDefaultMonitoring('2019-02-12T21:01:22.479Z'),
|
||||
revision: 0,
|
||||
running: false,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -90,6 +90,7 @@ const BaseRuleSavedObject: SavedObject<RawRule> = {
|
|||
error: null,
|
||||
warning: null,
|
||||
},
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
};
|
||||
|
|
|
@ -94,6 +94,7 @@ const BaseRuleSavedObject: SavedObject<RawRule> = {
|
|||
error: null,
|
||||
warning: null,
|
||||
},
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
};
|
||||
|
|
|
@ -91,6 +91,7 @@ const BaseRuleSavedObject: SavedObject<RawRule> = {
|
|||
error: null,
|
||||
warning: null,
|
||||
},
|
||||
revision: 0,
|
||||
},
|
||||
references: [],
|
||||
};
|
||||
|
|
|
@ -90,6 +90,7 @@ describe('update()', () => {
|
|||
alertTypeId: 'myType',
|
||||
schedule: { interval: '1m' },
|
||||
consumer: 'myApp',
|
||||
revision: 0,
|
||||
scheduledTaskId: 'task-123',
|
||||
params: {},
|
||||
actions: [
|
||||
|
@ -235,6 +236,7 @@ describe('update()', () => {
|
|||
},
|
||||
],
|
||||
notifyWhen: 'onActiveAlert',
|
||||
revision: 1,
|
||||
scheduledTaskId: 'task-123',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
|
@ -330,6 +332,7 @@ describe('update()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -395,6 +398,7 @@ describe('update()', () => {
|
|||
"risk_score": 40,
|
||||
"severity": "low",
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -527,6 +531,7 @@ describe('update()', () => {
|
|||
},
|
||||
],
|
||||
notifyWhen: 'onActiveAlert',
|
||||
revision: 1,
|
||||
scheduledTaskId: 'task-123',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
|
@ -623,6 +628,7 @@ describe('update()', () => {
|
|||
name: 'abc',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
params: { bar: true },
|
||||
revision: 1,
|
||||
schedule: { interval: '1m' },
|
||||
scheduledTaskId: 'task-123',
|
||||
tags: ['foo'],
|
||||
|
@ -673,6 +679,7 @@ describe('update()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -747,6 +754,7 @@ describe('update()', () => {
|
|||
},
|
||||
],
|
||||
notifyWhen: 'onActiveAlert',
|
||||
revision: 1,
|
||||
scheduledTaskId: 'task-123',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
|
@ -807,6 +815,7 @@ describe('update()', () => {
|
|||
name: 'abc',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
params: { bar: true, parameterThatIsSavedObjectRef: 'soRef_0' },
|
||||
revision: 1,
|
||||
schedule: { interval: '1m' },
|
||||
scheduledTaskId: 'task-123',
|
||||
tags: ['foo'],
|
||||
|
@ -852,6 +861,7 @@ describe('update()', () => {
|
|||
"bar": true,
|
||||
"parameterThatIsSavedObjectId": "9",
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -889,6 +899,7 @@ describe('update()', () => {
|
|||
},
|
||||
],
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
revision: 1,
|
||||
scheduledTaskId: 'task-123',
|
||||
},
|
||||
updated_at: new Date().toISOString(),
|
||||
|
@ -942,6 +953,7 @@ describe('update()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -986,6 +998,7 @@ describe('update()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -1044,6 +1057,7 @@ describe('update()', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
revision: 1,
|
||||
scheduledTaskId: 'task-123',
|
||||
apiKey: null,
|
||||
},
|
||||
|
@ -1099,6 +1113,7 @@ describe('update()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -1135,6 +1150,7 @@ describe('update()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -2079,6 +2095,7 @@ describe('update()', () => {
|
|||
},
|
||||
],
|
||||
notifyWhen: 'onActiveAlert',
|
||||
revision: 1,
|
||||
scheduledTaskId: 'task-123',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
|
@ -2144,6 +2161,7 @@ describe('update()', () => {
|
|||
name: 'abc',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
params: { bar: true },
|
||||
revision: 1,
|
||||
schedule: { interval: '1m' },
|
||||
scheduledTaskId: 'task-123',
|
||||
tags: ['foo'],
|
||||
|
@ -2178,6 +2196,7 @@ describe('update()', () => {
|
|||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"revision": 1,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -2690,6 +2709,7 @@ describe('update()', () => {
|
|||
name: 'abc',
|
||||
notifyWhen: null,
|
||||
params: { bar: true, risk_score: 40, severity: 'low' },
|
||||
revision: 1,
|
||||
schedule: { interval: '1m' },
|
||||
scheduledTaskId: 'task-123',
|
||||
tags: ['foo'],
|
||||
|
|
|
@ -64,6 +64,7 @@ describe('updateApiKey()', () => {
|
|||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: {
|
||||
revision: 0,
|
||||
schedule: { interval: '10s' },
|
||||
alertTypeId: 'myType',
|
||||
consumer: 'myApp',
|
||||
|
@ -117,6 +118,7 @@ describe('updateApiKey()', () => {
|
|||
enabled: true,
|
||||
apiKey: Buffer.from('234:abc').toString('base64'),
|
||||
apiKeyOwner: 'elastic',
|
||||
revision: 0,
|
||||
updatedBy: 'elastic',
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
actions: [
|
||||
|
@ -177,6 +179,7 @@ describe('updateApiKey()', () => {
|
|||
enabled: true,
|
||||
apiKey: Buffer.from('234:abc').toString('base64'),
|
||||
apiKeyOwner: 'elastic',
|
||||
revision: 0,
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
actions: [
|
||||
|
|
|
@ -40,6 +40,7 @@ export const AlertAttributesExcludedFromAAD = [
|
|||
'isSnoozedUntil',
|
||||
'lastRun',
|
||||
'nextRun',
|
||||
'revision',
|
||||
'running',
|
||||
];
|
||||
|
||||
|
@ -60,6 +61,7 @@ export type AlertAttributesExcludedFromAADType =
|
|||
| 'isSnoozedUntil'
|
||||
| 'lastRun'
|
||||
| 'nextRun'
|
||||
| 'revision'
|
||||
| 'running';
|
||||
|
||||
export function setupSavedObjects(
|
||||
|
|
|
@ -198,6 +198,10 @@ export const alertMappings: SavedObjectsTypeMappingDefinition = {
|
|||
},
|
||||
},
|
||||
},
|
||||
revision: {
|
||||
index: true, // Explicitly setting to `true` as there is need to query for a rule by a specific revision
|
||||
type: 'long',
|
||||
},
|
||||
snoozeSchedule: {
|
||||
type: 'nested',
|
||||
properties: {
|
||||
|
|
|
@ -11,6 +11,16 @@ import { v4 as uuidv4 } from 'uuid';
|
|||
import { createEsoMigration, pipeMigrations } from '../utils';
|
||||
import { RawRule } from '../../../types';
|
||||
|
||||
function addRevision(doc: SavedObjectUnsanitizedDoc<RawRule>): SavedObjectUnsanitizedDoc<RawRule> {
|
||||
return {
|
||||
...doc,
|
||||
attributes: {
|
||||
...doc.attributes,
|
||||
revision: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function addActionUuid(
|
||||
doc: SavedObjectUnsanitizedDoc<RawRule>
|
||||
): SavedObjectUnsanitizedDoc<RawRule> {
|
||||
|
@ -36,5 +46,5 @@ export const getMigrations880 = (encryptedSavedObjects: EncryptedSavedObjectsPlu
|
|||
createEsoMigration(
|
||||
encryptedSavedObjects,
|
||||
(doc: SavedObjectUnsanitizedDoc<RawRule>): doc is SavedObjectUnsanitizedDoc<RawRule> => true,
|
||||
pipeMigrations(addActionUuid)
|
||||
pipeMigrations(addActionUuid, addRevision)
|
||||
);
|
||||
|
|
|
@ -2652,6 +2652,14 @@ describe('successful migrations', () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('migrates rule to include revision and defaults revision to 0', () => {
|
||||
const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.8.0'];
|
||||
|
||||
const rule = getMockData();
|
||||
const migratedAlert880 = migration880(rule, migrationContext);
|
||||
expect(migratedAlert880.attributes.revision).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Metrics Inventory Threshold rule', () => {
|
||||
|
|
|
@ -37,6 +37,7 @@ const alert: SanitizedRule<{
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
describe('Alert Task Instance', () => {
|
||||
|
|
|
@ -210,6 +210,7 @@ export const mockedRuleTypeSavedObject: Rule<RuleTypeParams> = {
|
|||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
monitoring: getDefaultMonitoring('2020-08-20T19:23:38Z'),
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
export const mockTaskInstance = () => ({
|
||||
|
|
|
@ -350,6 +350,7 @@ export interface RawRule extends SavedObjectAttributes {
|
|||
isSnoozedUntil?: string | null;
|
||||
lastRun?: RawRuleLastRun | null;
|
||||
nextRun?: string | null;
|
||||
revision: number;
|
||||
running?: boolean | null;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,6 +87,7 @@ Array [
|
|||
"name": "monitoring_alert_cpu_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -250,6 +251,7 @@ Array [
|
|||
"name": "monitoring_alert_jvm_memory_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -310,6 +312,7 @@ Array [
|
|||
"name": "monitoring_alert_jvm_memory_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -602,6 +605,7 @@ Array [
|
|||
"name": "monitoring_alert_nodes_changed_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -662,6 +666,7 @@ Array [
|
|||
"name": "monitoring_alert_nodes_changed_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -722,6 +727,7 @@ Array [
|
|||
"name": "monitoring_alert_disk_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -782,6 +788,7 @@ Array [
|
|||
"name": "monitoring_alert_license_expiration_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -842,6 +849,7 @@ Array [
|
|||
"name": "monitoring_alert_license_expiration_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -931,6 +939,7 @@ Array [
|
|||
"name": "monitoring_alert_jvm_memory_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -1032,6 +1041,7 @@ Array [
|
|||
"name": "monitoring_alert_nodes_changed_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -1068,6 +1078,7 @@ Array [
|
|||
"name": "monitoring_alert_disk_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -1104,6 +1115,7 @@ Array [
|
|||
"name": "monitoring_alert_license_expiration_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -1205,6 +1217,7 @@ Array [
|
|||
"name": "monitoring_alert_logstash_version_mismatch_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -1241,6 +1254,7 @@ Array [
|
|||
"name": "monitoring_alert_cpu_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -1277,6 +1291,7 @@ Array [
|
|||
"name": "monitoring_alert_thread_pool_write_rejections_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
|
|
@ -114,6 +114,7 @@ Array [
|
|||
"name": "monitoring_alert_jvm_memory_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -174,6 +175,7 @@ Array [
|
|||
"name": "monitoring_alert_jvm_memory_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -407,6 +409,7 @@ Array [
|
|||
"name": "monitoring_alert_nodes_changed_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -467,6 +470,7 @@ Array [
|
|||
"name": "monitoring_alert_disk_usage_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -527,6 +531,7 @@ Array [
|
|||
"name": "monitoring_alert_license_expiration_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -587,6 +592,7 @@ Array [
|
|||
"name": "monitoring_alert_nodes_changed_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -647,6 +653,7 @@ Array [
|
|||
"name": "monitoring_alert_license_expiration_label",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
@ -707,6 +714,7 @@ Array [
|
|||
"name": "monitoring_alert_nodes_changed_label_2",
|
||||
"notifyWhen": null,
|
||||
"params": Object {},
|
||||
"revision": 0,
|
||||
"schedule": Object {
|
||||
"interval": "1m",
|
||||
},
|
||||
|
|
|
@ -58,6 +58,7 @@ const mockAlert = {
|
|||
lastExecutionDate: new Date('2020-12-08'),
|
||||
},
|
||||
notifyWhen: null,
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
describe('getAlertPanelsByCategory', () => {
|
||||
|
|
|
@ -57,6 +57,7 @@ const mockAlert = {
|
|||
lastExecutionDate: new Date('2020-12-08'),
|
||||
},
|
||||
notifyWhen: null,
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
describe('getAlertPanelsByNode', () => {
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
BaseCreateProps,
|
||||
TypeSpecificCreateProps,
|
||||
} from '../../../rule_schema';
|
||||
import { created_at, updated_at, created_by, updated_by } from '../../../schemas/common';
|
||||
import { created_at, updated_at, created_by, updated_by, revision } from '../../../schemas/common';
|
||||
|
||||
/**
|
||||
* Differences from this and the createRulesSchema are
|
||||
|
@ -44,6 +44,7 @@ export const RuleToImport = t.intersection([
|
|||
created_by,
|
||||
related_integrations: RelatedIntegrationArray,
|
||||
required_fields: RequiredFieldArray,
|
||||
revision,
|
||||
setup: SetupGuide,
|
||||
})
|
||||
),
|
||||
|
|
|
@ -31,6 +31,7 @@ const getResponseBaseParams = (anchorDate: string = ANCHOR_DATE): SharedResponse
|
|||
immutable: false,
|
||||
name: 'Query with a rule id',
|
||||
references: ['test 1', 'test 2'],
|
||||
revision: 0,
|
||||
severity: 'high' as const,
|
||||
severity_mapping: [],
|
||||
updated_by: 'elastic_kibana',
|
||||
|
|
|
@ -36,6 +36,7 @@ import {
|
|||
updated_by,
|
||||
created_at,
|
||||
created_by,
|
||||
revision,
|
||||
} from '../../schemas/common';
|
||||
|
||||
import {
|
||||
|
@ -154,6 +155,7 @@ const responseRequiredFields = {
|
|||
updated_by,
|
||||
created_at,
|
||||
created_by,
|
||||
revision,
|
||||
|
||||
// NOTE: For now, Related Integrations, Required Fields and Setup Guide are supported for prebuilt
|
||||
// rules only. We don't want to allow users to edit these 3 fields via the API. If we added them
|
||||
|
|
|
@ -59,6 +59,7 @@ export const status_code = PositiveInteger;
|
|||
export const message = t.string;
|
||||
export const perPage = PositiveInteger;
|
||||
export const total = PositiveInteger;
|
||||
export const revision = PositiveInteger;
|
||||
export const success = t.boolean;
|
||||
export const success_count = PositiveInteger;
|
||||
|
||||
|
|
|
@ -499,6 +499,7 @@ export const expectedExportedRule = (ruleResponse: Cypress.Response<RuleResponse
|
|||
tags,
|
||||
interval,
|
||||
enabled,
|
||||
revision: 0,
|
||||
description,
|
||||
risk_score: riskScore,
|
||||
severity,
|
||||
|
|
|
@ -375,6 +375,7 @@ export const getRuleMock = <T extends RuleParams>(params: T): SanitizedRule<T> =
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
});
|
||||
|
||||
export const resolveRuleMock = <T extends RuleParams>(params: T): ResolvedSanitizedRule<T> => ({
|
||||
|
@ -552,6 +553,7 @@ export const legacyGetNotificationResult = ({
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -600,6 +602,7 @@ export const legacyGetHourlyNotificationResult = (
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -648,6 +651,7 @@ export const legacyGetDailyNotificationResult = (
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -696,6 +700,7 @@ export const legacyGetWeeklyNotificationResult = (
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
@ -86,6 +86,7 @@ export const getOutputRuleAlertForRest = (): RuleResponse => ({
|
|||
type: 'query',
|
||||
note: '# Investigative notes',
|
||||
version: 1,
|
||||
revision: 0,
|
||||
execution_summary: undefined,
|
||||
related_integrations: [],
|
||||
required_fields: [],
|
||||
|
|
|
@ -544,6 +544,7 @@ export const performBulkActionRoute = (
|
|||
exceptionsList: exceptions,
|
||||
},
|
||||
},
|
||||
shouldIncrementRevision: () => false,
|
||||
});
|
||||
|
||||
// TODO: figureout why types can't return just updatedRule
|
||||
|
|
|
@ -76,6 +76,7 @@ describe('duplicateRule', () => {
|
|||
mutedInstanceIds: [],
|
||||
updatedAt: new Date(2021, 0),
|
||||
createdAt: new Date(2021, 0),
|
||||
revision: 0,
|
||||
scheduledTaskId: undefined,
|
||||
executionStatus: {
|
||||
lastExecutionDate: new Date(2021, 0),
|
||||
|
|
|
@ -17,6 +17,8 @@ export interface PatchRulesOptions {
|
|||
nextParams: PatchRuleRequestBody;
|
||||
existingRule: RuleAlertType | null | undefined;
|
||||
allowMissingConnectorSecrets?: boolean;
|
||||
|
||||
shouldIncrementRevision?: boolean;
|
||||
}
|
||||
|
||||
export const patchRules = async ({
|
||||
|
@ -24,6 +26,7 @@ export const patchRules = async ({
|
|||
existingRule,
|
||||
nextParams,
|
||||
allowMissingConnectorSecrets,
|
||||
shouldIncrementRevision = true,
|
||||
}: PatchRulesOptions): Promise<PartialRule<RuleParams> | null> => {
|
||||
if (existingRule == null) {
|
||||
return null;
|
||||
|
@ -35,6 +38,7 @@ export const patchRules = async ({
|
|||
id: existingRule.id,
|
||||
data: patchedRule,
|
||||
allowMissingConnectorSecrets,
|
||||
shouldIncrementRevision: () => shouldIncrementRevision,
|
||||
});
|
||||
|
||||
if (nextParams.throttle !== undefined) {
|
||||
|
|
|
@ -93,6 +93,7 @@ describe('getExportAll', () => {
|
|||
references: ['http://example.com', 'https://example.com'],
|
||||
related_integrations: [],
|
||||
required_fields: [],
|
||||
revision: 0,
|
||||
setup: '',
|
||||
timeline_id: 'some-timeline-id',
|
||||
timeline_title: 'some-timeline-title',
|
||||
|
@ -287,6 +288,7 @@ describe('getExportAll', () => {
|
|||
throttle: 'rule',
|
||||
note: '# Investigative notes',
|
||||
version: 1,
|
||||
revision: 0,
|
||||
exceptions_list: getListArrayMock(),
|
||||
});
|
||||
expect(detailsJson).toEqual({
|
||||
|
|
|
@ -91,6 +91,7 @@ describe('get_export_by_object_ids', () => {
|
|||
references: ['http://example.com', 'https://example.com'],
|
||||
related_integrations: [],
|
||||
required_fields: [],
|
||||
revision: 0,
|
||||
setup: '',
|
||||
timeline_id: 'some-timeline-id',
|
||||
timeline_title: 'some-timeline-title',
|
||||
|
@ -297,6 +298,7 @@ describe('get_export_by_object_ids', () => {
|
|||
throttle: 'rule',
|
||||
note: '# Investigative notes',
|
||||
version: 1,
|
||||
revision: 0,
|
||||
exceptions_list: getListArrayMock(),
|
||||
});
|
||||
expect(detailsJson).toEqual({
|
||||
|
@ -399,6 +401,7 @@ describe('get_export_by_object_ids', () => {
|
|||
throttle: 'no_actions',
|
||||
note: '# Investigative notes',
|
||||
version: 1,
|
||||
revision: 0,
|
||||
exceptions_list: getListArrayMock(),
|
||||
execution_summary: undefined,
|
||||
outcome: undefined,
|
||||
|
|
|
@ -141,6 +141,7 @@ export const importRules = async ({
|
|||
exceptions_list: [...exceptions],
|
||||
},
|
||||
allowMissingConnectorSecrets,
|
||||
shouldIncrementRevision: false,
|
||||
});
|
||||
resolve({
|
||||
rule_id: parsedRule.rule_id,
|
||||
|
|
|
@ -692,6 +692,7 @@ export const internalRuleToAPIResponse = (
|
|||
tags: rule.tags,
|
||||
interval: rule.schedule.interval,
|
||||
enabled: rule.enabled,
|
||||
revision: rule.revision,
|
||||
// Security solution shared rule params
|
||||
...commonParamsCamelToSnake(rule.params),
|
||||
// Type specific security solution rule params
|
||||
|
|
|
@ -46,6 +46,7 @@ export const ruleOutput = (): RuleResponse => ({
|
|||
throttle: 'no_actions',
|
||||
threat: getThreatMock(),
|
||||
version: 1,
|
||||
revision: 0,
|
||||
filters: [
|
||||
{
|
||||
query: {
|
||||
|
|
|
@ -497,6 +497,7 @@ export const sampleSignalHit = (): SignalHit => ({
|
|||
related_integrations: [],
|
||||
required_fields: [],
|
||||
response_actions: undefined,
|
||||
revision: 0,
|
||||
setup: '',
|
||||
throttle: 'no_actions',
|
||||
actions: [],
|
||||
|
|
|
@ -20,7 +20,8 @@ export * from './detail_flyout';
|
|||
export * from './alert_rules/default_status_alert.journey';
|
||||
export * from './test_now_mode.journey';
|
||||
export * from './data_retention.journey';
|
||||
export * from './monitor_details_page/monitor_summary.journey';
|
||||
// Additional flake skip along with https://github.com/elastic/kibana/pull/151936
|
||||
// export * from './monitor_details_page/monitor_summary.journey';
|
||||
export * from './test_run_details.journey';
|
||||
export * from './step_details.journey';
|
||||
export * from './project_monitor_read_only.journey';
|
||||
|
|
|
@ -63,6 +63,7 @@ type NewMonitorStatusAlert = Omit<
|
|||
| 'muteAll'
|
||||
| 'mutedInstanceIds'
|
||||
| 'executionStatus'
|
||||
| 'revision'
|
||||
| 'ruleTypeId'
|
||||
| 'notifyWhen'
|
||||
| 'actions'
|
||||
|
|
|
@ -95,6 +95,7 @@ describe('createRule', () => {
|
|||
createdAt: new Date('2021-04-01T21:33:13.247Z'),
|
||||
updatedAt: new Date('2021-04-01T21:33:13.247Z'),
|
||||
apiKeyOwner: '',
|
||||
revision: 0,
|
||||
};
|
||||
http.post.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ describe('updateRule', () => {
|
|||
updatedAt: new Date('1970-01-01T00:00:00.000Z'),
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
revision: 0,
|
||||
};
|
||||
const resolvedValue: Rule = {
|
||||
...ruleToUpdate,
|
||||
|
@ -40,6 +41,7 @@ describe('updateRule', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 1,
|
||||
};
|
||||
http.put.mockResolvedValueOnce(resolvedValue);
|
||||
|
||||
|
|
|
@ -275,6 +275,7 @@ describe('getRuleWithInvalidatedFields', () => {
|
|||
throttle: '',
|
||||
updatedAt: new Date(),
|
||||
updatedBy: '',
|
||||
revision: 0,
|
||||
};
|
||||
const baseAlertErrors = {};
|
||||
const actionsErrors: IErrorObject[] = [];
|
||||
|
@ -313,6 +314,7 @@ describe('getRuleWithInvalidatedFields', () => {
|
|||
throttle: '',
|
||||
updatedAt: new Date(),
|
||||
updatedBy: '',
|
||||
revision: 0,
|
||||
};
|
||||
const baseAlertErrors = {};
|
||||
const actionsErrors: IErrorObject[] = [];
|
||||
|
@ -363,6 +365,7 @@ describe('getRuleWithInvalidatedFields', () => {
|
|||
throttle: '',
|
||||
updatedAt: new Date(),
|
||||
updatedBy: '',
|
||||
revision: 0,
|
||||
};
|
||||
const baseAlertErrors = {};
|
||||
const actionsErrors = [{ 'incident.field.name': ['Name is required.'] }];
|
||||
|
@ -422,6 +425,7 @@ describe('getRuleWithInvalidatedFields', () => {
|
|||
throttle: '',
|
||||
updatedAt: new Date(),
|
||||
updatedBy: '',
|
||||
revision: 0,
|
||||
};
|
||||
const baseAlertErrors = {};
|
||||
const actionsErrors = [
|
||||
|
|
|
@ -385,6 +385,7 @@ function mockRule(overloads: Partial<Rule> = {}): Rule {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...overloads,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ describe('rule_actions_popover', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...overloads,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -215,6 +215,7 @@ function mockRule(overwrite = {}): Rule {
|
|||
updatedAt: new Date(),
|
||||
consumer: 'alerts',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
revision: 0,
|
||||
executionStatus: {
|
||||
status: 'active',
|
||||
lastDuration: 500,
|
||||
|
|
|
@ -818,6 +818,7 @@ describe('rule_details', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...overloads,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -494,6 +494,7 @@ function mockRule(overloads: Partial<Rule> = {}): Rule {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...overloads,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ const mockRule: Rule = {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
const loadActionErrorLogMock = jest.fn();
|
||||
|
|
|
@ -125,6 +125,7 @@ function mockRule(overloads: Partial<Rule> = {}): Rule {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...overloads,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ export function mockRule(overloads: Partial<Rule> = {}): Rule {
|
|||
notifyWhen: null,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
revision: 0,
|
||||
executionStatus: {
|
||||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
|
|
|
@ -93,6 +93,7 @@ function mockRule(overloads: Partial<Rule> = {}): Rule {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...overloads,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -397,6 +397,7 @@ function mockRule(overloads: Partial<Rule> = {}): Rule {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...overloads,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -174,6 +174,7 @@ describe('rule_edit', () => {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...initialRuleFields,
|
||||
};
|
||||
actionTypeRegistry.get.mockReturnValueOnce(actionTypeModel);
|
||||
|
|
|
@ -266,6 +266,7 @@ function mockRule(overloads: Partial<Rule> = {}): Rule {
|
|||
status: 'unknown',
|
||||
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
|
||||
},
|
||||
revision: 0,
|
||||
...overloads,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ describe('CollapsedItemActions', () => {
|
|||
ruleType: 'Test Rule Type',
|
||||
isEditable: true,
|
||||
enabledInLicense: true,
|
||||
revision: 0,
|
||||
...overrides,
|
||||
};
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ const getRule = (overrides = {}): RuleTableItem => ({
|
|||
ruleType: 'Test Rule Type',
|
||||
isEditable: true,
|
||||
enabledInLicense: true,
|
||||
revision: 0,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ describe('RuleEnabledSwitch', () => {
|
|||
notifyWhen: null,
|
||||
index: 0,
|
||||
updatedAt: new Date('2020-08-20T19:23:38Z'),
|
||||
revision: 0,
|
||||
},
|
||||
onRuleChanged: jest.fn(),
|
||||
};
|
||||
|
@ -86,6 +87,7 @@ describe('RuleEnabledSwitch', () => {
|
|||
notifyWhen: null,
|
||||
index: 0,
|
||||
updatedAt: new Date('2020-08-20T19:23:38Z'),
|
||||
revision: 0,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -126,6 +126,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
execution_status: response.body.execution_status,
|
||||
revision: 0,
|
||||
...(response.body.next_run ? { next_run: response.body.next_run } : {}),
|
||||
...(response.body.last_run ? { last_run: response.body.last_run } : {}),
|
||||
});
|
||||
|
|
|
@ -123,6 +123,7 @@ const findTestUtils = (
|
|||
api_key_owner: 'elastic',
|
||||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
revision: 0,
|
||||
execution_status: match.execution_status,
|
||||
...(match.next_run ? { next_run: match.next_run } : {}),
|
||||
...(match.last_run ? { last_run: match.last_run } : {}),
|
||||
|
@ -335,6 +336,7 @@ const findTestUtils = (
|
|||
created_at: match.created_at,
|
||||
updated_at: match.updated_at,
|
||||
execution_status: match.execution_status,
|
||||
revision: 0,
|
||||
...(match.next_run ? { next_run: match.next_run } : {}),
|
||||
...(match.last_run ? { last_run: match.last_run } : {}),
|
||||
...(describeType === 'internal'
|
||||
|
|
|
@ -82,6 +82,7 @@ const getTestUtils = (
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
execution_status: response.body.execution_status,
|
||||
revision: 0,
|
||||
...(response.body.next_run ? { next_run: response.body.next_run } : {}),
|
||||
...(response.body.last_run ? { last_run: response.body.last_run } : {}),
|
||||
...(describeType === 'internal'
|
||||
|
|
|
@ -134,6 +134,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
revision: 1,
|
||||
...(response.body.next_run ? { next_run: response.body.next_run } : {}),
|
||||
...(response.body.last_run ? { last_run: response.body.last_run } : {}),
|
||||
});
|
||||
|
@ -224,6 +225,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
revision: 1,
|
||||
...(response.body.next_run ? { next_run: response.body.next_run } : {}),
|
||||
...(response.body.last_run ? { last_run: response.body.last_run } : {}),
|
||||
});
|
||||
|
@ -325,6 +327,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
revision: 1,
|
||||
...(response.body.next_run ? { next_run: response.body.next_run } : {}),
|
||||
...(response.body.last_run ? { last_run: response.body.last_run } : {}),
|
||||
});
|
||||
|
@ -426,6 +429,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
revision: 1,
|
||||
...(response.body.next_run ? { next_run: response.body.next_run } : {}),
|
||||
...(response.body.last_run ? { last_run: response.body.last_run } : {}),
|
||||
});
|
||||
|
@ -525,6 +529,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
revision: 1,
|
||||
...(response.body.next_run ? { next_run: response.body.next_run } : {}),
|
||||
...(response.body.last_run ? { last_run: response.body.last_run } : {}),
|
||||
});
|
||||
|
|
|
@ -98,6 +98,8 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.api_key_owner).to.eql(user.username);
|
||||
// Ensure revision is not incremented when API key is updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -152,6 +154,8 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.api_key_owner).to.eql(user.username);
|
||||
// Ensure revision is not incremented when API key is updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -217,6 +221,8 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.api_key_owner).to.eql(user.username);
|
||||
// Ensure revision is not incremented when API key is updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -282,6 +288,8 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.api_key_owner).to.eql(user.username);
|
||||
// Ensure revision is not incremented when API key is updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -346,6 +354,8 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte
|
|||
.auth(user.username, user.password)
|
||||
.expect(200);
|
||||
expect(updatedAlert.api_key_owner).to.eql(user.username);
|
||||
// Ensure revision is not incremented when API key is updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
|
|
@ -35,6 +35,7 @@ const getDefaultRules = (response: any) => ({
|
|||
scheduledTaskId: response.body.rules[0].scheduledTaskId,
|
||||
executionStatus: response.body.rules[0].executionStatus,
|
||||
monitoring: response.body.rules[0].monitoring,
|
||||
revision: 0,
|
||||
...(response.body.rules[0].nextRun ? { nextRun: response.body.rules[0].nextRun } : {}),
|
||||
...(response.body.rules[0].lastRun ? { lastRun: response.body.rules[0].lastRun } : {}),
|
||||
});
|
||||
|
@ -67,6 +68,7 @@ const getThreeRules = (response: any) => {
|
|||
scheduledTaskId: response.body.rules[i].scheduledTaskId,
|
||||
executionStatus: response.body.rules[i].executionStatus,
|
||||
monitoring: response.body.rules[i].monitoring,
|
||||
revision: 0,
|
||||
...(response.body.rules[i].nextRun ? { nextRun: response.body.rules[i].nextRun } : {}),
|
||||
...(response.body.rules[i].lastRun ? { lastRun: response.body.rules[i].lastRun } : {}),
|
||||
});
|
||||
|
|
|
@ -34,6 +34,7 @@ const getDefaultRules = (response: any) => ({
|
|||
scheduledTaskId: response.body.rules[0].scheduledTaskId,
|
||||
executionStatus: response.body.rules[0].executionStatus,
|
||||
monitoring: response.body.rules[0].monitoring,
|
||||
revision: 0,
|
||||
...(response.body.rules[0].nextRun ? { nextRun: response.body.rules[0].nextRun } : {}),
|
||||
...(response.body.rules[0].lastRun ? { lastRun: response.body.rules[0].lastRun } : {}),
|
||||
});
|
||||
|
|
|
@ -95,6 +95,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
snoozeSchedule: [],
|
||||
updatedAt: response.body.rules[0].updatedAt,
|
||||
createdAt: response.body.rules[0].createdAt,
|
||||
revision: 0,
|
||||
scheduledTaskId: response.body.rules[0].scheduledTaskId,
|
||||
executionStatus: {
|
||||
lastDuration: 0,
|
||||
|
|
|
@ -165,6 +165,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
execution_status: response.body.execution_status,
|
||||
revision: 0,
|
||||
last_run: {
|
||||
alerts_count: {
|
||||
active: 0,
|
||||
|
|
|
@ -81,6 +81,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
],
|
||||
enabled: true,
|
||||
rule_type_id: 'test.noop',
|
||||
revision: 0,
|
||||
running: false,
|
||||
consumer: 'alertsFixture',
|
||||
params: {},
|
||||
|
@ -184,6 +185,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
],
|
||||
enabled: true,
|
||||
rule_type_id: 'test.noop',
|
||||
revision: 0,
|
||||
running: false,
|
||||
consumer: 'alertsFixture',
|
||||
params: {},
|
||||
|
@ -503,6 +505,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
createdAt: response.body.createdAt,
|
||||
updatedAt: response.body.updatedAt,
|
||||
executionStatus: response.body.executionStatus,
|
||||
revision: 0,
|
||||
running: false,
|
||||
...(response.body.next_run ? { next_run: response.body.next_run } : {}),
|
||||
...(response.body.last_run ? { last_run: response.body.last_run } : {}),
|
||||
|
|
|
@ -63,6 +63,14 @@ export default function createDisableRuleTests({ getService }: FtrProviderContex
|
|||
expect(taskRecord.task.enabled).to.eql(false);
|
||||
});
|
||||
|
||||
const { body: disabledRule } = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdRule.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
|
||||
// Ensure revision was not updated
|
||||
expect(disabledRule.revision).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest: supertestWithoutAuth,
|
||||
|
@ -173,6 +181,14 @@ export default function createDisableRuleTests({ getService }: FtrProviderContex
|
|||
});
|
||||
await ruleUtils.disable(createdRule.id);
|
||||
|
||||
const { body: disabledRule } = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdRule.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
|
||||
// Ensure revision was not updated
|
||||
expect(disabledRule.revision).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest: supertestWithoutAuth,
|
||||
|
@ -209,6 +225,14 @@ export default function createDisableRuleTests({ getService }: FtrProviderContex
|
|||
expect(taskRecord.task.enabled).to.eql(false);
|
||||
});
|
||||
|
||||
const { body: disabledRule } = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdRule.id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
|
||||
// Ensure revision was not updated
|
||||
expect(disabledRule.revision).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest: supertestWithoutAuth,
|
||||
|
|
|
@ -61,6 +61,9 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex
|
|||
});
|
||||
expect(taskRecord.task.enabled).to.eql(true);
|
||||
|
||||
// Ensure revision was not updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest: supertestWithoutAuth,
|
||||
|
@ -114,6 +117,9 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex
|
|||
});
|
||||
expect(taskRecord.task.enabled).to.eql(true);
|
||||
|
||||
// Ensure revision was not updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest: supertestWithoutAuth,
|
||||
|
|
|
@ -87,6 +87,7 @@ const findTestUtils = (
|
|||
name: 'abc',
|
||||
tags: ['foo'],
|
||||
rule_type_id: 'test.noop',
|
||||
revision: 0,
|
||||
running: false,
|
||||
consumer: 'alertsFixture',
|
||||
schedule: { interval: '1m' },
|
||||
|
@ -369,6 +370,7 @@ export default function createFindTests({ getService }: FtrProviderContext) {
|
|||
createdAt: match.createdAt,
|
||||
updatedAt: match.updatedAt,
|
||||
executionStatus: match.executionStatus,
|
||||
revision: 0,
|
||||
running: false,
|
||||
...(match.nextRun ? { nextRun: match.nextRun } : {}),
|
||||
...(match.lastRun ? { lastRun: match.lastRun } : {}),
|
||||
|
|
|
@ -38,6 +38,7 @@ const getTestUtils = (
|
|||
name: 'abc',
|
||||
tags: ['foo'],
|
||||
rule_type_id: 'test.noop',
|
||||
revision: 0,
|
||||
running: false,
|
||||
consumer: 'alertsFixture',
|
||||
schedule: { interval: '1m' },
|
||||
|
@ -155,6 +156,7 @@ export default function createGetTests({ getService }: FtrProviderContext) {
|
|||
createdAt: response.body.createdAt,
|
||||
updatedAt: response.body.updatedAt,
|
||||
executionStatus: response.body.executionStatus,
|
||||
revision: 0,
|
||||
running: false,
|
||||
...(response.body.nextRun ? { nextRun: response.body.nextRun } : {}),
|
||||
...(response.body.lastRun ? { lastRun: response.body.lastRun } : {}),
|
||||
|
|
|
@ -60,6 +60,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
notify_when: 'onThrottleInterval',
|
||||
revision: 1,
|
||||
scheduled_task_id: createdAlert.scheduled_task_id,
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
|
@ -169,6 +170,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
createdAt: response.body.createdAt,
|
||||
updatedAt: response.body.updatedAt,
|
||||
executionStatus: response.body.executionStatus,
|
||||
revision: 1,
|
||||
running: false,
|
||||
...(response.body.nextRun ? { nextRun: response.body.nextRun } : {}),
|
||||
...(response.body.lastRun ? { lastRun: response.body.lastRun } : {}),
|
||||
|
|
|
@ -46,6 +46,9 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte
|
|||
.expect(200);
|
||||
expect(updatedAlert.api_key_owner).to.eql(null);
|
||||
|
||||
// Ensure revision is not incremented when API key is updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest: supertestWithoutAuth,
|
||||
|
@ -92,6 +95,9 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte
|
|||
.expect(200);
|
||||
expect(updatedAlert.api_key_owner).to.eql(null);
|
||||
|
||||
// Ensure revision is not incremented when API key is updated
|
||||
expect(updatedAlert.revision).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest: supertestWithoutAuth,
|
||||
|
|
|
@ -76,6 +76,9 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
|
||||
expect(updatedRule.tags).to.eql(['default', 'tag-1']);
|
||||
|
||||
// Ensure revision is updated
|
||||
expect(updatedRule.revision).to.eql(1);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -137,6 +140,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
|
||||
updatedRules.forEach((rule) => {
|
||||
expect(rule.tags).to.eql([`rewritten`]);
|
||||
expect(rule.revision).to.eql(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -174,6 +178,9 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
|
||||
expect(updatedRule.schedule).to.eql({ interval: '1h' });
|
||||
|
||||
// Ensure revision is updated
|
||||
expect(updatedRule.revision).to.eql(1);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -217,6 +224,9 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
|
||||
expect(updatedRule).property('throttle', '1h');
|
||||
|
||||
// Ensure revision is updated
|
||||
expect(updatedRule.revision).to.eql(1);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -260,6 +270,9 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
|
||||
expect(updatedRule).property('notify_when', 'onActionGroupChange');
|
||||
|
||||
// Ensure revision is updated
|
||||
expect(updatedRule.revision).to.eql(1);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
supertest,
|
||||
|
@ -305,6 +318,9 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
expect(bulkSnoozeResponse.body.rules[0].snooze_schedule.length).to.eql(1);
|
||||
expect(bulkSnoozeResponse.body.rules[0].snooze_schedule[0].duration).to.eql(28800000);
|
||||
|
||||
// Ensure revision is NOT updated
|
||||
expect(bulkSnoozeResponse.body.rules[0].revision).to.eql(0);
|
||||
|
||||
const bulkUnsnoozeResponse = await supertest
|
||||
.post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_bulk_edit`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
|
@ -378,6 +394,8 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
expect(bulkSnoozeResponse.body.errors).to.have.length(0);
|
||||
expect(bulkSnoozeResponse.body.rules).to.have.length(1);
|
||||
expect(bulkSnoozeResponse.body.rules[0].snooze_schedule.length).to.eql(5);
|
||||
// Ensure revision is NOT updated
|
||||
expect(bulkSnoozeResponse.body.rules[0].revision).to.eql(0);
|
||||
|
||||
// Try adding more than 5 schedules
|
||||
const bulkSnoozeError = await supertest
|
||||
|
@ -433,6 +451,8 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
expect(bulkSnoozeResponse.body.errors).to.have.length(0);
|
||||
expect(bulkSnoozeResponse.body.rules).to.have.length(1);
|
||||
expect(bulkSnoozeResponse.body.rules[0].snooze_schedule).empty();
|
||||
// Ensure revision is NOT updated
|
||||
expect(bulkSnoozeResponse.body.rules[0].revision).to.eql(0);
|
||||
|
||||
// Ensure AAD isn't broken
|
||||
await checkAAD({
|
||||
|
@ -473,6 +493,8 @@ export default function createUpdateTests({ getService }: FtrProviderContext) {
|
|||
expect(bulkApiKeyResponse.body.errors).to.have.length(0);
|
||||
expect(bulkApiKeyResponse.body.rules).to.have.length(1);
|
||||
expect(bulkApiKeyResponse.body.rules[0].api_key_owner).to.eql(null);
|
||||
// Ensure revision is updated
|
||||
expect(bulkApiKeyResponse.body.rules[0].revision).to.eql(1);
|
||||
});
|
||||
|
||||
it(`shouldn't bulk edit rule from another space`, async () => {
|
||||
|
|
|
@ -81,6 +81,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
executionStatus: { status: 'pending', lastExecutionDate: '2022-12-20T09:10:15.500Z' },
|
||||
ruleTypeId: 'xpack.synthetics.alerts.monitorStatus',
|
||||
running: false,
|
||||
revision: 0,
|
||||
},
|
||||
omitFields
|
||||
)
|
||||
|
|
|
@ -102,6 +102,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
throttle: 'no_actions',
|
||||
exceptions_list: [],
|
||||
version: 1,
|
||||
revision: 0,
|
||||
};
|
||||
|
||||
const { body } = await supertest
|
||||
|
|
|
@ -361,6 +361,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
};
|
||||
ruleOutput.name = 'some other name';
|
||||
ruleOutput.version = 2;
|
||||
ruleOutput.revision = 0;
|
||||
expect(bodyToCompare).to.eql(ruleOutput);
|
||||
});
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -86,6 +87,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutputWithoutRuleId();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -103,6 +105,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -138,6 +141,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
outputRule.enabled = false;
|
||||
outputRule.severity = 'low';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
|
@ -165,6 +169,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
outputRule.timeline_title = 'some title';
|
||||
outputRule.timeline_id = 'some id';
|
||||
outputRule.version = 3;
|
||||
outputRule.revision = 2;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
|
|
|
@ -50,6 +50,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -71,10 +72,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule1 = getSimpleRuleOutput();
|
||||
outputRule1.name = 'some other name';
|
||||
outputRule1.version = 2;
|
||||
outputRule1.revision = 1;
|
||||
|
||||
const outputRule2 = getSimpleRuleOutput('rule-2');
|
||||
outputRule2.name = 'some other name';
|
||||
outputRule2.version = 2;
|
||||
outputRule2.revision = 1;
|
||||
|
||||
const bodyToCompare1 = removeServerGeneratedProperties(body[0]);
|
||||
const bodyToCompare2 = removeServerGeneratedProperties(body[1]);
|
||||
|
@ -95,6 +98,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -116,10 +120,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule1 = getSimpleRuleOutputWithoutRuleId('rule-1');
|
||||
outputRule1.name = 'some other name';
|
||||
outputRule1.version = 2;
|
||||
outputRule1.revision = 1;
|
||||
|
||||
const outputRule2 = getSimpleRuleOutputWithoutRuleId('rule-2');
|
||||
outputRule2.name = 'some other name';
|
||||
outputRule2.version = 2;
|
||||
outputRule2.revision = 1;
|
||||
|
||||
const bodyToCompare1 = removeServerGeneratedPropertiesIncludingRuleId(body[0]);
|
||||
const bodyToCompare2 = removeServerGeneratedPropertiesIncludingRuleId(body[1]);
|
||||
|
@ -140,6 +146,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -175,6 +182,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
outputRule.enabled = false;
|
||||
outputRule.severity = 'low';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
|
@ -202,6 +210,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
outputRule.timeline_title = 'some title';
|
||||
outputRule.timeline_id = 'some id';
|
||||
outputRule.version = 3;
|
||||
outputRule.revision = 2;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
|
@ -256,6 +265,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect([bodyToCompare, body[1]]).to.eql([
|
||||
|
@ -286,6 +296,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect([bodyToCompare, body[1]]).to.eql([
|
||||
|
|
|
@ -57,6 +57,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -102,6 +103,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutputWithoutRuleId();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -124,6 +126,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -146,6 +149,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
outputRule.enabled = false;
|
||||
outputRule.severity = 'low';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
|
@ -178,6 +182,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 3;
|
||||
outputRule.revision = 2;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
|
|
|
@ -57,6 +57,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -87,10 +88,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule1 = getSimpleRuleOutput();
|
||||
outputRule1.name = 'some other name';
|
||||
outputRule1.version = 2;
|
||||
outputRule1.revision = 1;
|
||||
|
||||
const outputRule2 = getSimpleRuleOutput('rule-2');
|
||||
outputRule2.name = 'some other name';
|
||||
outputRule2.version = 2;
|
||||
outputRule2.revision = 1;
|
||||
|
||||
const bodyToCompare1 = removeServerGeneratedProperties(body[0]);
|
||||
const bodyToCompare2 = removeServerGeneratedProperties(body[1]);
|
||||
|
@ -116,6 +119,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -144,10 +148,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule1 = getSimpleRuleOutputWithoutRuleId('rule-1');
|
||||
outputRule1.name = 'some other name';
|
||||
outputRule1.version = 2;
|
||||
outputRule1.revision = 1;
|
||||
|
||||
const outputRule2 = getSimpleRuleOutputWithoutRuleId('rule-2');
|
||||
outputRule2.name = 'some other name';
|
||||
outputRule2.version = 2;
|
||||
outputRule2.revision = 1;
|
||||
|
||||
const bodyToCompare1 = removeServerGeneratedPropertiesIncludingRuleId(body[0]);
|
||||
const bodyToCompare2 = removeServerGeneratedPropertiesIncludingRuleId(body[1]);
|
||||
|
@ -173,6 +179,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
});
|
||||
|
@ -195,6 +202,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
outputRule.enabled = false;
|
||||
outputRule.severity = 'low';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
|
@ -227,6 +235,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 3;
|
||||
outputRule.revision = 2;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect(bodyToCompare).to.eql(outputRule);
|
||||
|
@ -294,6 +303,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect([bodyToCompare, body[1]]).to.eql([
|
||||
|
@ -331,6 +341,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const outputRule = getSimpleRuleOutput();
|
||||
outputRule.name = 'some other name';
|
||||
outputRule.version = 2;
|
||||
outputRule.revision = 1;
|
||||
|
||||
const bodyToCompare = removeServerGeneratedProperties(body[0]);
|
||||
expect([bodyToCompare, body[1]]).to.eql([
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue