mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[RAM] Return rules from bulk delete (#147077)
## Summary In this PR we are returning in response for bulkDelete API actual rules which have being deleted like we do for the bulk editing API. Security solution needs that information as part of the API contract so they can have their stateful rule table still working the way it is. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
031d8a8cea
commit
925666e04b
10 changed files with 300 additions and 410 deletions
|
@ -26,7 +26,7 @@ beforeEach(() => {
|
|||
|
||||
describe('bulkDeleteRulesRoute', () => {
|
||||
const bulkDeleteRequest = { filter: '' };
|
||||
const bulkDeleteResult = { errors: [], total: 1, taskIdsFailedToBeDeleted: [] };
|
||||
const bulkDeleteResult = { rules: [], errors: [], total: 1, taskIdsFailedToBeDeleted: [] };
|
||||
|
||||
it('should delete rules with proper parameters', async () => {
|
||||
const licenseState = licenseStateMock.create();
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
export { mapSortField } from './map_sort_field';
|
||||
export { validateOperationOnAttributes } from './validate_attributes';
|
||||
export { retryIfBulkEditConflicts } from './retry_if_bulk_edit_conflicts';
|
||||
export { retryIfBulkDeleteConflicts } from './retry_if_bulk_delete_conflicts';
|
||||
export { retryIfBulkDisableConflicts } from './retry_if_bulk_disable_conflicts';
|
||||
export { retryIfBulkOperationConflicts } from './retry_if_bulk_operation_conflicts';
|
||||
export { applyBulkEditOperation } from './apply_bulk_edit_operation';
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* 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 { KueryNode } from '@kbn/es-query';
|
||||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
|
||||
import { retryIfBulkDeleteConflicts } from './retry_if_bulk_delete_conflicts';
|
||||
import { RETRY_IF_CONFLICTS_ATTEMPTS } from './wait_before_next_retry';
|
||||
|
||||
const mockFilter: KueryNode = {
|
||||
type: 'function',
|
||||
value: 'mock',
|
||||
};
|
||||
|
||||
const mockLogger = loggingSystemMock.create().get();
|
||||
|
||||
const mockSuccessfulResult = {
|
||||
apiKeysToInvalidate: ['apiKey1'],
|
||||
errors: [],
|
||||
taskIdsToDelete: ['taskId1'],
|
||||
};
|
||||
|
||||
const error409 = {
|
||||
message: 'some fake message',
|
||||
status: 409,
|
||||
rule: {
|
||||
id: 'fake_rule_id',
|
||||
name: 'fake rule name',
|
||||
},
|
||||
};
|
||||
|
||||
const getOperationConflictsTimes = (times: number) => {
|
||||
return async () => {
|
||||
conflictOperationMock();
|
||||
times--;
|
||||
if (times >= 0) {
|
||||
return {
|
||||
apiKeysToInvalidate: [],
|
||||
taskIdsToDelete: [],
|
||||
errors: [error409],
|
||||
};
|
||||
}
|
||||
return mockSuccessfulResult;
|
||||
};
|
||||
};
|
||||
|
||||
const OperationSuccessful = async () => mockSuccessfulResult;
|
||||
const conflictOperationMock = jest.fn();
|
||||
|
||||
describe('retryIfBulkDeleteConflicts', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
test('should work when operation is successful', async () => {
|
||||
const result = await retryIfBulkDeleteConflicts(mockLogger, OperationSuccessful, mockFilter);
|
||||
|
||||
expect(result).toEqual(mockSuccessfulResult);
|
||||
});
|
||||
|
||||
test('should throw error when operation fails', async () => {
|
||||
await expect(
|
||||
retryIfBulkDeleteConflicts(
|
||||
mockLogger,
|
||||
async () => {
|
||||
throw Error('Test failure');
|
||||
},
|
||||
mockFilter
|
||||
)
|
||||
).rejects.toThrowError('Test failure');
|
||||
});
|
||||
|
||||
test(`should return conflict errors when number of retries exceeds ${RETRY_IF_CONFLICTS_ATTEMPTS}`, async () => {
|
||||
const result = await retryIfBulkDeleteConflicts(
|
||||
mockLogger,
|
||||
getOperationConflictsTimes(RETRY_IF_CONFLICTS_ATTEMPTS + 1),
|
||||
mockFilter
|
||||
);
|
||||
|
||||
expect(result.errors).toEqual([error409]);
|
||||
expect(mockLogger.warn).toBeCalledWith('Bulk delete rules conflicts, exceeded retries');
|
||||
});
|
||||
|
||||
for (let i = 1; i <= RETRY_IF_CONFLICTS_ATTEMPTS; i++) {
|
||||
test(`should work when operation conflicts ${i} times`, async () => {
|
||||
const result = await retryIfBulkDeleteConflicts(
|
||||
mockLogger,
|
||||
getOperationConflictsTimes(i),
|
||||
mockFilter
|
||||
);
|
||||
|
||||
expect(conflictOperationMock.mock.calls.length).toBe(i + 1);
|
||||
expect(result).toStrictEqual(mockSuccessfulResult);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* 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 pMap from 'p-map';
|
||||
import { chunk } from 'lodash';
|
||||
import { KueryNode } from '@kbn/es-query';
|
||||
import { Logger } from '@kbn/core/server';
|
||||
import { convertRuleIdsToKueryNode } from '../../lib';
|
||||
import { BulkOperationError } from '../types';
|
||||
import { waitBeforeNextRetry, RETRY_IF_CONFLICTS_ATTEMPTS } from './wait_before_next_retry';
|
||||
|
||||
const MAX_RULES_IDS_IN_RETRY = 1000;
|
||||
|
||||
export type BulkDeleteOperation = (filter: KueryNode | null) => Promise<{
|
||||
apiKeysToInvalidate: string[];
|
||||
errors: BulkOperationError[];
|
||||
taskIdsToDelete: string[];
|
||||
}>;
|
||||
|
||||
interface ReturnRetry {
|
||||
apiKeysToInvalidate: string[];
|
||||
errors: BulkOperationError[];
|
||||
taskIdsToDelete: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retries BulkDelete requests
|
||||
* If in response are presents conflicted savedObjects(409 statusCode), this util constructs filter with failed SO ids and retries bulkDelete operation until
|
||||
* all SO updated or number of retries exceeded
|
||||
* @param logger
|
||||
* @param bulkEditOperation
|
||||
* @param filter - KueryNode filter
|
||||
* @param retries - number of retries left
|
||||
* @param accApiKeysToInvalidate - accumulated apiKeys that need to be invalidated
|
||||
* @param accErrors - accumulated conflict errors
|
||||
* @param accTaskIdsToDelete - accumulated task ids
|
||||
* @returns Promise<ReturnRetry>
|
||||
*/
|
||||
export const retryIfBulkDeleteConflicts = async (
|
||||
logger: Logger,
|
||||
bulkDeleteOperation: BulkDeleteOperation,
|
||||
filter: KueryNode | null,
|
||||
retries: number = RETRY_IF_CONFLICTS_ATTEMPTS,
|
||||
accApiKeysToInvalidate: string[] = [],
|
||||
accErrors: BulkOperationError[] = [],
|
||||
accTaskIdsToDelete: string[] = []
|
||||
): Promise<ReturnRetry> => {
|
||||
try {
|
||||
const {
|
||||
apiKeysToInvalidate: currentApiKeysToInvalidate,
|
||||
errors: currentErrors,
|
||||
taskIdsToDelete: currentTaskIdsToDelete,
|
||||
} = await bulkDeleteOperation(filter);
|
||||
|
||||
const apiKeysToInvalidate = [...accApiKeysToInvalidate, ...currentApiKeysToInvalidate];
|
||||
const taskIdsToDelete = [...accTaskIdsToDelete, ...currentTaskIdsToDelete];
|
||||
const errors =
|
||||
retries <= 0
|
||||
? [...accErrors, ...currentErrors]
|
||||
: [...accErrors, ...currentErrors.filter((error) => error.status !== 409)];
|
||||
|
||||
const ruleIdsWithConflictError = currentErrors.reduce<string[]>((acc, error) => {
|
||||
if (error.status === 409) {
|
||||
return [...acc, error.rule.id];
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (ruleIdsWithConflictError.length === 0) {
|
||||
return {
|
||||
apiKeysToInvalidate,
|
||||
errors,
|
||||
taskIdsToDelete,
|
||||
};
|
||||
}
|
||||
|
||||
if (retries <= 0) {
|
||||
logger.warn('Bulk delete rules conflicts, exceeded retries');
|
||||
|
||||
return {
|
||||
apiKeysToInvalidate,
|
||||
errors,
|
||||
taskIdsToDelete,
|
||||
};
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`Bulk delete rules conflicts, retrying ..., ${ruleIdsWithConflictError.length} saved objects conflicted`
|
||||
);
|
||||
|
||||
await waitBeforeNextRetry(retries);
|
||||
|
||||
// here, we construct filter query with ids. But, due to a fact that number of conflicted saved objects can exceed few thousands we can encounter following error:
|
||||
// "all shards failed: search_phase_execution_exception: [query_shard_exception] Reason: failed to create query: maxClauseCount is set to 2621"
|
||||
// That's why we chunk processing ids into pieces by size equals to MAX_RULES_IDS_IN_RETRY
|
||||
return (
|
||||
await pMap(
|
||||
chunk(ruleIdsWithConflictError, MAX_RULES_IDS_IN_RETRY),
|
||||
async (queryIds) =>
|
||||
retryIfBulkDeleteConflicts(
|
||||
logger,
|
||||
bulkDeleteOperation,
|
||||
convertRuleIdsToKueryNode(queryIds),
|
||||
retries - 1,
|
||||
apiKeysToInvalidate,
|
||||
errors,
|
||||
taskIdsToDelete
|
||||
),
|
||||
{
|
||||
concurrency: 1,
|
||||
}
|
||||
)
|
||||
).reduce<ReturnRetry>(
|
||||
(acc, item) => {
|
||||
return {
|
||||
apiKeysToInvalidate: [...acc.apiKeysToInvalidate, ...item.apiKeysToInvalidate],
|
||||
errors: [...acc.errors, ...item.errors],
|
||||
taskIdsToDelete: [...acc.taskIdsToDelete, ...item.taskIdsToDelete],
|
||||
};
|
||||
},
|
||||
{ apiKeysToInvalidate: [], errors: [], taskIdsToDelete: [] }
|
||||
);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
|
@ -6,14 +6,14 @@
|
|||
*/
|
||||
|
||||
import { KueryNode, nodeBuilder } from '@kbn/es-query';
|
||||
import { SavedObjectsBulkDeleteObject } from '@kbn/core/server';
|
||||
import { SavedObjectsBulkUpdateObject } from '@kbn/core/server';
|
||||
import { RawRule } from '../../types';
|
||||
import { convertRuleIdsToKueryNode } from '../../lib';
|
||||
import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation';
|
||||
import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events';
|
||||
import { getAuthorizationFilter, checkAuthorizationAndGetTotal } from '../lib';
|
||||
import { getAuthorizationFilter, checkAuthorizationAndGetTotal, getAlertFromRaw } from '../lib';
|
||||
import {
|
||||
retryIfBulkDeleteConflicts,
|
||||
retryIfBulkOperationConflicts,
|
||||
buildKueryNodeFilter,
|
||||
getAndValidateCommonBulkOptions,
|
||||
} from '../common';
|
||||
|
@ -35,11 +35,15 @@ export const bulkDeleteRules = async (context: RulesClientContext, options: Bulk
|
|||
action: 'DELETE',
|
||||
});
|
||||
|
||||
const { apiKeysToInvalidate, errors, taskIdsToDelete } = await retryIfBulkDeleteConflicts(
|
||||
context.logger,
|
||||
(filterKueryNode: KueryNode | null) => bulkDeleteWithOCC(context, { filter: filterKueryNode }),
|
||||
kueryNodeFilterWithAuth
|
||||
);
|
||||
const { rules, errors, accListSpecificForBulkOperation } = await retryIfBulkOperationConflicts({
|
||||
action: 'DELETE',
|
||||
logger: context.logger,
|
||||
bulkOperation: (filterKueryNode: KueryNode | null) =>
|
||||
bulkDeleteWithOCC(context, { filter: filterKueryNode }),
|
||||
filter: kueryNodeFilterWithAuth,
|
||||
});
|
||||
|
||||
const [apiKeysToInvalidate, taskIdsToDelete] = accListSpecificForBulkOperation;
|
||||
|
||||
const taskIdsFailedToBeDeleted: string[] = [];
|
||||
const taskIdsSuccessfullyDeleted: string[] = [];
|
||||
|
@ -80,7 +84,18 @@ export const bulkDeleteRules = async (context: RulesClientContext, options: Bulk
|
|||
context.unsecuredSavedObjectsClient
|
||||
);
|
||||
|
||||
return { errors, total, taskIdsFailedToBeDeleted };
|
||||
const deletedRules = rules.map(({ id, attributes, references }) => {
|
||||
return getAlertFromRaw(
|
||||
context,
|
||||
id,
|
||||
attributes.alertTypeId as string,
|
||||
attributes as RawRule,
|
||||
references,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
return { errors, rules: deletedRules, total, taskIdsFailedToBeDeleted };
|
||||
};
|
||||
|
||||
const bulkDeleteWithOCC = async (
|
||||
|
@ -97,7 +112,7 @@ const bulkDeleteWithOCC = async (
|
|||
}
|
||||
);
|
||||
|
||||
const rules: SavedObjectsBulkDeleteObject[] = [];
|
||||
const rulesToDelete: Array<SavedObjectsBulkUpdateObject<RawRule>> = [];
|
||||
const apiKeysToInvalidate: string[] = [];
|
||||
const taskIdsToDelete: string[] = [];
|
||||
const errors: BulkOperationError[] = [];
|
||||
|
@ -116,7 +131,7 @@ const bulkDeleteWithOCC = async (
|
|||
if (rule.attributes.scheduledTaskId) {
|
||||
taskIdToRuleIdMapping[rule.id] = rule.attributes.scheduledTaskId;
|
||||
}
|
||||
rules.push(rule);
|
||||
rulesToDelete.push(rule);
|
||||
|
||||
context.auditLogger?.log(
|
||||
ruleAuditEvent({
|
||||
|
@ -128,7 +143,9 @@ const bulkDeleteWithOCC = async (
|
|||
}
|
||||
}
|
||||
|
||||
const result = await context.unsecuredSavedObjectsClient.bulkDelete(rules);
|
||||
const result = await context.unsecuredSavedObjectsClient.bulkDelete(rulesToDelete);
|
||||
|
||||
const deletedRuleIds: string[] = [];
|
||||
|
||||
result.statuses.forEach((status) => {
|
||||
if (status.error === undefined) {
|
||||
|
@ -138,6 +155,7 @@ const bulkDeleteWithOCC = async (
|
|||
if (taskIdToRuleIdMapping[status.id]) {
|
||||
taskIdsToDelete.push(taskIdToRuleIdMapping[status.id]);
|
||||
}
|
||||
deletedRuleIds.push(status.id);
|
||||
} else {
|
||||
errors.push({
|
||||
message: status.error.message ?? 'n/a',
|
||||
|
@ -149,5 +167,11 @@ const bulkDeleteWithOCC = async (
|
|||
});
|
||||
}
|
||||
});
|
||||
return { apiKeysToInvalidate, errors, taskIdsToDelete };
|
||||
const rules = rulesToDelete.filter((rule) => deletedRuleIds.includes(rule.id));
|
||||
|
||||
return {
|
||||
errors,
|
||||
rules,
|
||||
accListSpecificForBulkOperation: [apiKeysToInvalidate, taskIdsToDelete],
|
||||
};
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks';
|
|||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation';
|
||||
import { loggerMock } from '@kbn/logging-mocks';
|
||||
import { enabledRule1, enabledRule2, returnedRule1, returnedRule2 } from './test_helpers';
|
||||
|
||||
jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({
|
||||
bulkMarkApiKeysForInvalidation: jest.fn(),
|
||||
|
@ -63,33 +64,9 @@ setGlobalDate();
|
|||
|
||||
describe('bulkDelete', () => {
|
||||
let rulesClient: RulesClient;
|
||||
const existingRule = {
|
||||
id: 'id1',
|
||||
type: 'alert',
|
||||
attributes: {},
|
||||
references: [],
|
||||
version: '123',
|
||||
};
|
||||
const existingDecryptedRule1 = {
|
||||
...existingRule,
|
||||
attributes: {
|
||||
...existingRule.attributes,
|
||||
scheduledTaskId: 'taskId1',
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
};
|
||||
const existingDecryptedRule2 = {
|
||||
...existingRule,
|
||||
id: 'id2',
|
||||
attributes: {
|
||||
...existingRule.attributes,
|
||||
scheduledTaskId: 'taskId2',
|
||||
apiKey: Buffer.from('321:abc').toString('base64'),
|
||||
},
|
||||
};
|
||||
|
||||
const mockCreatePointInTimeFinderAsInternalUser = (
|
||||
response = { saved_objects: [existingDecryptedRule1, existingDecryptedRule2] }
|
||||
response = { saved_objects: [enabledRule1, enabledRule2] }
|
||||
) => {
|
||||
encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest
|
||||
.fn()
|
||||
|
@ -103,6 +80,7 @@ describe('bulkDelete', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
rulesClient = new RulesClient(rulesClientParams);
|
||||
mockCreatePointInTimeFinderAsInternalUser();
|
||||
authorization.getFindAuthorizationFilter.mockResolvedValue({
|
||||
ensureRuleTypeIsAuthorized() {},
|
||||
});
|
||||
|
@ -136,7 +114,6 @@ describe('bulkDelete', () => {
|
|||
});
|
||||
|
||||
test('should try to delete rules, one successful and one with 500 error', async () => {
|
||||
mockCreatePointInTimeFinderAsInternalUser();
|
||||
unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({
|
||||
statuses: [
|
||||
{ id: 'id1', type: 'alert', success: true },
|
||||
|
@ -157,11 +134,11 @@ describe('bulkDelete', () => {
|
|||
|
||||
expect(unsecuredSavedObjectsClient.bulkDelete).toHaveBeenCalledTimes(1);
|
||||
expect(unsecuredSavedObjectsClient.bulkDelete).toHaveBeenCalledWith([
|
||||
existingDecryptedRule1,
|
||||
existingDecryptedRule2,
|
||||
enabledRule1,
|
||||
enabledRule2,
|
||||
]);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledTimes(1);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledWith(['taskId1']);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledWith(['id1']);
|
||||
expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledTimes(1);
|
||||
expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledWith(
|
||||
{ apiKeys: ['MTIzOmFiYw=='] },
|
||||
|
@ -169,7 +146,8 @@ describe('bulkDelete', () => {
|
|||
expect.anything()
|
||||
);
|
||||
expect(result).toStrictEqual({
|
||||
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'n/a' }, status: 500 }],
|
||||
rules: [returnedRule1],
|
||||
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 500 }],
|
||||
total: 2,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
|
@ -226,19 +204,19 @@ describe('bulkDelete', () => {
|
|||
.mockResolvedValueOnce({
|
||||
close: jest.fn(),
|
||||
find: function* asyncGenerator() {
|
||||
yield { saved_objects: [existingDecryptedRule1, existingDecryptedRule2] };
|
||||
yield { saved_objects: [enabledRule1, enabledRule2] };
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
close: jest.fn(),
|
||||
find: function* asyncGenerator() {
|
||||
yield { saved_objects: [existingDecryptedRule2] };
|
||||
yield { saved_objects: [enabledRule2] };
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
close: jest.fn(),
|
||||
find: function* asyncGenerator() {
|
||||
yield { saved_objects: [existingDecryptedRule2] };
|
||||
yield { saved_objects: [enabledRule2] };
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -246,7 +224,7 @@ describe('bulkDelete', () => {
|
|||
|
||||
expect(unsecuredSavedObjectsClient.bulkDelete).toHaveBeenCalledTimes(3);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledTimes(1);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledWith(['taskId1']);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledWith(['id1']);
|
||||
expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledTimes(1);
|
||||
expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledWith(
|
||||
{ apiKeys: ['MTIzOmFiYw=='] },
|
||||
|
@ -254,7 +232,8 @@ describe('bulkDelete', () => {
|
|||
expect.anything()
|
||||
);
|
||||
expect(result).toStrictEqual({
|
||||
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'n/a' }, status: 409 }],
|
||||
rules: [returnedRule1],
|
||||
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 409 }],
|
||||
total: 2,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
|
@ -292,19 +271,19 @@ describe('bulkDelete', () => {
|
|||
.mockResolvedValueOnce({
|
||||
close: jest.fn(),
|
||||
find: function* asyncGenerator() {
|
||||
yield { saved_objects: [existingDecryptedRule1, existingDecryptedRule2] };
|
||||
yield { saved_objects: [enabledRule1, enabledRule2] };
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
close: jest.fn(),
|
||||
find: function* asyncGenerator() {
|
||||
yield { saved_objects: [existingDecryptedRule2] };
|
||||
yield { saved_objects: [enabledRule2] };
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
close: jest.fn(),
|
||||
find: function* asyncGenerator() {
|
||||
yield { saved_objects: [existingDecryptedRule2] };
|
||||
yield { saved_objects: [enabledRule2] };
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -312,7 +291,7 @@ describe('bulkDelete', () => {
|
|||
|
||||
expect(unsecuredSavedObjectsClient.bulkDelete).toHaveBeenCalledTimes(2);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledTimes(1);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledWith(['taskId1', 'taskId2']);
|
||||
expect(taskManager.bulkRemoveIfExist).toHaveBeenCalledWith(['id1', 'id2']);
|
||||
expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledTimes(1);
|
||||
expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledWith(
|
||||
{ apiKeys: ['MTIzOmFiYw==', 'MzIxOmFiYw=='] },
|
||||
|
@ -320,6 +299,7 @@ describe('bulkDelete', () => {
|
|||
expect.anything()
|
||||
);
|
||||
expect(result).toStrictEqual({
|
||||
rules: [returnedRule1, returnedRule2],
|
||||
errors: [],
|
||||
total: 2,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
|
@ -345,7 +325,6 @@ describe('bulkDelete', () => {
|
|||
});
|
||||
|
||||
test('should throw an error if we do not get buckets', async () => {
|
||||
mockCreatePointInTimeFinderAsInternalUser();
|
||||
unsecuredSavedObjectsClient.find.mockResolvedValue({
|
||||
aggregations: {
|
||||
alertTypeId: {},
|
||||
|
@ -363,7 +342,6 @@ describe('bulkDelete', () => {
|
|||
|
||||
describe('taskManager', () => {
|
||||
test('should return task id if deleting task failed', async () => {
|
||||
mockCreatePointInTimeFinderAsInternalUser();
|
||||
unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({
|
||||
statuses: [
|
||||
{ id: 'id1', type: 'alert', success: true },
|
||||
|
@ -373,12 +351,12 @@ describe('bulkDelete', () => {
|
|||
taskManager.bulkRemoveIfExist.mockImplementation(async () => ({
|
||||
statuses: [
|
||||
{
|
||||
id: 'taskId1',
|
||||
id: 'id1',
|
||||
type: 'alert',
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
id: 'taskId2',
|
||||
id: 'id2',
|
||||
type: 'alert',
|
||||
success: false,
|
||||
error: {
|
||||
|
@ -394,16 +372,13 @@ describe('bulkDelete', () => {
|
|||
|
||||
expect(logger.debug).toBeCalledTimes(1);
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Successfully deleted schedules for underlying tasks: taskId1'
|
||||
'Successfully deleted schedules for underlying tasks: id1'
|
||||
);
|
||||
expect(logger.error).toBeCalledTimes(1);
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Failure to delete schedules for underlying tasks: taskId2'
|
||||
);
|
||||
expect(logger.error).toBeCalledWith('Failure to delete schedules for underlying tasks: id2');
|
||||
});
|
||||
|
||||
test('should not throw an error if taskManager throw an error', async () => {
|
||||
mockCreatePointInTimeFinderAsInternalUser();
|
||||
unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({
|
||||
statuses: [
|
||||
{ id: 'id1', type: 'alert', success: true },
|
||||
|
@ -418,7 +393,7 @@ describe('bulkDelete', () => {
|
|||
|
||||
expect(logger.error).toBeCalledTimes(1);
|
||||
expect(logger.error).toBeCalledWith(
|
||||
'Failure to delete schedules for underlying tasks: taskId1, taskId2. TaskManager bulkRemoveIfExist failed with Error: UPS'
|
||||
'Failure to delete schedules for underlying tasks: id1, id2. TaskManager bulkRemoveIfExist failed with Error: UPS'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -433,12 +408,12 @@ describe('bulkDelete', () => {
|
|||
taskManager.bulkRemoveIfExist.mockImplementation(async () => ({
|
||||
statuses: [
|
||||
{
|
||||
id: 'taskId1',
|
||||
id: 'id1',
|
||||
type: 'alert',
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
id: 'taskId2',
|
||||
id: 'id2',
|
||||
type: 'alert',
|
||||
success: true,
|
||||
},
|
||||
|
@ -449,7 +424,7 @@ describe('bulkDelete', () => {
|
|||
|
||||
expect(logger.debug).toBeCalledTimes(1);
|
||||
expect(logger.debug).toBeCalledWith(
|
||||
'Successfully deleted schedules for underlying tasks: taskId1, taskId2'
|
||||
'Successfully deleted schedules for underlying tasks: id1, id2'
|
||||
);
|
||||
expect(logger.error).toBeCalledTimes(0);
|
||||
});
|
||||
|
@ -459,7 +434,6 @@ describe('bulkDelete', () => {
|
|||
jest.spyOn(auditLogger, 'log').mockImplementation();
|
||||
|
||||
test('logs audit event when deleting rules', async () => {
|
||||
mockCreatePointInTimeFinderAsInternalUser();
|
||||
unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({
|
||||
statuses: [
|
||||
{ id: 'id1', type: 'alert', success: true },
|
||||
|
@ -482,7 +456,6 @@ describe('bulkDelete', () => {
|
|||
});
|
||||
|
||||
test('logs audit event when authentication failed', async () => {
|
||||
mockCreatePointInTimeFinderAsInternalUser();
|
||||
authorization.ensureAuthorized.mockImplementation(() => {
|
||||
throw new Error('Unauthorized');
|
||||
});
|
||||
|
@ -499,7 +472,6 @@ describe('bulkDelete', () => {
|
|||
});
|
||||
|
||||
test('logs audit event when getting an authorization filter failed', async () => {
|
||||
mockCreatePointInTimeFinderAsInternalUser();
|
||||
authorization.getFindAuthorizationFilter.mockImplementation(() => {
|
||||
throw new Error('Error');
|
||||
});
|
||||
|
|
|
@ -21,16 +21,14 @@ import { loggerMock } from '@kbn/logging-mocks';
|
|||
import { BulkUpdateTaskResult } from '@kbn/task-manager-plugin/server/task_scheduling';
|
||||
import { eventLoggerMock } from '@kbn/event-log-plugin/server/mocks';
|
||||
import {
|
||||
disabledRule1,
|
||||
disabledRule2,
|
||||
enabledRule1,
|
||||
enabledRule2,
|
||||
savedObjectWith409Error,
|
||||
savedObjectWith500Error,
|
||||
successfulSavedObject1,
|
||||
successfulSavedObject2,
|
||||
successfulSavedObjects,
|
||||
updatedRule1,
|
||||
updatedRule2,
|
||||
returnedDisabledRule1,
|
||||
returnedDisabledRule2,
|
||||
} from './test_helpers';
|
||||
|
||||
jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({
|
||||
|
@ -130,7 +128,7 @@ describe('bulkDisableRules', () => {
|
|||
|
||||
test('should disable two rule', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [disabledRule1, disabledRule2],
|
||||
});
|
||||
|
||||
const result = await rulesClient.bulkDisableRules({ filter: 'fake_filter' });
|
||||
|
@ -156,14 +154,14 @@ describe('bulkDisableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [],
|
||||
rules: [updatedRule1, updatedRule2],
|
||||
rules: [returnedDisabledRule1, returnedDisabledRule2],
|
||||
total: 2,
|
||||
});
|
||||
});
|
||||
|
||||
test('should try to disable rules, one successful and one with 500 error', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1, savedObjectWith500Error],
|
||||
saved_objects: [disabledRule1, savedObjectWith500Error],
|
||||
});
|
||||
|
||||
const result = await rulesClient.bulkDisableRules({ filter: 'fake_filter' });
|
||||
|
@ -183,7 +181,7 @@ describe('bulkDisableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 500 }],
|
||||
rules: [updatedRule1],
|
||||
rules: [returnedDisabledRule1],
|
||||
total: 2,
|
||||
});
|
||||
});
|
||||
|
@ -191,7 +189,7 @@ describe('bulkDisableRules', () => {
|
|||
test('should try to disable rules, one successful and one with 409 error, which will not be deleted with retry', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate
|
||||
.mockResolvedValueOnce({
|
||||
saved_objects: [successfulSavedObject1, savedObjectWith409Error],
|
||||
saved_objects: [disabledRule1, savedObjectWith409Error],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
saved_objects: [savedObjectWith409Error],
|
||||
|
@ -228,7 +226,7 @@ describe('bulkDisableRules', () => {
|
|||
expect(taskManager.bulkDisable).toHaveBeenCalledWith(['id1']);
|
||||
expect(result).toStrictEqual({
|
||||
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 409 }],
|
||||
rules: [updatedRule1],
|
||||
rules: [returnedDisabledRule1],
|
||||
total: 2,
|
||||
});
|
||||
});
|
||||
|
@ -236,10 +234,10 @@ describe('bulkDisableRules', () => {
|
|||
test('should try to disable rules, one successful and one with 409 error, which successfully will be disabled with retry', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate
|
||||
.mockResolvedValueOnce({
|
||||
saved_objects: [successfulSavedObject1, savedObjectWith409Error],
|
||||
saved_objects: [disabledRule1, savedObjectWith409Error],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
saved_objects: [successfulSavedObject2],
|
||||
saved_objects: [disabledRule2],
|
||||
});
|
||||
|
||||
encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest
|
||||
|
@ -269,7 +267,7 @@ describe('bulkDisableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [],
|
||||
rules: [updatedRule1, updatedRule2],
|
||||
rules: [returnedDisabledRule1, returnedDisabledRule2],
|
||||
total: 2,
|
||||
});
|
||||
});
|
||||
|
@ -313,7 +311,7 @@ describe('bulkDisableRules', () => {
|
|||
saved_objects: [enabledRule1, disabledRule2],
|
||||
});
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1],
|
||||
saved_objects: [disabledRule1],
|
||||
});
|
||||
|
||||
const result = await rulesClient.bulkDisableRules({ filter: 'fake_filter' });
|
||||
|
@ -333,7 +331,7 @@ describe('bulkDisableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [],
|
||||
rules: [updatedRule1],
|
||||
rules: [returnedDisabledRule1],
|
||||
total: 2,
|
||||
});
|
||||
});
|
||||
|
@ -341,7 +339,7 @@ describe('bulkDisableRules', () => {
|
|||
describe('taskManager', () => {
|
||||
test('should call task manager bulkDisable', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [disabledRule1, disabledRule2],
|
||||
});
|
||||
|
||||
taskManager.bulkDisable.mockResolvedValue({
|
||||
|
@ -375,14 +373,16 @@ describe('bulkDisableRules', () => {
|
|||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
...successfulSavedObject1,
|
||||
...enabledRule1,
|
||||
attributes: {
|
||||
...enabledRule1.attributes,
|
||||
scheduledTaskId: 'taskId1',
|
||||
},
|
||||
} as SavedObject,
|
||||
{
|
||||
...successfulSavedObject2,
|
||||
...enabledRule2,
|
||||
attributes: {
|
||||
...enabledRule1,
|
||||
scheduledTaskId: 'taskId2',
|
||||
},
|
||||
} as SavedObject,
|
||||
|
@ -411,7 +411,7 @@ describe('bulkDisableRules', () => {
|
|||
|
||||
test('should disable one task if one rule was successfully disabled and one has 500 error', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1, savedObjectWith500Error],
|
||||
saved_objects: [disabledRule1, savedObjectWith500Error],
|
||||
});
|
||||
|
||||
taskManager.bulkDisable.mockResolvedValue({
|
||||
|
@ -442,7 +442,7 @@ describe('bulkDisableRules', () => {
|
|||
],
|
||||
});
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1],
|
||||
saved_objects: [disabledRule1],
|
||||
});
|
||||
|
||||
await rulesClient.bulkDisableRules({ filter: 'fake_filter' });
|
||||
|
@ -453,7 +453,7 @@ describe('bulkDisableRules', () => {
|
|||
|
||||
test('should not throw an error if taskManager.bulkDisable throw an error', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [disabledRule1, disabledRule2],
|
||||
});
|
||||
taskManager.bulkDisable.mockImplementation(() => {
|
||||
throw new Error('Something happend during bulkDisable');
|
||||
|
@ -471,8 +471,9 @@ describe('bulkDisableRules', () => {
|
|||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [
|
||||
{
|
||||
...successfulSavedObject1,
|
||||
...disabledRule1,
|
||||
attributes: {
|
||||
...disabledRule1.attributes,
|
||||
scheduledTaskId: 'taskId1',
|
||||
},
|
||||
} as SavedObject,
|
||||
|
@ -497,7 +498,7 @@ describe('bulkDisableRules', () => {
|
|||
|
||||
test('logs audit event when disabling rules', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1],
|
||||
saved_objects: [disabledRule1],
|
||||
});
|
||||
|
||||
await rulesClient.bulkDisableRules({ filter: 'fake_filter' });
|
||||
|
@ -561,7 +562,7 @@ describe('bulkDisableRules', () => {
|
|||
});
|
||||
test('should call logEvent', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [disabledRule1, disabledRule2],
|
||||
});
|
||||
|
||||
await rulesClient.bulkDisableRules({ filter: 'fake_filter' });
|
||||
|
@ -574,7 +575,7 @@ describe('bulkDisableRules', () => {
|
|||
throw new Error('UPS');
|
||||
});
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [disabledRule1, disabledRule2],
|
||||
});
|
||||
|
||||
await rulesClient.bulkDisableRules({ filter: 'fake_filter' });
|
||||
|
|
|
@ -21,14 +21,14 @@ import { BulkUpdateTaskResult } from '@kbn/task-manager-plugin/server/task_sched
|
|||
import {
|
||||
disabledRule1,
|
||||
disabledRule2,
|
||||
disabledRuleWithAction1,
|
||||
disabledRuleWithAction2,
|
||||
enabledRule1,
|
||||
enabledRule2,
|
||||
savedObjectWith409Error,
|
||||
savedObjectWith500Error,
|
||||
successfulSavedObject1,
|
||||
successfulSavedObject2,
|
||||
successfulSavedObjects,
|
||||
updatedRule1,
|
||||
updatedRule2,
|
||||
returnedRule1,
|
||||
returnedRule2,
|
||||
} from './test_helpers';
|
||||
|
||||
jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({
|
||||
|
@ -126,7 +126,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
test('should enable two rule', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [enabledRule1, enabledRule2],
|
||||
});
|
||||
|
||||
const result = await rulesClient.bulkEnableRules({ filter: 'fake_filter' });
|
||||
|
@ -152,7 +152,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [],
|
||||
rules: [updatedRule1, updatedRule2],
|
||||
rules: [returnedRule1, returnedRule2],
|
||||
total: 2,
|
||||
taskIdsFailedToBeEnabled: [],
|
||||
});
|
||||
|
@ -160,7 +160,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
test('should try to enable rules, one successful and one with 500 error', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1, savedObjectWith500Error],
|
||||
saved_objects: [enabledRule1, savedObjectWith500Error],
|
||||
});
|
||||
|
||||
const result = await rulesClient.bulkEnableRules({ filter: 'fake_filter' });
|
||||
|
@ -180,7 +180,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 500 }],
|
||||
rules: [updatedRule1],
|
||||
rules: [returnedRule1],
|
||||
total: 2,
|
||||
taskIdsFailedToBeEnabled: [],
|
||||
});
|
||||
|
@ -189,7 +189,7 @@ describe('bulkEnableRules', () => {
|
|||
test('should try to enable rules, one successful and one with 409 error, which will not be deleted with retry', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate
|
||||
.mockResolvedValueOnce({
|
||||
saved_objects: [successfulSavedObject1, savedObjectWith409Error],
|
||||
saved_objects: [enabledRule1, savedObjectWith409Error],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
saved_objects: [savedObjectWith409Error],
|
||||
|
@ -224,7 +224,7 @@ describe('bulkEnableRules', () => {
|
|||
expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(3);
|
||||
expect(result).toStrictEqual({
|
||||
errors: [{ message: 'UPS', rule: { id: 'id2', name: 'fakeName' }, status: 409 }],
|
||||
rules: [updatedRule1],
|
||||
rules: [returnedRule1],
|
||||
total: 2,
|
||||
taskIdsFailedToBeEnabled: [],
|
||||
});
|
||||
|
@ -233,10 +233,10 @@ describe('bulkEnableRules', () => {
|
|||
test('should try to enable rules, one successful and one with 409 error, which successfully will be deleted with retry', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate
|
||||
.mockResolvedValueOnce({
|
||||
saved_objects: [successfulSavedObject1, savedObjectWith409Error],
|
||||
saved_objects: [enabledRule1, savedObjectWith409Error],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
saved_objects: [successfulSavedObject2],
|
||||
saved_objects: [enabledRule2],
|
||||
});
|
||||
|
||||
encryptedSavedObjects.createPointInTimeFinderDecryptedAsInternalUser = jest
|
||||
|
@ -265,13 +265,13 @@ describe('bulkEnableRules', () => {
|
|||
expect(unsecuredSavedObjectsClient.bulkCreate).toHaveBeenCalledTimes(2);
|
||||
expect(result).toStrictEqual({
|
||||
errors: [],
|
||||
rules: [updatedRule1, updatedRule2],
|
||||
rules: [returnedRule1, returnedRule2],
|
||||
total: 2,
|
||||
taskIdsFailedToBeEnabled: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('should thow an error if number of matched rules greater than 10,000', async () => {
|
||||
test('should throw an error if number of matched rules greater than 10,000', async () => {
|
||||
unsecuredSavedObjectsClient.find.mockResolvedValue({
|
||||
aggregations: {
|
||||
alertTypeId: {
|
||||
|
@ -305,7 +305,10 @@ describe('bulkEnableRules', () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('should thow if there are actions, but do not have execute permissions', async () => {
|
||||
test('should throw if there are actions, but do not have execute permissions', async () => {
|
||||
mockCreatePointInTimeFinderAsInternalUser({
|
||||
saved_objects: [disabledRuleWithAction1, disabledRuleWithAction2],
|
||||
});
|
||||
actionsAuthorization.ensureAuthorized.mockImplementation(() => {
|
||||
throw new Error('UPS');
|
||||
});
|
||||
|
@ -339,7 +342,7 @@ describe('bulkEnableRules', () => {
|
|||
saved_objects: [disabledRule1, enabledRule2],
|
||||
});
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1],
|
||||
saved_objects: [enabledRule1],
|
||||
});
|
||||
|
||||
const result = await rulesClient.bulkEnableRules({ filter: 'fake_filter' });
|
||||
|
@ -359,7 +362,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [],
|
||||
rules: [updatedRule1],
|
||||
rules: [returnedRule1],
|
||||
total: 2,
|
||||
taskIdsFailedToBeEnabled: [],
|
||||
});
|
||||
|
@ -368,7 +371,7 @@ describe('bulkEnableRules', () => {
|
|||
describe('taskManager', () => {
|
||||
test('should return task id if deleting task failed', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [enabledRule1, enabledRule2],
|
||||
});
|
||||
taskManager.bulkEnable.mockImplementation(
|
||||
async () =>
|
||||
|
@ -400,7 +403,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [],
|
||||
rules: [updatedRule1, updatedRule2],
|
||||
rules: [returnedRule1, returnedRule2],
|
||||
total: 2,
|
||||
taskIdsFailedToBeEnabled: ['id2'],
|
||||
});
|
||||
|
@ -408,7 +411,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
test('should not throw an error if taskManager throw an error', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [enabledRule1, enabledRule2],
|
||||
});
|
||||
taskManager.bulkEnable.mockImplementation(() => {
|
||||
throw new Error('UPS');
|
||||
|
@ -423,7 +426,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
expect(result).toStrictEqual({
|
||||
errors: [],
|
||||
rules: [updatedRule1, updatedRule2],
|
||||
rules: [returnedRule1, returnedRule2],
|
||||
taskIdsFailedToBeEnabled: ['id1', 'id2'],
|
||||
total: 2,
|
||||
});
|
||||
|
@ -431,7 +434,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
test('should call task manager bulkEnable for two tasks', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: successfulSavedObjects,
|
||||
saved_objects: [enabledRule1, enabledRule2],
|
||||
});
|
||||
|
||||
await rulesClient.bulkEnableRules({ filter: 'fake_filter' });
|
||||
|
@ -442,7 +445,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
test('should should call task manager bulkEnable only for one task, if one rule have an error', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1, savedObjectWith500Error],
|
||||
saved_objects: [enabledRule1, savedObjectWith500Error],
|
||||
});
|
||||
|
||||
await rulesClient.bulkEnableRules({ filter: 'fake_filter' });
|
||||
|
@ -456,7 +459,7 @@ describe('bulkEnableRules', () => {
|
|||
saved_objects: [disabledRule1, enabledRule2],
|
||||
});
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1],
|
||||
saved_objects: [enabledRule1],
|
||||
});
|
||||
|
||||
taskManager.bulkEnable.mockImplementation(
|
||||
|
@ -485,7 +488,7 @@ describe('bulkEnableRules', () => {
|
|||
|
||||
test('logs audit event when enabling rules', async () => {
|
||||
unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({
|
||||
saved_objects: [successfulSavedObject1],
|
||||
saved_objects: [enabledRule1],
|
||||
});
|
||||
|
||||
await rulesClient.bulkEnableRules({ filter: 'fake_filter' });
|
||||
|
|
|
@ -7,24 +7,6 @@
|
|||
|
||||
import type { SavedObject } from '@kbn/core-saved-objects-common';
|
||||
|
||||
export const successfulSavedObject1 = {
|
||||
id: 'id1',
|
||||
version: '1',
|
||||
attributes: {
|
||||
scheduledTaskId: 'id1',
|
||||
},
|
||||
} as SavedObject;
|
||||
|
||||
export const successfulSavedObject2 = {
|
||||
id: 'id2',
|
||||
version: '1',
|
||||
attributes: {
|
||||
scheduledTaskId: 'id2',
|
||||
},
|
||||
} as SavedObject;
|
||||
|
||||
export const successfulSavedObjects = [successfulSavedObject1, successfulSavedObject2];
|
||||
|
||||
export const savedObjectWith500Error = {
|
||||
id: 'id2',
|
||||
error: {
|
||||
|
@ -53,16 +35,7 @@ export const defaultRule = {
|
|||
consumer: 'fakeConsumer',
|
||||
alertTypeId: 'fakeType',
|
||||
schedule: { interval: '5m' },
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionTypeId: '1',
|
||||
actionRef: '1',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
actions: [] as unknown,
|
||||
},
|
||||
references: [],
|
||||
version: '1',
|
||||
|
@ -85,7 +58,7 @@ export const enabledRule2 = {
|
|||
...defaultRule.attributes,
|
||||
enabled: true,
|
||||
scheduledTaskId: 'id2',
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
apiKey: Buffer.from('321:abc').toString('base64'),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -94,7 +67,7 @@ export const disabledRule1 = {
|
|||
attributes: {
|
||||
...defaultRule.attributes,
|
||||
enabled: false,
|
||||
scheduledTaskId: 'id2',
|
||||
scheduledTaskId: 'id1',
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
};
|
||||
|
@ -110,33 +83,80 @@ export const disabledRule2 = {
|
|||
},
|
||||
};
|
||||
|
||||
export const rule2 = {
|
||||
...defaultRule,
|
||||
id: 'id2',
|
||||
export const disabledRuleWithAction1 = {
|
||||
...disabledRule1,
|
||||
attributes: {
|
||||
...defaultRule.attributes,
|
||||
enabled: true,
|
||||
scheduledTaskId: 'id2',
|
||||
apiKey: Buffer.from('321:abc').toString('base64'),
|
||||
...disabledRule1.attributes,
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionTypeId: '1',
|
||||
actionRef: '1',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const updatedRule1 = {
|
||||
actions: [],
|
||||
id: 'id1',
|
||||
notifyWhen: undefined,
|
||||
params: undefined,
|
||||
schedule: undefined,
|
||||
snoozeSchedule: [],
|
||||
scheduledTaskId: 'id1',
|
||||
export const disabledRuleWithAction2 = {
|
||||
...disabledRule2,
|
||||
attributes: {
|
||||
...disabledRule2.attributes,
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionTypeId: '1',
|
||||
actionRef: '1',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const updatedRule2 = {
|
||||
export const returnedRule1 = {
|
||||
actions: [],
|
||||
id: 'id2',
|
||||
alertTypeId: 'fakeType',
|
||||
apiKey: 'MTIzOmFiYw==',
|
||||
consumer: 'fakeConsumer',
|
||||
enabled: true,
|
||||
id: 'id1',
|
||||
name: 'fakeName',
|
||||
notifyWhen: undefined,
|
||||
params: undefined,
|
||||
schedule: undefined,
|
||||
schedule: {
|
||||
interval: '5m',
|
||||
},
|
||||
scheduledTaskId: 'id1',
|
||||
snoozeSchedule: [],
|
||||
scheduledTaskId: 'id2',
|
||||
};
|
||||
|
||||
export const returnedRule2 = {
|
||||
actions: [],
|
||||
alertTypeId: 'fakeType',
|
||||
apiKey: 'MzIxOmFiYw==',
|
||||
consumer: 'fakeConsumer',
|
||||
enabled: true,
|
||||
id: 'id2',
|
||||
name: 'fakeName',
|
||||
notifyWhen: undefined,
|
||||
params: undefined,
|
||||
schedule: {
|
||||
interval: '5m',
|
||||
},
|
||||
scheduledTaskId: 'id2',
|
||||
snoozeSchedule: [],
|
||||
};
|
||||
|
||||
export const returnedDisabledRule1 = {
|
||||
...returnedRule1,
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
export const returnedDisabledRule2 = {
|
||||
...returnedRule2,
|
||||
enabled: false,
|
||||
};
|
||||
|
|
|
@ -10,7 +10,67 @@ import { UserAtSpaceScenarios, SuperuserAtSpace1 } from '../../../scenarios';
|
|||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||
import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib';
|
||||
|
||||
const defaultSuccessfulResponse = { errors: [], total: 1, taskIdsFailedToBeDeleted: [] };
|
||||
const getDefaultRules = (response: any) => ({
|
||||
id: response.body.rules[0].id,
|
||||
apiKey: response.body.rules[0].apiKey,
|
||||
notifyWhen: 'onThrottleInterval',
|
||||
enabled: true,
|
||||
name: 'abc',
|
||||
tags: ['foo'],
|
||||
consumer: 'alertsFixture',
|
||||
throttle: '1m',
|
||||
alertTypeId: 'test.noop',
|
||||
apiKeyOwner: response.body.rules[0].apiKeyOwner,
|
||||
createdBy: 'elastic',
|
||||
updatedBy: response.body.rules[0].updatedBy,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
schedule: { interval: '1m' },
|
||||
actions: [],
|
||||
params: {},
|
||||
snoozeSchedule: [],
|
||||
updatedAt: response.body.rules[0].updatedAt,
|
||||
createdAt: response.body.rules[0].createdAt,
|
||||
scheduledTaskId: response.body.rules[0].scheduledTaskId,
|
||||
executionStatus: response.body.rules[0].executionStatus,
|
||||
monitoring: response.body.rules[0].monitoring,
|
||||
...(response.body.rules[0].nextRun ? { nextRun: response.body.rules[0].nextRun } : {}),
|
||||
...(response.body.rules[0].lastRun ? { lastRun: response.body.rules[0].lastRun } : {}),
|
||||
});
|
||||
|
||||
const getThreeRules = (response: any) => {
|
||||
const rules: any[] = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
rules.push({
|
||||
id: response.body.rules[i].id,
|
||||
apiKey: response.body.rules[i].apiKey,
|
||||
notifyWhen: 'onThrottleInterval',
|
||||
enabled: true,
|
||||
name: 'abc',
|
||||
tags: ['multiple-rules-delete'],
|
||||
consumer: 'alertsFixture',
|
||||
throttle: '1m',
|
||||
alertTypeId: 'test.noop',
|
||||
apiKeyOwner: response.body.rules[i].apiKeyOwner,
|
||||
createdBy: 'elastic',
|
||||
updatedBy: response.body.rules[i].updatedBy,
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
schedule: { interval: '1m' },
|
||||
actions: [],
|
||||
params: {},
|
||||
snoozeSchedule: [],
|
||||
updatedAt: response.body.rules[i].updatedAt,
|
||||
createdAt: response.body.rules[i].createdAt,
|
||||
scheduledTaskId: response.body.rules[i].scheduledTaskId,
|
||||
executionStatus: response.body.rules[i].executionStatus,
|
||||
monitoring: response.body.rules[i].monitoring,
|
||||
...(response.body.rules[i].nextRun ? { nextRun: response.body.rules[i].nextRun } : {}),
|
||||
...(response.body.rules[i].lastRun ? { lastRun: response.body.rules[i].lastRun } : {}),
|
||||
});
|
||||
}
|
||||
return rules;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext) => {
|
||||
|
@ -76,7 +136,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
case 'superuser at space1':
|
||||
case 'space_1_all at space1':
|
||||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.body).to.eql(defaultSuccessfulResponse);
|
||||
expect(response.body).to.eql({
|
||||
rules: [getDefaultRules(response)],
|
||||
errors: [],
|
||||
total: 1,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
expect(response.statusCode).to.eql(200);
|
||||
try {
|
||||
await getScheduledTask(createdRule1.scheduled_task_id);
|
||||
|
@ -147,7 +212,18 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
break;
|
||||
case 'superuser at space1':
|
||||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.body).to.eql(defaultSuccessfulResponse);
|
||||
expect(response.body).to.eql({
|
||||
rules: [
|
||||
{
|
||||
...getDefaultRules(response),
|
||||
alertTypeId: 'test.restricted-noop',
|
||||
consumer: 'alertsRestrictedFixture',
|
||||
},
|
||||
],
|
||||
errors: [],
|
||||
total: 1,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
expect(response.statusCode).to.eql(200);
|
||||
try {
|
||||
await getScheduledTask(createdRule1.scheduled_task_id);
|
||||
|
@ -207,7 +283,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await getScheduledTask(createdRule1.scheduled_task_id);
|
||||
break;
|
||||
case 'superuser at space1':
|
||||
expect(response.body).to.eql(defaultSuccessfulResponse);
|
||||
expect(response.body).to.eql({
|
||||
rules: [{ ...getDefaultRules(response), alertTypeId: 'test.restricted-noop' }],
|
||||
errors: [],
|
||||
total: 1,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
expect(response.statusCode).to.eql(200);
|
||||
try {
|
||||
await getScheduledTask(createdRule1.scheduled_task_id);
|
||||
|
@ -267,7 +348,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
case 'space_1_all at space1':
|
||||
case 'space_1_all_alerts_none_actions at space1':
|
||||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.body).to.eql(defaultSuccessfulResponse);
|
||||
expect(response.body).to.eql({
|
||||
rules: [{ ...getDefaultRules(response), consumer: 'alerts' }],
|
||||
errors: [],
|
||||
total: 1,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
expect(response.statusCode).to.eql(200);
|
||||
try {
|
||||
await getScheduledTask(createdRule1.scheduled_task_id);
|
||||
|
@ -287,7 +373,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
supertest
|
||||
.post(`${getUrlPrefix(space.id)}/api/alerting/rule`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send(getTestRuleData({ tags: ['multiple-rules-edit'] }))
|
||||
.send(getTestRuleData({ tags: ['multiple-rules-delete'] }))
|
||||
.expect(200)
|
||||
)
|
||||
);
|
||||
|
@ -332,7 +418,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
case 'superuser at space1':
|
||||
case 'space_1_all at space1':
|
||||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.body).to.eql({ ...defaultSuccessfulResponse, total: 3 });
|
||||
expect(response.body).to.eql({
|
||||
rules: getThreeRules(response),
|
||||
errors: [],
|
||||
total: 3,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
expect(response.statusCode).to.eql(200);
|
||||
for (const rule of rules) {
|
||||
try {
|
||||
|
@ -399,7 +490,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
case 'superuser at space1':
|
||||
case 'space_1_all at space1':
|
||||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
expect(response.body).to.eql({ ...defaultSuccessfulResponse, total: 3 });
|
||||
expect(response.body).to.eql({
|
||||
rules: getThreeRules(response),
|
||||
errors: [],
|
||||
total: 3,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
expect(response.statusCode).to.eql(200);
|
||||
for (const rule of rules) {
|
||||
try {
|
||||
|
@ -431,7 +527,12 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
switch (scenario.id) {
|
||||
// This superuser has more privileges that we think
|
||||
case 'superuser at space1':
|
||||
expect(response.body).to.eql(defaultSuccessfulResponse);
|
||||
expect(response.body).to.eql({
|
||||
rules: [getDefaultRules(response)],
|
||||
errors: [],
|
||||
total: 1,
|
||||
taskIdsFailedToBeDeleted: [],
|
||||
});
|
||||
expect(response.statusCode).to.eql(200);
|
||||
try {
|
||||
await getScheduledTask(createdRule.scheduled_task_id);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue