mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[ResponseOps][Rules] Fix KQL wildcards in alerts filtering in actions and MW (#183901)
## Summary This PR 1. Show KQL error messages to the UI 2. Respect the `query:allowLeadingWildcards` advance setting in the MW ## Testing Verify that the bug except the one about DSL filtering described in https://github.com/elastic/kibana/issues/168600 is fixed. Also, test the following scenarios. ### Actions **Error**: 1. Go to Stack -> Advanced setting and disable `query:allowLeadingWildcards` 2. Create a rule with an action and make the action conditional by toggling the "If alert matches query" 3. Add a KQL like `kibana.alert.instance.id : *development`. The leading `*` is important 4. Save the rule. You should see a toaster error with a message about `query:allowLeadingWildcards` **Happy path**: 1. Go to Stack -> Advanced setting and make sure `query:allowLeadingWildcards` is enabled 2. Create a rule with an action and make the action conditional by toggling the "If alert matches query" 3. Add a KQL like `kibana.alert.instance.id : *development`. The leading `*` is important 4. Save the rule. You should not see any errors. ### Maintenance Windows **Error**: 1. Go to Stack -> Advanced setting and disable `query:allowLeadingWildcards` 2. Go to Stack -> Maintenance Windows -> Create window 3. Toggle "Filter alerts" and add a KQL like `kibana.alert.instance.id : *development`. The leading `*` is important 4. Create the MW. You should see a toaster error with a message about `query:allowLeadingWildcards` **Happy path**: 1. Go to Stack -> Advanced setting and make sure `query:allowLeadingWildcards` is enabled 2. Go to Stack -> Maintenance Windows -> Create window 3. Toggle "Filter alerts" and add a KQL like `kibana.alert.instance.id : *development`. The leading `*` is important 4. Create the MW. You should not see any errors. Fixes: https://github.com/elastic/kibana/issues/168600 ### Checklist Delete any items that are not applicable to this PR. - [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 - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ## Release notes Show errors about invalid KQL in conditional actions and respect the `query:allowLeadingWildcards` advanced setting in maintenance windows
This commit is contained in:
parent
690690ea21
commit
b9e47025fa
25 changed files with 218 additions and 41 deletions
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { Logger, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import type { IUiSettingsClient, Logger, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import { FilterStateStore } from '@kbn/es-query';
|
||||
import { RRuleParams } from './rrule_type';
|
||||
|
||||
|
@ -76,6 +76,7 @@ export type MaintenanceWindowCreateBody = Omit<
|
|||
>;
|
||||
|
||||
export interface MaintenanceWindowClientContext {
|
||||
readonly uiSettings: IUiSettingsClient;
|
||||
getModificationMetadata: () => Promise<MaintenanceWindowModificationMetadata>;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
logger: Logger;
|
||||
|
|
|
@ -79,7 +79,25 @@ describe('useCreateMaintenanceWindow', () => {
|
|||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockAddDanger).toBeCalledWith('Failed to create maintenance window.')
|
||||
expect(mockAddDanger).toBeCalledWith('Failed to create maintenance window')
|
||||
);
|
||||
});
|
||||
|
||||
it('should show 400 error messages', async () => {
|
||||
createMaintenanceWindow.mockRejectedValue({
|
||||
body: { statusCode: 400, message: 'Bad request' },
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useCreateMaintenanceWindow(), {
|
||||
wrapper: appMockRenderer.AppWrapper,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.mutate(maintenanceWindow);
|
||||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(mockAddDanger).toBeCalledWith('Failed to create maintenance window: Bad request')
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,6 +13,19 @@ import type { KibanaServerError } from '@kbn/kibana-utils-plugin/public';
|
|||
import { useKibana } from '../utils/kibana_react';
|
||||
import { createMaintenanceWindow, CreateParams } from '../services/maintenance_windows_api/create';
|
||||
|
||||
const onErrorWithMessage = (message: string) =>
|
||||
i18n.translate('xpack.alerting.maintenanceWindowsCreateFailureWithMessage', {
|
||||
defaultMessage: 'Failed to create maintenance window: {message}',
|
||||
values: { message },
|
||||
});
|
||||
|
||||
const onErrorWithoutMessage = i18n.translate(
|
||||
'xpack.alerting.maintenanceWindowsCreateFailureWithoutMessage',
|
||||
{
|
||||
defaultMessage: 'Failed to create maintenance window',
|
||||
}
|
||||
);
|
||||
|
||||
interface UseCreateMaintenanceWindowProps {
|
||||
onError?: (error: IHttpFetchError<KibanaServerError>) => void;
|
||||
}
|
||||
|
@ -41,10 +54,11 @@ export function useCreateMaintenanceWindow(props?: UseCreateMaintenanceWindowPro
|
|||
);
|
||||
},
|
||||
onError: (error: IHttpFetchError<KibanaServerError>) => {
|
||||
const getDefaultErrorMessage = (message?: string): string =>
|
||||
!message ? onErrorWithoutMessage : onErrorWithMessage(message);
|
||||
|
||||
toasts.addDanger(
|
||||
i18n.translate('xpack.alerting.maintenanceWindowsCreateFailure', {
|
||||
defaultMessage: 'Failed to create maintenance window.',
|
||||
})
|
||||
getDefaultErrorMessage(error.body?.statusCode === 400 ? error.body?.message : '')
|
||||
);
|
||||
onError?.(error);
|
||||
},
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
import moment from 'moment-timezone';
|
||||
import { Frequency } from '@kbn/rrule';
|
||||
import { archiveMaintenanceWindow } from './archive_maintenance_window';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsUpdateResponse, SavedObject } from '@kbn/core/server';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
|
@ -18,6 +22,7 @@ import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/te
|
|||
import type { MaintenanceWindow } from '../../types';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const firstTimestamp = '2023-02-26T00:00:00.000Z';
|
||||
const secondTimestamp = '2023-03-26T00:00:00.000Z';
|
||||
|
@ -33,6 +38,7 @@ const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
|||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - archive', () => {
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
|
||||
import { bulkGetMaintenanceWindows } from './bulk_get_maintenance_windows';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
|
@ -15,11 +19,13 @@ import {
|
|||
import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/test_helpers';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - get', () => {
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
import moment from 'moment-timezone';
|
||||
import { createMaintenanceWindow } from './create_maintenance_window';
|
||||
import { CreateMaintenanceWindowParams } from './types';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
|
@ -19,6 +23,7 @@ import type { MaintenanceWindow } from '../../types';
|
|||
import { FilterStateStore } from '@kbn/es-query';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const updatedMetadata = {
|
||||
createdAt: '2023-03-26T00:00:00.000Z',
|
||||
|
@ -31,6 +36,7 @@ const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
|||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - create', () => {
|
||||
|
@ -223,6 +229,21 @@ describe('MaintenanceWindowClient - create', () => {
|
|||
).toMatchInlineSnapshot(
|
||||
`"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"_id\\":\\"'1234'\\"}}],\\"minimum_should_match\\":1}},{\\"match_phrase\\":{\\"kibana.alert.action_group\\":\\"test\\"}}],\\"should\\":[],\\"must_not\\":[]}}"`
|
||||
);
|
||||
|
||||
expect(uiSettings.get).toHaveBeenCalledTimes(3);
|
||||
expect(uiSettings.get.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"query:allowLeadingWildcards",
|
||||
],
|
||||
Array [
|
||||
"query:queryString:options",
|
||||
],
|
||||
Array [
|
||||
"courier:ignoreFilterIfFieldNotInIndex",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should throw if trying to create a maintenance window with invalid scoped query', async () => {
|
||||
|
|
|
@ -9,6 +9,7 @@ import moment from 'moment';
|
|||
import Boom from '@hapi/boom';
|
||||
import { SavedObjectsUtils } from '@kbn/core/server';
|
||||
import { buildEsQuery, Filter } from '@kbn/es-query';
|
||||
import { getEsQueryConfig } from '../../../../lib/get_es_query_config';
|
||||
import { generateMaintenanceWindowEvents } from '../../lib/generate_maintenance_window_events';
|
||||
import type { MaintenanceWindowClientContext } from '../../../../../common';
|
||||
import { getScopedQueryErrorMessage } from '../../../../../common';
|
||||
|
@ -26,8 +27,9 @@ export async function createMaintenanceWindow(
|
|||
params: CreateMaintenanceWindowParams
|
||||
): Promise<MaintenanceWindow> {
|
||||
const { data } = params;
|
||||
const { savedObjectsClient, getModificationMetadata, logger } = context;
|
||||
const { savedObjectsClient, getModificationMetadata, logger, uiSettings } = context;
|
||||
const { title, duration, rRule, categoryIds, scopedQuery } = data;
|
||||
const esQueryConfig = await getEsQueryConfig(uiSettings);
|
||||
|
||||
try {
|
||||
createMaintenanceWindowParamsSchema.validate(params);
|
||||
|
@ -36,15 +38,18 @@ export async function createMaintenanceWindow(
|
|||
}
|
||||
|
||||
let scopedQueryWithGeneratedValue = scopedQuery;
|
||||
|
||||
try {
|
||||
if (scopedQuery) {
|
||||
const dsl = JSON.stringify(
|
||||
buildEsQuery(
|
||||
undefined,
|
||||
[{ query: scopedQuery.kql, language: 'kuery' }],
|
||||
scopedQuery.filters as Filter[]
|
||||
scopedQuery.filters as Filter[],
|
||||
esQueryConfig
|
||||
)
|
||||
);
|
||||
|
||||
scopedQueryWithGeneratedValue = {
|
||||
...scopedQuery,
|
||||
dsl,
|
||||
|
|
|
@ -6,18 +6,24 @@
|
|||
*/
|
||||
|
||||
import { deleteMaintenanceWindow } from './delete_maintenance_window';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE,
|
||||
} from '../../../../../common';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - delete', () => {
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
|
||||
import { findMaintenanceWindows } from './find_maintenance_windows';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsFindResponse } from '@kbn/core/server';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
|
@ -15,11 +19,13 @@ import {
|
|||
import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/test_helpers';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - find', () => {
|
||||
|
|
|
@ -8,7 +8,11 @@
|
|||
import moment from 'moment-timezone';
|
||||
import { Frequency } from '@kbn/rrule';
|
||||
import { finishMaintenanceWindow } from './finish_maintenance_window';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsUpdateResponse, SavedObject } from '@kbn/core/server';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
|
@ -18,6 +22,7 @@ import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/te
|
|||
import type { MaintenanceWindow } from '../../types';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const firstTimestamp = '2023-02-26T00:00:00.000Z';
|
||||
|
||||
|
@ -32,6 +37,7 @@ const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
|||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - finish', () => {
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
|
||||
import { getMaintenanceWindow } from './get_maintenance_window';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
|
@ -15,11 +19,13 @@ import {
|
|||
import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/test_helpers';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - get', () => {
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
|
||||
import { getActiveMaintenanceWindows } from './get_active_maintenance_windows';
|
||||
import { toElasticsearchQuery } from '@kbn/es-query';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SavedObjectsFindResponse } from '@kbn/core/server';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
|
@ -16,11 +20,13 @@ import {
|
|||
import { getMockMaintenanceWindow } from '../../../../data/maintenance_window/test_helpers';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - getActiveMaintenanceWindows', () => {
|
||||
|
|
|
@ -9,7 +9,11 @@ import moment from 'moment-timezone';
|
|||
import { Frequency } from '@kbn/rrule';
|
||||
import { updateMaintenanceWindow } from './update_maintenance_window';
|
||||
import { UpdateMaintenanceWindowParams } from './types';
|
||||
import { savedObjectsClientMock, loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import {
|
||||
savedObjectsClientMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { SavedObject } from '@kbn/core/server';
|
||||
import {
|
||||
MaintenanceWindowClientContext,
|
||||
|
@ -20,6 +24,7 @@ import type { MaintenanceWindow } from '../../types';
|
|||
import { FilterStateStore } from '@kbn/es-query';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const uiSettings = uiSettingsServiceMock.createClient();
|
||||
|
||||
const firstTimestamp = '2023-02-26T00:00:00.000Z';
|
||||
const secondTimestamp = '2023-03-26T00:00:00.000Z';
|
||||
|
@ -47,6 +52,7 @@ const mockContext: jest.Mocked<MaintenanceWindowClientContext> = {
|
|||
logger: loggingSystemMock.create().get(),
|
||||
getModificationMetadata: jest.fn(),
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
describe('MaintenanceWindowClient - update', () => {
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 { uiSettingsServiceMock } from '@kbn/core-ui-settings-server-mocks';
|
||||
import { getEsQueryConfig } from './get_es_query_config';
|
||||
|
||||
describe('getEsQueryConfig', () => {
|
||||
const uiSettingsClient = uiSettingsServiceMock.createClient();
|
||||
|
||||
it('should get the es query config correctly', async () => {
|
||||
const settings = await getEsQueryConfig(uiSettingsClient);
|
||||
|
||||
expect(settings).toEqual({
|
||||
allowLeadingWildcards: false,
|
||||
ignoreFilterIfFieldNotInIndex: false,
|
||||
queryStringOptions: false,
|
||||
});
|
||||
});
|
||||
});
|
23
x-pack/plugins/alerting/server/lib/get_es_query_config.ts
Normal file
23
x-pack/plugins/alerting/server/lib/get_es_query_config.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { IUiSettingsClient } from '@kbn/core/server';
|
||||
import { UI_SETTINGS } from '@kbn/data-plugin/server';
|
||||
|
||||
export async function getEsQueryConfig(uiSettings: IUiSettingsClient) {
|
||||
const allowLeadingWildcards = await uiSettings.get(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS);
|
||||
const queryStringOptions = await uiSettings.get(UI_SETTINGS.QUERY_STRING_OPTIONS);
|
||||
const ignoreFilterIfFieldNotInIndex = await uiSettings.get(
|
||||
UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX
|
||||
);
|
||||
|
||||
return {
|
||||
allowLeadingWildcards,
|
||||
queryStringOptions,
|
||||
ignoreFilterIfFieldNotInIndex,
|
||||
};
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { Logger, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import { IUiSettingsClient, Logger, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
|
||||
import { createMaintenanceWindow } from '../application/maintenance_window/methods/create/create_maintenance_window';
|
||||
import type { CreateMaintenanceWindowParams } from '../application/maintenance_window/methods/create/types';
|
||||
|
@ -33,6 +33,7 @@ import {
|
|||
import type { MaintenanceWindow } from '../application/maintenance_window/types';
|
||||
|
||||
export interface MaintenanceWindowClientConstructorOptions {
|
||||
readonly uiSettings: IUiSettingsClient;
|
||||
readonly logger: Logger;
|
||||
readonly savedObjectsClient: SavedObjectsClientContract;
|
||||
readonly getUserName: () => Promise<string | null>;
|
||||
|
@ -52,6 +53,7 @@ export class MaintenanceWindowClient {
|
|||
logger: this.logger,
|
||||
savedObjectsClient: this.savedObjectsClient,
|
||||
getModificationMetadata: this.getModificationMetadata.bind(this),
|
||||
uiSettings: options.uiSettings,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
savedObjectsClientMock,
|
||||
savedObjectsServiceMock,
|
||||
loggingSystemMock,
|
||||
uiSettingsServiceMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
import { AuthenticatedUser } from '@kbn/security-plugin/common';
|
||||
import { securityMock } from '@kbn/security-plugin/server/mocks';
|
||||
|
@ -23,12 +24,13 @@ jest.mock('./maintenance_window_client');
|
|||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const savedObjectsService = savedObjectsServiceMock.createInternalStartContract();
|
||||
|
||||
const securityPluginStart = securityMock.createStart();
|
||||
const uiSettings = uiSettingsServiceMock.createStartContract();
|
||||
|
||||
const maintenanceWindowClientFactoryParams: jest.Mocked<MaintenanceWindowClientFactoryOpts> = {
|
||||
logger: loggingSystemMock.create().get(),
|
||||
savedObjectsService,
|
||||
uiSettings,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
Logger,
|
||||
SavedObjectsServiceStart,
|
||||
SECURITY_EXTENSION_ID,
|
||||
UiSettingsServiceStart,
|
||||
} from '@kbn/core/server';
|
||||
import { SecurityPluginStart } from '@kbn/security-plugin/server';
|
||||
import { MaintenanceWindowClient } from './maintenance_window_client';
|
||||
|
@ -19,6 +20,7 @@ export interface MaintenanceWindowClientFactoryOpts {
|
|||
logger: Logger;
|
||||
savedObjectsService: SavedObjectsServiceStart;
|
||||
securityPluginStart?: SecurityPluginStart;
|
||||
uiSettings: UiSettingsServiceStart;
|
||||
}
|
||||
|
||||
export class MaintenanceWindowClientFactory {
|
||||
|
@ -26,6 +28,7 @@ export class MaintenanceWindowClientFactory {
|
|||
private logger!: Logger;
|
||||
private savedObjectsService!: SavedObjectsServiceStart;
|
||||
private securityPluginStart?: SecurityPluginStart;
|
||||
private uiSettings!: UiSettingsServiceStart;
|
||||
|
||||
public initialize(options: MaintenanceWindowClientFactoryOpts) {
|
||||
if (this.isInitialized) {
|
||||
|
@ -35,6 +38,7 @@ export class MaintenanceWindowClientFactory {
|
|||
this.logger = options.logger;
|
||||
this.savedObjectsService = options.savedObjectsService;
|
||||
this.securityPluginStart = options.securityPluginStart;
|
||||
this.uiSettings = options.uiSettings;
|
||||
}
|
||||
|
||||
private createMaintenanceWindowClient(request: KibanaRequest, withAuth: boolean) {
|
||||
|
@ -44,9 +48,12 @@ export class MaintenanceWindowClientFactory {
|
|||
...(withAuth ? {} : { excludedExtensions: [SECURITY_EXTENSION_ID] }),
|
||||
});
|
||||
|
||||
const uiSettingClient = this.uiSettings.asScopedToClient(savedObjectsClient);
|
||||
|
||||
return new MaintenanceWindowClient({
|
||||
logger: this.logger,
|
||||
savedObjectsClient,
|
||||
uiSettings: uiSettingClient,
|
||||
async getUserName() {
|
||||
if (!securityPluginStart || !request) {
|
||||
return null;
|
||||
|
|
|
@ -556,6 +556,7 @@ export class AlertingPlugin {
|
|||
logger: this.logger,
|
||||
savedObjectsService: core.savedObjects,
|
||||
securityPluginStart: plugins.security,
|
||||
uiSettings: core.uiSettings,
|
||||
});
|
||||
|
||||
const getRulesClientWithRequest = (request: KibanaRequest) => {
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`addGeneratedActionValues() throws error if KQL is not valid: "Error creating DSL query: invalid KQL" 1`] = `"Error creating DSL query: invalid KQL"`;
|
|
@ -32,14 +32,16 @@ describe('addGeneratedActionValues()', () => {
|
|||
const taskManager = taskManagerMock.createStart();
|
||||
const ruleTypeRegistry = ruleTypeRegistryMock.create();
|
||||
const unsecuredSavedObjectsClient = savedObjectsClientMock.create();
|
||||
|
||||
const encryptedSavedObjects = encryptedSavedObjectsMock.createClient();
|
||||
const authorization = alertingAuthorizationMock.create();
|
||||
const actionsAuthorization = actionsAuthorizationMock.create();
|
||||
const internalSavedObjectsRepository = savedObjectsRepositoryMock.create();
|
||||
|
||||
const kibanaVersion = 'v7.10.0';
|
||||
const logger = loggingSystemMock.create().get();
|
||||
const uiSettings = uiSettingsServiceMock.createStartContract();
|
||||
const uiSettingsClient = uiSettingsServiceMock.createClient();
|
||||
|
||||
uiSettings.asScopedToClient.mockReturnValue(uiSettingsClient);
|
||||
|
||||
const rulesClientParams: jest.Mocked<ConstructorOptions> = {
|
||||
taskManager,
|
||||
|
@ -64,7 +66,7 @@ describe('addGeneratedActionValues()', () => {
|
|||
getAlertIndicesAlias: jest.fn(),
|
||||
alertsService: null,
|
||||
backfillClient: backfillClientMock.create(),
|
||||
uiSettings: uiSettingsServiceMock.createStartContract(),
|
||||
uiSettings,
|
||||
connectorAdapterRegistry: new ConnectorAdapterRegistry(),
|
||||
isSystemAction: jest.fn(),
|
||||
};
|
||||
|
@ -103,6 +105,10 @@ describe('addGeneratedActionValues()', () => {
|
|||
params: {},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('adds uuid', async () => {
|
||||
const actionWithGeneratedValues = await addGeneratedActionValues(
|
||||
[mockAction],
|
||||
|
@ -145,6 +151,21 @@ describe('addGeneratedActionValues()', () => {
|
|||
params: {},
|
||||
uuid: '111-222',
|
||||
});
|
||||
|
||||
expect(uiSettingsClient.get).toHaveBeenCalledTimes(3);
|
||||
expect(uiSettingsClient.get.mock.calls).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Array [
|
||||
"query:allowLeadingWildcards",
|
||||
],
|
||||
Array [
|
||||
"query:queryString:options",
|
||||
],
|
||||
Array [
|
||||
"courier:ignoreFilterIfFieldNotInIndex",
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('throws error if KQL is not valid', async () => {
|
||||
|
@ -156,6 +177,7 @@ describe('addGeneratedActionValues()', () => {
|
|||
alertsFilter: { query: { kql: 'foo:bar:1', filters: [] } },
|
||||
},
|
||||
],
|
||||
|
||||
[mockSystemAction],
|
||||
{
|
||||
...rulesClientParams,
|
||||
|
@ -163,6 +185,12 @@ describe('addGeneratedActionValues()', () => {
|
|||
minimumScheduleIntervalInMs: 0,
|
||||
}
|
||||
)
|
||||
).rejects.toThrowErrorMatchingSnapshot('"Error creating DSL query: invalid KQL"');
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`
|
||||
"Invalid KQL: Expected AND, OR, end of input but \\":\\" found.
|
||||
foo:bar:1
|
||||
-------^"
|
||||
`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import { v4 } from 'uuid';
|
||||
import { buildEsQuery, Filter } from '@kbn/es-query';
|
||||
import Boom from '@hapi/boom';
|
||||
import { UI_SETTINGS } from '@kbn/data-plugin/common';
|
||||
import {
|
||||
NormalizedAlertAction,
|
||||
NormalizedAlertDefaultActionWithGeneratedValues,
|
||||
|
@ -16,6 +15,7 @@ import {
|
|||
NormalizedSystemAction,
|
||||
RulesClientContext,
|
||||
} from '..';
|
||||
import { getEsQueryConfig } from '../../lib/get_es_query_config';
|
||||
|
||||
export async function addGeneratedActionValues(
|
||||
actions: NormalizedAlertAction[] = [],
|
||||
|
@ -26,24 +26,15 @@ export async function addGeneratedActionValues(
|
|||
systemActions: NormalizedAlertSystemActionWithGeneratedValues[];
|
||||
}> {
|
||||
const uiSettingClient = context.uiSettings.asScopedToClient(context.unsecuredSavedObjectsClient);
|
||||
const [allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex] =
|
||||
await Promise.all([
|
||||
uiSettingClient.get(UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS),
|
||||
uiSettingClient.get(UI_SETTINGS.QUERY_STRING_OPTIONS),
|
||||
uiSettingClient.get(UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX),
|
||||
]);
|
||||
const esQueryConfig = {
|
||||
allowLeadingWildcards,
|
||||
queryStringOptions,
|
||||
ignoreFilterIfFieldNotInIndex,
|
||||
};
|
||||
const esQueryConfig = await getEsQueryConfig(uiSettingClient);
|
||||
|
||||
const generateDSL = (kql: string, filters: Filter[]): string => {
|
||||
try {
|
||||
return JSON.stringify(
|
||||
buildEsQuery(undefined, [{ query: kql, language: 'kuery' }], filters, esQueryConfig)
|
||||
);
|
||||
} catch (e) {
|
||||
throw Boom.badRequest(`Error creating DSL query: invalid KQL`);
|
||||
throw Boom.badRequest(`Invalid KQL: ${e.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -8567,7 +8567,6 @@
|
|||
"xpack.alerting.maintenanceWindows.upcoming": "À venir",
|
||||
"xpack.alerting.maintenanceWindowsArchiveFailure": "Impossible d'archiver la fenêtre de maintenance.",
|
||||
"xpack.alerting.maintenanceWindowsArchiveSuccess": "Fenêtre de maintenance archivée \"{title}\"",
|
||||
"xpack.alerting.maintenanceWindowsCreateFailure": "Impossible de créer la fenêtre de maintenance.",
|
||||
"xpack.alerting.maintenanceWindowsCreateSuccess": "Fenêtre de maintenance créée \"{title}\"",
|
||||
"xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure": "Impossible d'annuler et d'archiver la fenêtre de maintenance.",
|
||||
"xpack.alerting.maintenanceWindowsFinishedAndArchiveSuccess": "Fenêtre de maintenance en cours d'exécution annulée et archivée \"{title}\"",
|
||||
|
|
|
@ -8554,7 +8554,6 @@
|
|||
"xpack.alerting.maintenanceWindows.upcoming": "予定",
|
||||
"xpack.alerting.maintenanceWindowsArchiveFailure": "保守時間枠をアーカイブできませんでした。",
|
||||
"xpack.alerting.maintenanceWindowsArchiveSuccess": "アーカイブされた保守時間枠'{title}'",
|
||||
"xpack.alerting.maintenanceWindowsCreateFailure": "保守時間枠を作成できませんでした。",
|
||||
"xpack.alerting.maintenanceWindowsCreateSuccess": "作成された保守時間枠'{title}'",
|
||||
"xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure": "保守時間枠をキャンセルしてアーカイブできませんでした。",
|
||||
"xpack.alerting.maintenanceWindowsFinishedAndArchiveSuccess": "キャンセルされ、アーカイブされた実行中の保守時間枠'{title}'",
|
||||
|
|
|
@ -8571,7 +8571,6 @@
|
|||
"xpack.alerting.maintenanceWindows.upcoming": "即将发生",
|
||||
"xpack.alerting.maintenanceWindowsArchiveFailure": "无法归档维护窗口。",
|
||||
"xpack.alerting.maintenanceWindowsArchiveSuccess": "已归档维护窗口“{title}”",
|
||||
"xpack.alerting.maintenanceWindowsCreateFailure": "无法创建维护窗口。",
|
||||
"xpack.alerting.maintenanceWindowsCreateSuccess": "已创建维护窗口“{title}”",
|
||||
"xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure": "无法取消并归档维护窗口。",
|
||||
"xpack.alerting.maintenanceWindowsFinishedAndArchiveSuccess": "已取消并归档正在运行的维护窗口“{title}”",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue