mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Response Ops][Alerting] Alert Deletion - Persist settings (#211488)
## Summary https://github.com/elastic/kibana/issues/209258 updates the settings endpoint to also be able to get/set the alert deletion settings. The alert deletion setting should make use of this new endpoint to load its initial data and store any user update. > [!WARNING] > This will be merged into a feature branch. ## QA: Activate the feature flag ``` # config/kibana.dev.yml xpack.trigger_actions_ui.enableExperimental: ['alertDeletionSettingsEnabled'] ``` Follow these steps: - Go to rules - Click on settings - Change the alert deletion settings - Click on save - Reload and check the settings kept the values --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
445646d31d
commit
8928dbbdef
29 changed files with 780 additions and 96 deletions
|
@ -10,6 +10,7 @@
|
|||
import { HttpSetup } from '@kbn/core/public';
|
||||
import type { AsApiContract } from '@kbn/actions-types';
|
||||
import type { RulesSettingsAlertDeletion } from '@kbn/alerting-types';
|
||||
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
const transformAlertsDeletionSettingsResponse = ({
|
||||
active_alerts_deletion_threshold: activeAlertsDeletionThreshold,
|
||||
|
@ -31,19 +32,10 @@ const transformAlertsDeletionSettingsResponse = ({
|
|||
updatedBy,
|
||||
});
|
||||
|
||||
export const fetchAlertsDeletionSettings = async ({ http }: { http: HttpSetup }) => {
|
||||
// TODO: https://github.com/elastic/kibana/issues/209258
|
||||
|
||||
const res = {
|
||||
is_active_alerts_deletion_enabled: false,
|
||||
is_inactive_alerts_deletion_enabled: false,
|
||||
active_alerts_deletion_threshold: 0,
|
||||
inactive_alerts_deletion_threshold: 90,
|
||||
created_at: String(new Date().valueOf),
|
||||
updated_at: String(new Date().valueOf),
|
||||
created_by: null,
|
||||
updated_by: null,
|
||||
};
|
||||
export const fetchAlertDeletionSettings = async ({ http }: { http: HttpSetup }) => {
|
||||
const res = await http.get<AsApiContract<RulesSettingsAlertDeletion>>(
|
||||
`${INTERNAL_BASE_ALERTING_API_PATH}/rules/settings/_alert_deletion`
|
||||
);
|
||||
|
||||
return transformAlertsDeletionSettingsResponse(res);
|
||||
};
|
|
@ -7,5 +7,5 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { fetchAlertsDeletionSettings } from './fetch_alerts_deletion_settings';
|
||||
export { updateAlertsDeletionSettings } from './update_alerts_deletion_settings';
|
||||
export { fetchAlertDeletionSettings } from './fetch_alert_deletion_settings';
|
||||
export { updateAlertDeletionSettings } from './update_alert_deletion_settings';
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { HttpSetup } from '@kbn/core/public';
|
||||
import type { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common';
|
||||
import type {
|
||||
RulesSettingsAlertDeletion,
|
||||
RulesSettingsAlertDeletionProperties,
|
||||
} from '@kbn/alerting-types/rule_settings';
|
||||
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
|
||||
|
||||
const rewriteBodyRes: RewriteRequestCase<RulesSettingsAlertDeletion> = ({
|
||||
is_active_alerts_deletion_enabled: isActiveAlertsDeletionEnabled,
|
||||
is_inactive_alerts_deletion_enabled: isInactiveAlertsDeletionEnabled,
|
||||
active_alerts_deletion_threshold: activeAlertsDeletionThreshold,
|
||||
inactive_alerts_deletion_threshold: inactiveAlertsDeletionThreshold,
|
||||
created_by: createdBy,
|
||||
updated_by: updatedBy,
|
||||
created_at: createdAt,
|
||||
updated_at: updatedAt,
|
||||
}: any) => ({
|
||||
isActiveAlertsDeletionEnabled,
|
||||
isInactiveAlertsDeletionEnabled,
|
||||
activeAlertsDeletionThreshold,
|
||||
inactiveAlertsDeletionThreshold,
|
||||
createdBy,
|
||||
updatedBy,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
});
|
||||
|
||||
export const updateAlertDeletionSettings = async ({
|
||||
http,
|
||||
alertDeletionSettings,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alertDeletionSettings: RulesSettingsAlertDeletionProperties;
|
||||
}) => {
|
||||
let body: string;
|
||||
try {
|
||||
body = JSON.stringify({
|
||||
is_active_alerts_deletion_enabled: alertDeletionSettings.isActiveAlertsDeletionEnabled,
|
||||
is_inactive_alerts_deletion_enabled: alertDeletionSettings.isInactiveAlertsDeletionEnabled,
|
||||
active_alerts_deletion_threshold: alertDeletionSettings.activeAlertsDeletionThreshold,
|
||||
inactive_alerts_deletion_threshold: alertDeletionSettings.inactiveAlertsDeletionThreshold,
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to parse alert deletion settings update params: ${e}`);
|
||||
}
|
||||
|
||||
const response = await http.post<AsApiContract<RulesSettingsAlertDeletion>>(
|
||||
`${INTERNAL_BASE_ALERTING_API_PATH}/rules/settings/_alert_deletion`,
|
||||
{
|
||||
body,
|
||||
}
|
||||
);
|
||||
|
||||
return rewriteBodyRes(response);
|
||||
};
|
|
@ -1,44 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { HttpSetup } from '@kbn/core/public';
|
||||
import type { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common';
|
||||
import type {
|
||||
RulesSettingsAlertDeletion,
|
||||
RulesSettingsAlertDeletionProperties,
|
||||
} from '@kbn/alerting-types/rule_settings';
|
||||
|
||||
const rewriteBodyRes: RewriteRequestCase<RulesSettingsAlertDeletion> = ({ ...rest }: any) => ({
|
||||
...rest,
|
||||
});
|
||||
|
||||
export const updateAlertsDeletionSettings = async ({
|
||||
http,
|
||||
alertsDeletionSettings,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
alertsDeletionSettings: RulesSettingsAlertDeletionProperties;
|
||||
}) => {
|
||||
// TODO: https://github.com/elastic/kibana/issues/209258
|
||||
|
||||
const response: AsApiContract<RulesSettingsAlertDeletion> = await new Promise((resolve) => {
|
||||
resolve({
|
||||
is_active_alerts_deletion_enabled: alertsDeletionSettings.isActiveAlertsDeletionEnabled,
|
||||
is_inactive_alerts_deletion_enabled: alertsDeletionSettings.isInactiveAlertsDeletionEnabled,
|
||||
active_alerts_deletion_threshold: alertsDeletionSettings.activeAlertsDeletionThreshold,
|
||||
inactive_alerts_deletion_threshold: alertsDeletionSettings.inactiveAlertsDeletionThreshold,
|
||||
created_by: null,
|
||||
updated_by: null,
|
||||
created_at: '2021-08-25T14:00:00.000Z',
|
||||
updated_at: '2021-08-25T14:00:00.000Z',
|
||||
});
|
||||
});
|
||||
|
||||
return rewriteBodyRes(response);
|
||||
};
|
|
@ -14,4 +14,4 @@ export * from './fetch_connectors';
|
|||
export * from './fetch_connector_types';
|
||||
export * from './fetch_rule_type_aad_template_fields';
|
||||
export * from './fetch_ui_health_status';
|
||||
export * from './fetch_alerts_deletion_settings';
|
||||
export * from './alert_deletion_settings';
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { HttpStart } from '@kbn/core-http-browser';
|
||||
import type { RulesSettingsAlertDeletion } from '@kbn/alerting-types/rule_settings';
|
||||
import { fetchAlertsDeletionSettings } from '../apis/fetch_alerts_deletion_settings';
|
||||
import { fetchAlertDeletionSettings } from '../apis/alert_deletion_settings';
|
||||
|
||||
interface Props {
|
||||
http: HttpStart;
|
||||
|
@ -21,7 +21,7 @@ export const useFetchAlertsDeletionSettings = (props: Props) => {
|
|||
const { http, enabled, onSuccess } = props;
|
||||
|
||||
const queryFn = () => {
|
||||
return fetchAlertsDeletionSettings({ http });
|
||||
return fetchAlertDeletionSettings({ http });
|
||||
};
|
||||
|
||||
const { data, isFetching, isError, isLoadingError, isLoading, isInitialLoading } = useQuery({
|
||||
|
|
|
@ -5,14 +5,24 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { updateQueryDelaySettingsBodySchema } from './schemas/latest';
|
||||
export {
|
||||
updateQueryDelaySettingsBodySchema,
|
||||
updateAlertDeletionSettingsBodySchema,
|
||||
} from './schemas/latest';
|
||||
export type {
|
||||
UpdateQueryDelaySettingsRequestBody,
|
||||
UpdateQueryDelaySettingsResponse,
|
||||
UpdateAlertDeletionSettingsRequestBody,
|
||||
UpdateAlertDeletionSettingsResponse,
|
||||
} from './types/latest';
|
||||
|
||||
export { updateQueryDelaySettingsBodySchema as updateQueryDelaySettingsBodySchemaV1 } from './schemas/v1';
|
||||
export {
|
||||
updateQueryDelaySettingsBodySchema as updateQueryDelaySettingsBodySchemaV1,
|
||||
updateAlertDeletionSettingsBodySchema as updateAlertDeletionSettingsBodySchemaV1,
|
||||
} from './schemas/v1';
|
||||
export type {
|
||||
UpdateQueryDelaySettingsRequestBody as UpdateQueryDelaySettingsRequestBodyV1,
|
||||
UpdateQueryDelaySettingsResponse as UpdateQueryDelaySettingsResponseV1,
|
||||
UpdateAlertDeletionSettingsRequestBody as UpdateAlertDeletionSettingsRequestBodyV1,
|
||||
UpdateAlertDeletionSettingsResponse as UpdateAlertDeletionSettingsResponseV1,
|
||||
} from './types/v1';
|
||||
|
|
|
@ -10,3 +10,33 @@ import { schema } from '@kbn/config-schema';
|
|||
export const updateQueryDelaySettingsBodySchema = schema.object({
|
||||
delay: schema.number(),
|
||||
});
|
||||
|
||||
export const updateAlertDeletionSettingsBodySchema = schema.object({
|
||||
is_active_alerts_deletion_enabled: schema.boolean({
|
||||
meta: {
|
||||
description: 'Enable deletion of active alerts when set to true',
|
||||
},
|
||||
}),
|
||||
active_alerts_deletion_threshold: schema.number({
|
||||
min: 1,
|
||||
max: 1000,
|
||||
meta: {
|
||||
description:
|
||||
'Threshold (in days) for deleting active alerts older than this value, applies only when deletion is enabled',
|
||||
},
|
||||
}),
|
||||
is_inactive_alerts_deletion_enabled: schema.boolean({
|
||||
meta: {
|
||||
description:
|
||||
'Enable deletion of inactive alerts (recovered/closed/untracked) when set to true',
|
||||
},
|
||||
}),
|
||||
inactive_alerts_deletion_threshold: schema.number({
|
||||
min: 1,
|
||||
max: 1000,
|
||||
meta: {
|
||||
description:
|
||||
'Threshold (in days) for deleting inactive alerts (recovered/closed/untracked) older than this value, applies only when deletion is enabled',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -6,11 +6,24 @@
|
|||
*/
|
||||
|
||||
import type { TypeOf } from '@kbn/config-schema';
|
||||
import type { queryDelaySettingsResponseSchemaV1 } from '../../../response';
|
||||
import type { updateQueryDelaySettingsBodySchemaV1 } from '..';
|
||||
import type {
|
||||
queryDelaySettingsResponseSchemaV1,
|
||||
alertDeletionSettingsResponseSchemaV1,
|
||||
} from '../../../response';
|
||||
import type {
|
||||
updateQueryDelaySettingsBodySchemaV1,
|
||||
updateAlertDeletionSettingsBodySchemaV1,
|
||||
} from '..';
|
||||
|
||||
export type UpdateQueryDelaySettingsRequestBody = TypeOf<
|
||||
typeof updateQueryDelaySettingsBodySchemaV1
|
||||
>;
|
||||
|
||||
export type UpdateQueryDelaySettingsResponse = TypeOf<typeof queryDelaySettingsResponseSchemaV1>;
|
||||
|
||||
export type UpdateAlertDeletionSettingsRequestBody = TypeOf<
|
||||
typeof updateAlertDeletionSettingsBodySchemaV1
|
||||
>;
|
||||
export type UpdateAlertDeletionSettingsResponse = TypeOf<
|
||||
typeof alertDeletionSettingsResponseSchemaV1
|
||||
>;
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
export { queryDelaySettingsResponseSchema } from './schemas/latest';
|
||||
export type { QueryDelaySettingsResponse } from './types/latest';
|
||||
export { alertDeletionSettingsResponseSchema } from './schemas/latest';
|
||||
export type { AlertDeletionSettingsResponse } from './types/latest';
|
||||
|
||||
export { queryDelaySettingsResponseSchema as queryDelaySettingsResponseSchemaV1 } from './schemas/v1';
|
||||
export type { QueryDelaySettingsResponse as QueryDelaySettingsResponseV1 } from './types/v1';
|
||||
export { alertDeletionSettingsResponseSchema as alertDeletionSettingsResponseSchemaV1 } from './schemas/v1';
|
||||
export type { AlertDeletionSettingsResponse as AlertDeletionSettingsResponseV1 } from './types/v1';
|
||||
|
|
|
@ -18,3 +18,18 @@ export const queryDelaySettingsResponseBodySchema = schema.object({
|
|||
export const queryDelaySettingsResponseSchema = schema.object({
|
||||
body: queryDelaySettingsResponseBodySchema,
|
||||
});
|
||||
|
||||
export const alertDeletionSettingsResponseBodySchema = schema.object({
|
||||
active_alerts_deletion_threshold: schema.number({ min: 1, max: 1000 }),
|
||||
is_active_alerts_deletion_enabled: schema.boolean(),
|
||||
inactive_alerts_deletion_threshold: schema.number({ min: 1, max: 1000 }),
|
||||
is_inactive_alerts_deletion_enabled: schema.boolean(),
|
||||
created_at: schema.string(),
|
||||
created_by: schema.nullable(schema.string()),
|
||||
updated_at: schema.string(),
|
||||
updated_by: schema.nullable(schema.string()),
|
||||
});
|
||||
|
||||
export const alertDeletionSettingsResponseSchema = schema.object({
|
||||
body: alertDeletionSettingsResponseBodySchema,
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { TypeOf } from '@kbn/config-schema';
|
||||
import type { queryDelaySettingsResponseSchemaV1 } from '..';
|
||||
import type { alertDeletionSettingsResponseSchemaV1, queryDelaySettingsResponseSchemaV1 } from '..';
|
||||
|
||||
export type QueryDelaySettingsResponse = TypeOf<typeof queryDelaySettingsResponseSchemaV1>;
|
||||
export type AlertDeletionSettingsResponse = TypeOf<typeof alertDeletionSettingsResponseSchemaV1>;
|
||||
|
|
|
@ -46,6 +46,8 @@ import { bulkDisableRulesRoute } from './rule/apis/bulk_disable/bulk_disable_rul
|
|||
import { cloneRuleRoute } from './rule/apis/clone/clone_rule_route';
|
||||
import { getFlappingSettingsRoute } from './get_flapping_settings';
|
||||
import { updateFlappingSettingsRoute } from './update_flapping_settings';
|
||||
import { getAlertDeletionSettingsRoute } from './rules_settings/apis/get/get_alert_deletion_settings';
|
||||
import { updateAlertDeletionSettingsRoute } from './rules_settings/apis/update/update_alert_deletion_settings';
|
||||
import { getRuleTagsRoute } from './rule/apis/tags/get_rule_tags';
|
||||
import { getScheduleFrequencyRoute } from './rule/apis/get_schedule_frequency';
|
||||
import { bulkUntrackAlertsRoute } from './rule/apis/bulk_untrack';
|
||||
|
@ -166,6 +168,8 @@ export function defineRoutes(opts: RouteOptions) {
|
|||
getActionErrorLogRoute(router, licenseState);
|
||||
getFlappingSettingsRoute(router, licenseState);
|
||||
updateFlappingSettingsRoute(router, licenseState);
|
||||
getAlertDeletionSettingsRoute(router, licenseState);
|
||||
updateAlertDeletionSettingsRoute(router, licenseState);
|
||||
runSoonRoute(router, licenseState);
|
||||
healthRoute(router, licenseState, encryptedSavedObjects);
|
||||
getGlobalExecutionKPIRoute(router, licenseState);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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 { httpServiceMock } from '@kbn/core/server/mocks';
|
||||
import { licenseStateMock } from '../../../../lib/license_state.mock';
|
||||
import { mockHandlerArguments } from '../../../_mock_handler_arguments';
|
||||
import type { RulesSettingsClientMock } from '../../../../rules_settings/rules_settings_client.mock';
|
||||
import { rulesSettingsClientMock } from '../../../../rules_settings/rules_settings_client.mock';
|
||||
import { getAlertDeletionSettingsRoute } from './get_alert_deletion_settings';
|
||||
|
||||
let rulesSettingsClient: RulesSettingsClientMock;
|
||||
|
||||
jest.mock('../../../../lib/license_api_access', () => ({
|
||||
verifyApiAccess: jest.fn(),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
rulesSettingsClient = rulesSettingsClientMock.create();
|
||||
});
|
||||
|
||||
describe('getAlertDeletionSettingsRoute', () => {
|
||||
test('gets alert deletion settings', async () => {
|
||||
const licenseState = licenseStateMock.create();
|
||||
const router = httpServiceMock.createRouter();
|
||||
|
||||
getAlertDeletionSettingsRoute(router, licenseState);
|
||||
|
||||
const [config, handler] = router.get.mock.calls[0];
|
||||
|
||||
expect(config).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"options": Object {
|
||||
"access": "internal",
|
||||
},
|
||||
"path": "/internal/alerting/rules/settings/_alert_deletion",
|
||||
"security": Object {
|
||||
"authz": Object {
|
||||
"requiredPrivileges": Array [
|
||||
"read-alert-deletion-settings",
|
||||
],
|
||||
},
|
||||
},
|
||||
"validate": false,
|
||||
}
|
||||
`);
|
||||
|
||||
(rulesSettingsClient.alertDeletion().get as jest.Mock).mockResolvedValue({
|
||||
isActiveAlertsDeletionEnabled: true,
|
||||
isInactiveAlertsDeletionEnabled: false,
|
||||
activeAlertsDeletionThreshold: 10,
|
||||
inactiveAlertsDeletionThreshold: 90,
|
||||
createdBy: 'test name',
|
||||
updatedBy: 'test name',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
const [context, req, res] = mockHandlerArguments({ rulesSettingsClient }, {}, ['ok']);
|
||||
|
||||
await handler(context, req, res);
|
||||
|
||||
expect(rulesSettingsClient.alertDeletion().get).toHaveBeenCalledTimes(1);
|
||||
expect(res.ok).toHaveBeenCalledWith({
|
||||
body: expect.objectContaining({
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: false,
|
||||
active_alerts_deletion_threshold: 10,
|
||||
inactive_alerts_deletion_threshold: 90,
|
||||
created_by: 'test name',
|
||||
updated_by: 'test name',
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { IRouter } from '@kbn/core/server';
|
||||
import type { ILicenseState } from '../../../../lib';
|
||||
import type { AlertingRequestHandlerContext } from '../../../../types';
|
||||
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../../types';
|
||||
import { verifyAccessAndContext } from '../../../lib';
|
||||
import { API_PRIVILEGES } from '../../../../../common';
|
||||
import { transformAlertDeletionSettingsToResponseV1 } from '../../transforms';
|
||||
|
||||
export const getAlertDeletionSettingsRoute = (
|
||||
router: IRouter<AlertingRequestHandlerContext>,
|
||||
licenseState: ILicenseState
|
||||
) => {
|
||||
router.get(
|
||||
{
|
||||
path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/settings/_alert_deletion`,
|
||||
validate: false,
|
||||
security: {
|
||||
authz: {
|
||||
requiredPrivileges: [`${API_PRIVILEGES.READ_ALERT_DELETION_SETTINGS}`],
|
||||
},
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
router.handleLegacyErrors(
|
||||
verifyAccessAndContext(licenseState, async function (context, req, res) {
|
||||
const rulesSettingsClient = (await context.alerting).getRulesSettingsClient();
|
||||
const alertDeletionSettings = await rulesSettingsClient.alertDeletion().get();
|
||||
return res.ok(transformAlertDeletionSettingsToResponseV1(alertDeletionSettings));
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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 { httpServiceMock } from '@kbn/core/server/mocks';
|
||||
import { licenseStateMock } from '../../../../lib/license_state.mock';
|
||||
import { mockHandlerArguments } from '../../../_mock_handler_arguments';
|
||||
import type { RulesSettingsClientMock } from '../../../../rules_settings/rules_settings_client.mock';
|
||||
import { rulesSettingsClientMock } from '../../../../rules_settings/rules_settings_client.mock';
|
||||
import { updateAlertDeletionSettingsRoute } from './update_alert_deletion_settings';
|
||||
|
||||
let rulesSettingsClient: RulesSettingsClientMock;
|
||||
|
||||
jest.mock('../../../../lib/license_api_access', () => ({
|
||||
verifyApiAccess: jest.fn(),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
rulesSettingsClient = rulesSettingsClientMock.create();
|
||||
});
|
||||
|
||||
const mockAlertDeletionSettings = {
|
||||
isActiveAlertsDeletionEnabled: true,
|
||||
isInactiveAlertsDeletionEnabled: true,
|
||||
activeAlertsDeletionThreshold: 90,
|
||||
inactiveAlertsDeletionThreshold: 60,
|
||||
createdBy: 'test name',
|
||||
updatedBy: 'test name',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
describe('updateAlertDeletionSettingsRoute', () => {
|
||||
test('updates alert deletion settings', async () => {
|
||||
const licenseState = licenseStateMock.create();
|
||||
const router = httpServiceMock.createRouter();
|
||||
|
||||
updateAlertDeletionSettingsRoute(router, licenseState);
|
||||
|
||||
const [config, handler] = router.post.mock.calls[0];
|
||||
|
||||
expect(config.path).toMatchInlineSnapshot(
|
||||
`"/internal/alerting/rules/settings/_alert_deletion"`
|
||||
);
|
||||
expect(config.options).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"access": "internal",
|
||||
}
|
||||
`);
|
||||
|
||||
(rulesSettingsClient.alertDeletion().get as jest.Mock).mockResolvedValue(
|
||||
mockAlertDeletionSettings
|
||||
);
|
||||
(rulesSettingsClient.alertDeletion().update as jest.Mock).mockResolvedValue(
|
||||
mockAlertDeletionSettings
|
||||
);
|
||||
|
||||
const updateResult = {
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: true,
|
||||
active_alerts_deletion_threshold: 90,
|
||||
inactive_alerts_deletion_threshold: 60,
|
||||
};
|
||||
|
||||
const [context, req, res] = mockHandlerArguments(
|
||||
{ rulesSettingsClient },
|
||||
{
|
||||
body: updateResult,
|
||||
},
|
||||
['ok']
|
||||
);
|
||||
|
||||
await handler(context, req, res);
|
||||
|
||||
expect(rulesSettingsClient.alertDeletion().update).toHaveBeenCalledTimes(1);
|
||||
expect((rulesSettingsClient.alertDeletion().update as jest.Mock).mock.calls[0])
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"activeAlertsDeletionThreshold": 90,
|
||||
"inactiveAlertsDeletionThreshold": 60,
|
||||
"isActiveAlertsDeletionEnabled": true,
|
||||
"isInactiveAlertsDeletionEnabled": true,
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(res.ok).toHaveBeenCalledWith({
|
||||
body: expect.objectContaining({
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: true,
|
||||
active_alerts_deletion_threshold: 90,
|
||||
inactive_alerts_deletion_threshold: 60,
|
||||
created_by: 'test name',
|
||||
updated_by: 'test name',
|
||||
created_at: expect.any(String),
|
||||
updated_at: expect.any(String),
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { IRouter } from '@kbn/core/server';
|
||||
import { updateAlertDeletionSettingsBodySchemaV1 } from '../../../../../common/routes/rules_settings/apis/update';
|
||||
import type { ILicenseState } from '../../../../lib';
|
||||
import { verifyAccessAndContext } from '../../../lib';
|
||||
import type { AlertingRequestHandlerContext } from '../../../../types';
|
||||
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../../types';
|
||||
import { API_PRIVILEGES } from '../../../../../common';
|
||||
import {
|
||||
transformAlertDeletionSettingsRequestV1,
|
||||
transformAlertDeletionSettingsToResponseV1,
|
||||
} from '../../transforms';
|
||||
|
||||
export const updateAlertDeletionSettingsRoute = (
|
||||
router: IRouter<AlertingRequestHandlerContext>,
|
||||
licenseState: ILicenseState
|
||||
) => {
|
||||
router.post(
|
||||
{
|
||||
path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/settings/_alert_deletion`,
|
||||
validate: {
|
||||
body: updateAlertDeletionSettingsBodySchemaV1,
|
||||
},
|
||||
security: {
|
||||
authz: {
|
||||
requiredPrivileges: [`${API_PRIVILEGES.WRITE_ALERT_DELETION_SETTINGS}`],
|
||||
},
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
router.handleLegacyErrors(
|
||||
verifyAccessAndContext(licenseState, async function (context, req, res) {
|
||||
const rulesSettingsClient = (await context.alerting).getRulesSettingsClient();
|
||||
const body = transformAlertDeletionSettingsRequestV1(req.body);
|
||||
const updatedAlertDeletionSettings = await rulesSettingsClient.alertDeletion().update(body);
|
||||
|
||||
return res.ok(transformAlertDeletionSettingsToResponseV1(updatedAlertDeletionSettings));
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
|
@ -6,5 +6,9 @@
|
|||
*/
|
||||
|
||||
export { transformQueryDelaySettingsToResponse } from './transform_query_delay_settings_to_response/latest';
|
||||
export { transformAlertDeletionSettingsToResponse } from './transform_alert_deletion_settings_to_response/latest';
|
||||
export { transformAlertDeletionSettingsRequest } from './transform_alert_deletion_settings_request/latest';
|
||||
|
||||
export { transformQueryDelaySettingsToResponse as transformQueryDelaySettingsToResponseV1 } from './transform_query_delay_settings_to_response/v1';
|
||||
export { transformAlertDeletionSettingsToResponse as transformAlertDeletionSettingsToResponseV1 } from './transform_alert_deletion_settings_to_response/latest';
|
||||
export { transformAlertDeletionSettingsRequest as transformAlertDeletionSettingsRequestV1 } from './transform_alert_deletion_settings_request/latest';
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './v1';
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { RewriteRequestCase } from '@kbn/actions-types';
|
||||
import type { RulesSettingsAlertDeletionProperties } from '@kbn/alerting-types';
|
||||
|
||||
export const transformAlertDeletionSettingsRequest: RewriteRequestCase<
|
||||
RulesSettingsAlertDeletionProperties
|
||||
> = ({
|
||||
active_alerts_deletion_threshold: activeAlertsDeletionThreshold,
|
||||
is_active_alerts_deletion_enabled: isActiveAlertsDeletionEnabled,
|
||||
inactive_alerts_deletion_threshold: inactiveAlertsDeletionThreshold,
|
||||
is_inactive_alerts_deletion_enabled: isInactiveAlertsDeletionEnabled,
|
||||
}) => {
|
||||
return {
|
||||
activeAlertsDeletionThreshold,
|
||||
isActiveAlertsDeletionEnabled,
|
||||
inactiveAlertsDeletionThreshold,
|
||||
isInactiveAlertsDeletionEnabled,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './v1';
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { RulesSettingsAlertDeletion } from '@kbn/alerting-types';
|
||||
import type { AlertDeletionSettingsResponseV1 } from '../../../../../common/routes/rules_settings/response';
|
||||
|
||||
export const transformAlertDeletionSettingsToResponse = (
|
||||
settings: RulesSettingsAlertDeletion
|
||||
): AlertDeletionSettingsResponseV1 => {
|
||||
return {
|
||||
body: {
|
||||
active_alerts_deletion_threshold: settings.activeAlertsDeletionThreshold,
|
||||
is_active_alerts_deletion_enabled: settings.isActiveAlertsDeletionEnabled,
|
||||
inactive_alerts_deletion_threshold: settings.inactiveAlertsDeletionThreshold,
|
||||
is_inactive_alerts_deletion_enabled: settings.isInactiveAlertsDeletionEnabled,
|
||||
created_at: settings.createdAt,
|
||||
created_by: settings.createdBy,
|
||||
updated_at: settings.updatedAt,
|
||||
updated_by: settings.updatedBy,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -75,7 +75,8 @@
|
|||
"@kbn/core-saved-objects-base-server-internal",
|
||||
"@kbn/core-security-server-mocks",
|
||||
"@kbn/core-http-server-utils",
|
||||
"@kbn/response-ops-rule-params"
|
||||
"@kbn/response-ops-rule-params",
|
||||
"@kbn/actions-types"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
|
|
|
@ -21,8 +21,8 @@ import { updateFlappingSettings } from '../../lib/rule_api/update_flapping_setti
|
|||
import { getQueryDelaySettings } from '../../lib/rule_api/get_query_delay_settings';
|
||||
import { updateQueryDelaySettings } from '../../lib/rule_api/update_query_delay_settings';
|
||||
import { getIsExperimentalFeatureEnabled } from '../../../common/get_experimental_features';
|
||||
import { updateAlertsDeletionSettings } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_deletion_settings/update_alerts_deletion_settings';
|
||||
import { fetchAlertsDeletionSettings } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_deletion_settings/fetch_alerts_deletion_settings';
|
||||
import { updateAlertDeletionSettings } from '@kbn/alerts-ui-shared/src/common/apis/alert_deletion_settings/update_alert_deletion_settings';
|
||||
import { fetchAlertDeletionSettings } from '@kbn/alerts-ui-shared/src/common/apis/alert_deletion_settings/fetch_alert_deletion_settings';
|
||||
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
jest.mock('@kbn/alerts-ui-shared/src/common/apis/fetch_flapping_settings', () => ({
|
||||
|
@ -42,15 +42,15 @@ jest.mock('../../../common/get_experimental_features', () => ({
|
|||
}));
|
||||
|
||||
jest.mock(
|
||||
'@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_deletion_settings/fetch_alerts_deletion_settings',
|
||||
'@kbn/alerts-ui-shared/src/common/apis/alert_deletion_settings/fetch_alert_deletion_settings',
|
||||
() => ({
|
||||
fetchAlertsDeletionSettings: jest.fn(),
|
||||
fetchAlertDeletionSettings: jest.fn(),
|
||||
})
|
||||
);
|
||||
jest.mock(
|
||||
'@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_deletion_settings/update_alerts_deletion_settings',
|
||||
'@kbn/alerts-ui-shared/src/common/apis/alert_deletion_settings/update_alert_deletion_settings',
|
||||
() => ({
|
||||
updateAlertsDeletionSettings: jest.fn(),
|
||||
updateAlertDeletionSettings: jest.fn(),
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -82,13 +82,12 @@ const updateQueryDelaySettingsMock = updateQueryDelaySettings as unknown as jest
|
|||
const getIsExperimentalFeatureEnabledMock = getIsExperimentalFeatureEnabled as jest.MockedFunction<
|
||||
typeof getIsExperimentalFeatureEnabled
|
||||
>;
|
||||
const updateAlertsDeletionSettingsMock =
|
||||
updateAlertsDeletionSettings as unknown as jest.MockedFunction<
|
||||
typeof updateAlertsDeletionSettings
|
||||
>;
|
||||
const updateAlertDeletionSettingsMock =
|
||||
updateAlertDeletionSettings as unknown as jest.MockedFunction<typeof updateAlertDeletionSettings>;
|
||||
|
||||
const fetchAlertsDeletionSettingsMock =
|
||||
fetchAlertsDeletionSettings as unknown as jest.MockedFunction<typeof fetchAlertsDeletionSettings>;
|
||||
const fetchAlertDeletionSettingsMock = fetchAlertDeletionSettings as unknown as jest.MockedFunction<
|
||||
typeof fetchAlertDeletionSettings
|
||||
>;
|
||||
|
||||
const mockFlappingSetting: RulesSettingsFlapping = {
|
||||
enabled: true,
|
||||
|
@ -189,7 +188,7 @@ describe('rules_settings_modal', () => {
|
|||
updateFlappingSettingsMock.mockResolvedValue(mockFlappingSetting);
|
||||
getQueryDelaySettingsMock.mockResolvedValue(mockQueryDelaySetting);
|
||||
updateQueryDelaySettingsMock.mockResolvedValue(mockQueryDelaySetting);
|
||||
fetchAlertsDeletionSettingsMock.mockResolvedValue(mockAlertDeletionSetting);
|
||||
fetchAlertDeletionSettingsMock.mockResolvedValue(mockAlertDeletionSetting);
|
||||
getIsExperimentalFeatureEnabledMock.mockReturnValue(true);
|
||||
});
|
||||
|
||||
|
@ -273,7 +272,7 @@ describe('rules_settings_modal', () => {
|
|||
|
||||
expect(modalProps.onClose).toHaveBeenCalledTimes(1);
|
||||
expect(updateFlappingSettingsMock).not.toHaveBeenCalled();
|
||||
expect(updateAlertsDeletionSettingsMock).not.toHaveBeenCalled();
|
||||
expect(updateAlertDeletionSettingsMock).not.toHaveBeenCalled();
|
||||
expect(modalProps.onSave).not.toHaveBeenCalled();
|
||||
|
||||
expect(screen.queryByTestId('centerJustifiedSpinner')).toBe(null);
|
||||
|
@ -283,7 +282,7 @@ describe('rules_settings_modal', () => {
|
|||
|
||||
expect(fetchFlappingSettingsMock).toHaveBeenCalledTimes(1);
|
||||
expect(getQueryDelaySettingsMock).toHaveBeenCalledTimes(1);
|
||||
expect(fetchAlertsDeletionSettingsMock).toHaveBeenCalledTimes(1);
|
||||
expect(fetchAlertDeletionSettingsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('should prevent statusChangeThreshold from being greater than lookBackWindow', async () => {
|
||||
|
@ -507,7 +506,7 @@ describe('rules_settings_modal', () => {
|
|||
jest.clearAllMocks();
|
||||
user = userEvent.setup();
|
||||
getIsExperimentalFeatureEnabledMock.mockReturnValue(true);
|
||||
updateAlertsDeletionSettingsMock.mockResolvedValue(mockAlertDeletionSetting);
|
||||
updateAlertDeletionSettingsMock.mockResolvedValue(mockAlertDeletionSetting);
|
||||
getQueryDelaySettingsMock.mockResolvedValue(mockQueryDelaySetting);
|
||||
});
|
||||
|
||||
|
@ -535,9 +534,9 @@ describe('rules_settings_modal', () => {
|
|||
expect(modalProps.setUpdatingRulesSettings).toHaveBeenCalledWith(true);
|
||||
});
|
||||
expect(modalProps.onClose).toHaveBeenCalledTimes(1);
|
||||
expect(updateAlertsDeletionSettingsMock).toHaveBeenCalledWith(
|
||||
expect(updateAlertDeletionSettingsMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
alertsDeletionSettings: {
|
||||
alertDeletionSettings: {
|
||||
isActiveAlertsDeletionEnabled: true,
|
||||
isInactiveAlertsDeletionEnabled: true,
|
||||
activeAlertsDeletionThreshold: 5,
|
||||
|
@ -551,7 +550,7 @@ describe('rules_settings_modal', () => {
|
|||
});
|
||||
|
||||
test('should show error message if it fails', async () => {
|
||||
fetchAlertsDeletionSettingsMock.mockRejectedValue('failed!');
|
||||
fetchAlertDeletionSettingsMock.mockRejectedValue('failed!');
|
||||
render(<RulesSettingsModalWithProviders {...modalProps} />);
|
||||
await waitForModalLoad();
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
|
|||
useResettableState<RulesSettingsQueryDelayProperties>();
|
||||
|
||||
const [
|
||||
alertsDeletionSettings,
|
||||
alertDeletionSettings,
|
||||
hasAlertsDeletionChanged,
|
||||
setAlertsDeletionSettings,
|
||||
resetAlertsDeletionSettings,
|
||||
|
@ -161,7 +161,7 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
|
|||
http,
|
||||
enabled: isVisible && isAlertsDeletionSettingsEnabled,
|
||||
onSuccess: (fetchedSettings) => {
|
||||
if (!alertsDeletionSettings) {
|
||||
if (!alertDeletionSettings) {
|
||||
setAlertsDeletionSettings(
|
||||
{
|
||||
isActiveAlertsDeletionEnabled: fetchedSettings.isActiveAlertsDeletionEnabled,
|
||||
|
@ -236,11 +236,11 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
|
|||
}
|
||||
|
||||
if (setting === 'alertDeletion') {
|
||||
if (!alertsDeletionSettings) {
|
||||
if (!alertDeletionSettings) {
|
||||
return;
|
||||
}
|
||||
const newSettings = {
|
||||
...alertsDeletionSettings,
|
||||
...alertDeletionSettings,
|
||||
[key]: value,
|
||||
};
|
||||
setAlertsDeletionSettings(newSettings);
|
||||
|
@ -258,8 +258,8 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
|
|||
setQueryDelaySettings(queryDelaySettings!, true);
|
||||
}
|
||||
if (canWriteAlertsDeletionSettings && hasAlertsDeletionChanged) {
|
||||
updatedSettings.alertDeletion = alertsDeletionSettings;
|
||||
setAlertsDeletionSettings(alertsDeletionSettings!, true);
|
||||
updatedSettings.alertDeletion = alertDeletionSettings;
|
||||
setAlertsDeletionSettings(alertDeletionSettings!, true);
|
||||
}
|
||||
|
||||
mutate(updatedSettings);
|
||||
|
@ -304,7 +304,7 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => {
|
|||
<EuiSpacer />
|
||||
<RulesSettingsAlertsDeletionSection
|
||||
onChange={(key, value) => handleSettingsChange('alertDeletion', key, value)}
|
||||
settings={alertsDeletionSettings}
|
||||
settings={alertDeletionSettings}
|
||||
canWrite={canWriteAlertsDeletionSettings}
|
||||
hasError={hasAlertsDeletionError}
|
||||
/>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import type { RulesSettingsProperties } from '@kbn/alerting-plugin/common';
|
||||
import { updateAlertsDeletionSettings } from '@kbn/alerts-ui-shared/src/common/apis/fetch_alerts_deletion_settings';
|
||||
import { updateAlertDeletionSettings } from '@kbn/alerts-ui-shared/src/common/apis/alert_deletion_settings';
|
||||
import { useKibana } from '../../common/lib/kibana';
|
||||
import { updateFlappingSettings } from '../lib/rule_api/update_flapping_settings';
|
||||
import { updateQueryDelaySettings } from '../lib/rule_api/update_query_delay_settings';
|
||||
|
@ -39,7 +39,7 @@ export const useUpdateRuleSettings = (props: UseUpdateRuleSettingsProps) => {
|
|||
|
||||
if (settings.alertDeletion) {
|
||||
updates.push(
|
||||
updateAlertsDeletionSettings({ http, alertsDeletionSettings: settings.alertDeletion })
|
||||
updateAlertDeletionSettings({ http, alertDeletionSettings: settings.alertDeletion })
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { DEFAULT_ALERT_DELETION_SETTINGS } from '@kbn/alerting-plugin/common';
|
||||
import { UserAtSpaceScenarios } from '../../../scenarios';
|
||||
import { getUrlPrefix, resetRulesSettings } from '../../../../common/lib';
|
||||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function getAlertDeletionSettingsTests({ getService }: FtrProviderContext) {
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
|
||||
describe('getAlertDeletionSettings', () => {
|
||||
beforeEach(async () => {
|
||||
await resetRulesSettings(supertestWithoutAuth, 'space1');
|
||||
await resetRulesSettings(supertestWithoutAuth, 'space2');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await resetRulesSettings(supertestWithoutAuth, 'space1');
|
||||
await resetRulesSettings(supertestWithoutAuth, 'space2');
|
||||
});
|
||||
|
||||
for (const scenario of UserAtSpaceScenarios) {
|
||||
const { user, space } = scenario;
|
||||
describe(scenario.id, () => {
|
||||
it('should handle get alert deletion request appropriately', async () => {
|
||||
const response = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix(space.id)}/internal/alerting/rules/settings/_alert_deletion`)
|
||||
.auth(user.username, user.password);
|
||||
|
||||
switch (scenario.id) {
|
||||
case 'no_kibana_privileges at space1':
|
||||
case 'space_1_all at space2':
|
||||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
case 'space_1_all_alerts_none_actions at space1':
|
||||
expect(response.statusCode).to.eql(403);
|
||||
expect(response.body).to.eql({
|
||||
error: 'Forbidden',
|
||||
message:
|
||||
'API [GET /internal/alerting/rules/settings/_alert_deletion] is unauthorized for user, this action is granted by the Kibana privileges [read-alert-deletion-settings]',
|
||||
statusCode: 403,
|
||||
});
|
||||
break;
|
||||
case 'global_read at space1':
|
||||
case 'superuser at space1':
|
||||
case 'space_1_all at space1':
|
||||
expect(response.statusCode).to.eql(200);
|
||||
expect(response.body.is_active_alerts_deletion_enabled).to.eql(
|
||||
DEFAULT_ALERT_DELETION_SETTINGS.isActiveAlertsDeletionEnabled
|
||||
);
|
||||
expect(response.body.is_inactive_alerts_deletion_enabled).to.eql(
|
||||
DEFAULT_ALERT_DELETION_SETTINGS.isInactiveAlertsDeletionEnabled
|
||||
);
|
||||
expect(response.body.active_alerts_deletion_threshold).to.eql(
|
||||
DEFAULT_ALERT_DELETION_SETTINGS.activeAlertsDeletionThreshold
|
||||
);
|
||||
expect(response.body.inactive_alerts_deletion_threshold).to.eql(
|
||||
DEFAULT_ALERT_DELETION_SETTINGS.inactiveAlertsDeletionThreshold
|
||||
);
|
||||
expect(response.body.updated_by).to.be.a('string');
|
||||
expect(response.body.created_by).to.be.a('string');
|
||||
expect(Date.parse(response.body.created_at)).to.be.greaterThan(0);
|
||||
expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -34,6 +34,8 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC
|
|||
loadTestFile(require.resolve('./get_query_delay_settings'));
|
||||
loadTestFile(require.resolve('./update_query_delay_settings'));
|
||||
loadTestFile(require.resolve('./resolve'));
|
||||
loadTestFile(require.resolve('./get_alert_deletion_settings'));
|
||||
loadTestFile(require.resolve('./update_alert_deletion_settings'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { UserAtSpaceScenarios, Superuser } from '../../../scenarios';
|
||||
import { getUrlPrefix, resetRulesSettings } from '../../../../common/lib';
|
||||
import { FtrProviderContext } from '../../../../common/ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function updateAlertDeletionSettingsTest({ getService }: FtrProviderContext) {
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
|
||||
describe('updateAlertDeletionSettings', () => {
|
||||
afterEach(async () => {
|
||||
await resetRulesSettings(supertestWithoutAuth, 'space1');
|
||||
await resetRulesSettings(supertestWithoutAuth, 'space2');
|
||||
});
|
||||
|
||||
for (const scenario of UserAtSpaceScenarios) {
|
||||
const { user, space } = scenario;
|
||||
describe(scenario.id, () => {
|
||||
it('should handle update alert deletion settings request appropriately', async () => {
|
||||
const response = await supertestWithoutAuth
|
||||
.post(`${getUrlPrefix(space.id)}/internal/alerting/rules/settings/_alert_deletion`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(user.username, user.password)
|
||||
.send({
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: false,
|
||||
active_alerts_deletion_threshold: 70,
|
||||
inactive_alerts_deletion_threshold: 50,
|
||||
});
|
||||
|
||||
switch (scenario.id) {
|
||||
case 'no_kibana_privileges at space1':
|
||||
case 'global_read at space1':
|
||||
case 'space_1_all at space2':
|
||||
case 'space_1_all_with_restricted_fixture at space1':
|
||||
case 'space_1_all_alerts_none_actions at space1':
|
||||
expect(response.statusCode).to.eql(403);
|
||||
expect(response.body).to.eql({
|
||||
error: 'Forbidden',
|
||||
message:
|
||||
'API [POST /internal/alerting/rules/settings/_alert_deletion] is unauthorized for user, this action is granted by the Kibana privileges [write-alert-deletion-settings]',
|
||||
statusCode: 403,
|
||||
});
|
||||
break;
|
||||
case 'superuser at space1':
|
||||
case 'space_1_all at space1':
|
||||
expect(response.statusCode).to.eql(200);
|
||||
expect(response.body.is_active_alerts_deletion_enabled).to.eql(true);
|
||||
expect(response.body.is_inactive_alerts_deletion_enabled).to.eql(false);
|
||||
expect(response.body.active_alerts_deletion_threshold).to.eql(70);
|
||||
expect(response.body.inactive_alerts_deletion_threshold).to.eql(50);
|
||||
expect(Date.parse(response.body.created_at)).to.be.greaterThan(0);
|
||||
expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('should error if provided with invalid inputs', async () => {
|
||||
let response = await supertestWithoutAuth
|
||||
.post(`${getUrlPrefix('space1')}/internal/alerting/rules/settings/_alert_deletion`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(Superuser.username, Superuser.password)
|
||||
.send({
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: false,
|
||||
active_alerts_deletion_threshold: 2000,
|
||||
inactive_alerts_deletion_threshold: 50,
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.message).to.eql(
|
||||
'[request body.active_alerts_deletion_threshold]: Value must be equal to or lower than [1000].'
|
||||
);
|
||||
|
||||
response = await supertestWithoutAuth
|
||||
.post(`${getUrlPrefix('space1')}/internal/alerting/rules/settings/_alert_deletion`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(Superuser.username, Superuser.password)
|
||||
.send({
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: false,
|
||||
active_alerts_deletion_threshold: 70,
|
||||
inactive_alerts_deletion_threshold: 2000,
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.message).to.eql(
|
||||
'[request body.inactive_alerts_deletion_threshold]: Value must be equal to or lower than [1000].'
|
||||
);
|
||||
|
||||
response = await supertestWithoutAuth
|
||||
.post(`${getUrlPrefix('space1')}/internal/alerting/rules/settings/_alert_deletion`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(Superuser.username, Superuser.password)
|
||||
.send({
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: false,
|
||||
active_alerts_deletion_threshold: 0,
|
||||
inactive_alerts_deletion_threshold: 20,
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.message).to.eql(
|
||||
'[request body.active_alerts_deletion_threshold]: Value must be equal to or greater than [1].'
|
||||
);
|
||||
|
||||
response = await supertestWithoutAuth
|
||||
.post(`${getUrlPrefix('space1')}/internal/alerting/rules/settings/_alert_deletion`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(Superuser.username, Superuser.password)
|
||||
.send({
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: false,
|
||||
active_alerts_deletion_threshold: 20,
|
||||
inactive_alerts_deletion_threshold: 0,
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.message).to.eql(
|
||||
'[request body.inactive_alerts_deletion_threshold]: Value must be equal to or greater than [1].'
|
||||
);
|
||||
});
|
||||
|
||||
describe('updateAlertDeletionSettings for other spaces', () => {
|
||||
it('should update specific isolated settings depending on space', async () => {
|
||||
// Update the rules setting in space1
|
||||
const postResponse = await supertestWithoutAuth
|
||||
.post(`${getUrlPrefix('space1')}/internal/alerting/rules/settings/_alert_deletion`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.auth(Superuser.username, Superuser.password)
|
||||
.send({
|
||||
is_active_alerts_deletion_enabled: true,
|
||||
is_inactive_alerts_deletion_enabled: true,
|
||||
active_alerts_deletion_threshold: 10,
|
||||
inactive_alerts_deletion_threshold: 100,
|
||||
});
|
||||
|
||||
expect(postResponse.statusCode).to.eql(200);
|
||||
expect(postResponse.body.is_active_alerts_deletion_enabled).to.eql(true);
|
||||
expect(postResponse.body.is_inactive_alerts_deletion_enabled).to.eql(true);
|
||||
expect(postResponse.body.active_alerts_deletion_threshold).to.eql(10);
|
||||
expect(postResponse.body.inactive_alerts_deletion_threshold).to.eql(100);
|
||||
|
||||
// Get the rules settings in space2
|
||||
const getResponse = await supertestWithoutAuth
|
||||
.get(`${getUrlPrefix('space2')}/internal/alerting/rules/settings/_alert_deletion`)
|
||||
.auth(Superuser.username, Superuser.password);
|
||||
|
||||
expect(getResponse.statusCode).to.eql(200);
|
||||
expect(getResponse.body.is_active_alerts_deletion_enabled).to.eql(false);
|
||||
expect(getResponse.body.is_active_alerts_deletion_enabled).to.eql(false);
|
||||
expect(getResponse.body.active_alerts_deletion_threshold).to.eql(90);
|
||||
expect(getResponse.body.inactive_alerts_deletion_threshold).to.eql(90);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue