From da4c42da48b8bb62a743f898115f3aab963bfadb Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Fri, 21 Apr 2023 11:04:09 -0700 Subject: [PATCH 01/29] [DOCS] Automate email connector screenshot, edit secrets field label (#155464) --- .../connectors/action-types/email.asciidoc | 1 + .../connectors/images/email-connector.png | Bin 188529 -> 178393 bytes .../encrypted_fields_callout.test.tsx | 6 +++--- .../encrypted_fields_callout.tsx | 2 +- .../stack_connectors/connector_types.ts | 12 ++++++++++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/management/connectors/action-types/email.asciidoc b/docs/management/connectors/action-types/email.asciidoc index 65bc85a7d7133..f04888a681b6a 100644 --- a/docs/management/connectors/action-types/email.asciidoc +++ b/docs/management/connectors/action-types/email.asciidoc @@ -30,6 +30,7 @@ or as needed when you're creating a rule. For example: [role="screenshot"] image::management/connectors/images/email-connector.png[Email connector] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. [float] [[email-connector-configuration]] diff --git a/docs/management/connectors/images/email-connector.png b/docs/management/connectors/images/email-connector.png index b837fa545a4d111c8d0f02b05cd7cba70d91616c..3cce19021937fe2e838b666138411912be9db0e2 100644 GIT binary patch literal 178393 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

GeT6LXq5lp8i7?7e?)%Xb=lkmTTS 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 G_y0dzQW4Yu literal 188529 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

+`T!qfRkMFLyTo=(c*rSq%mg;( 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 Ke%?L37yk!#6pX9@ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx index 41893e785f5bf..78039c753a276 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx @@ -35,14 +35,14 @@ const renderWithSecretFields = ({ describe('EncryptedFieldsCallout', () => { const isCreateTests: Array<[number, string]> = [ - [1, 'Remember value label0. You must reenter it each time you edit the connector.'], + [1, 'Remember your label0 value. You must reenter it each time you edit the connector.'], [ 2, - 'Remember values label0 and label1. You must reenter them each time you edit the connector.', + 'Remember your label0 and label1 values. You must reenter them each time you edit the connector.', ], [ 3, - 'Remember values label0, label1, and label2. You must reenter them each time you edit the connector.', + 'Remember your label0, label1, and label2 values. You must reenter them each time you edit the connector.', ], ]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx index 7bc0fdbc0703c..35cbb473c6a3f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx @@ -104,7 +104,7 @@ const EncryptedFieldsCalloutComponent: React.FC = ( { values: { secretFieldsLabel, encryptedFieldsLength: totalSecretFields }, defaultMessage: - 'Remember value{encryptedFieldsLength, plural, one {} other {s}} {secretFieldsLabel}. You must reenter {encryptedFieldsLength, plural, one {it} other {them}} each time you edit the connector.', + 'Remember your {secretFieldsLabel} {encryptedFieldsLength, plural, one {value} other {values}}. You must reenter {encryptedFieldsLength, plural, one {it} other {them}} each time you edit the connector.', } )} dataTestSubj="create-connector-secrets-callout" diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts index 247b25d2be2ca..4eec4061347b9 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts @@ -57,5 +57,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton'); await flyOutCancelButton.click(); }); + + it('email connector screenshots', async () => { + await pageObjects.common.navigateToApp('connectors'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await actions.common.openNewConnectorForm('email'); + await testSubjects.setValue('nameInput', 'Gmail connector'); + await testSubjects.setValue('emailFromInput', 'test@gmail.com'); + await testSubjects.setValue('emailServiceSelectInput', 'gmail'); + await commonScreenshots.takeScreenshot('email-connector', screenshotDirectories); + const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton'); + await flyOutCancelButton.click(); + }); }); } From 81b003d431bbd0d5d0bc3fdf507a99e50133a386 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Fri, 21 Apr 2023 14:19:53 -0400 Subject: [PATCH 02/29] [Controls] Use Internal User to Get Value of `allow_expensive_queries` (#155430) changes the way we access `allow_expensive_queries` to use the internal user. --- .../options_list/options_list_service.ts | 2 +- .../options_list_cluster_settings_route.ts | 20 ++++++------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/plugins/controls/public/services/options_list/options_list_service.ts b/src/plugins/controls/public/services/options_list/options_list_service.ts index 7152d190c997d..4ad0ecddb80a8 100644 --- a/src/plugins/controls/public/services/options_list/options_list_service.ts +++ b/src/plugins/controls/public/services/options_list/options_list_service.ts @@ -99,7 +99,7 @@ class OptionsListService implements ControlsOptionsListService { private cachedAllowExpensiveQueries = memoize(async () => { const { allowExpensiveQueries } = await this.http.get<{ allowExpensiveQueries: boolean; - }>('/api/kibana/controls/optionsList/getClusterSettings'); + }>('/api/kibana/controls/optionsList/getExpensiveQueriesSetting'); return allowExpensiveQueries; }); diff --git a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts index f3b4ea598b886..c757a56950da3 100644 --- a/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts +++ b/src/plugins/controls/server/options_list/options_list_cluster_settings_route.ts @@ -8,18 +8,21 @@ import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server'; import { CoreSetup } from '@kbn/core/server'; -import { errors } from '@elastic/elasticsearch'; export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => { const router = http.createRouter(); router.get( { - path: '/api/kibana/controls/optionsList/getClusterSettings', + path: '/api/kibana/controls/optionsList/getExpensiveQueriesSetting', validate: false, }, async (context, _, response) => { try { - const esClient = (await context.core).elasticsearch.client.asCurrentUser; + /** + * using internal user here because in many cases the logged in user will not have the monitor permission required + * to check cluster settings. This endpoint does not take a query, params, or a body, so there is no chance of leaking info. + */ + const esClient = (await context.core).elasticsearch.client.asInternalUser; const settings = await esClient.cluster.getSettings({ include_defaults: true, filter_path: '**.allow_expensive_queries', @@ -40,17 +43,6 @@ export const setupOptionsListClusterSettingsRoute = ({ http }: CoreSetup) => { }, }); } catch (e) { - if (e instanceof errors.ResponseError && e.body.error.type === 'security_exception') { - /** - * in cases where the user does not have the 'monitor' permission this check will fail. In these cases, we will - * fall back to assume that the allowExpensiveQueries setting is on, because it defaults to true. - */ - return response.ok({ - body: { - allowExpensiveQueries: true, - }, - }); - } const kbnErr = getKbnServerError(e); return reportServerError(response, kbnErr); } From da5a4b08d37017b246092f5c38bcd124493ad72c Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Fri, 21 Apr 2023 14:37:10 -0400 Subject: [PATCH 03/29] [Synthetics] adjust synthetics enablement doc links (#155340) ## Summary Adjusts content for Synthetics enablement doc links. --- .../monitors_page/management/disabled_callout.tsx | 6 +++++- .../monitors_page/management/invalid_api_key_callout.tsx | 2 +- .../synthetics_enablement/synthetics_enablement.tsx | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx index 00c6e9b555727..0a75b9a44499b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/disabled_callout.tsx @@ -43,7 +43,11 @@ export const DisabledCallout = ({ total }: { total: number }) => { ) : (

{labels.CALLOUT_MANAGEMENT_CONTACT_ADMIN}{' '} - + {labels.LEARN_MORE_LABEL}

diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx index 8361df0588c04..70816a69c2188 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/invalid_api_key_callout.tsx @@ -34,7 +34,7 @@ export const InvalidApiKeyCalloutCallout = () => { {CALLOUT_MANAGEMENT_CONTACT_ADMIN}{' '} {LEARN_MORE_LABEL} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx index b1efd88590588..d6b927bbc43b3 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/management/synthetics_enablement/synthetics_enablement.tsx @@ -91,7 +91,7 @@ export const EnablementEmptyState = () => { {labels.DOCS_LABEL} From dbb8e2ebecaa87f6799cb0d220b190d0f35d6d02 Mon Sep 17 00:00:00 2001 From: Alexi Doak <109488926+doakalexi@users.noreply.github.com> Date: Fri, 21 Apr 2023 15:04:54 -0400 Subject: [PATCH 04/29] [ResponseOps][Window Maintenance] Add the stop and archive actions to the maintenance window table (#155201) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves https://github.com/elastic/kibana/issues/154814 ## Summary This pr adds the cancel/archive actions to the Maintenance Windows table, and adds an archive callout to the create form. @lcawl I think I need your help with some of the text in the modals 🙂 **Create Form:** Callout Screen Shot 2023-04-18 at 9 49 58 PM Modal Screen Shot 2023-04-18 at 9 50 05 PM **Cancel Modal:** Screen Shot 2023-04-18 at 9 52 18 PM **Cancel and Archive Modal:** Screen Shot 2023-04-18 at 9 52 56 PM **Archive Modal:** Screen Shot 2023-04-18 at 9 53 25 PM **Unarchive Modal:** Screen Shot 2023-04-18 at 9 54 16 PM ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Lisa Cawley --- .../use_archive_maintenance_window.test.tsx | 116 +++++++++ .../hooks/use_archive_maintenance_window.ts | 56 +++++ .../hooks/use_create_maintenance_window.ts | 4 +- .../hooks/use_find_maintenance_windows.ts | 7 +- ...sh_and_archive_maintenance_window.test.tsx | 110 +++++++++ ...e_finish_and_archive_maintenance_window.ts | 45 ++++ .../use_finish_maintenance_window.test.tsx | 85 +++++++ .../hooks/use_finish_maintenance_window.ts | 43 ++++ .../use_update_maintenance_window.test.tsx | 2 +- .../hooks/use_update_maintenance_window.ts | 7 +- .../create_maintenance_windows_form.tsx | 46 +++- .../maintenance_windows_list.test.tsx | 4 +- .../components/maintenance_windows_list.tsx | 96 ++++---- .../components/table_actions_popover.test.tsx | 101 ++++++++ .../components/table_actions_popover.tsx | 230 ++++++++++++++++++ .../pages/maintenance_windows/constants.ts | 8 +- .../pages/maintenance_windows/index.tsx | 10 +- .../pages/maintenance_windows/translations.ts | 96 ++++++++ .../maintenance_windows_api/archive.test.ts | 58 +++++ .../maintenance_windows_api/archive.ts | 35 +++ .../maintenance_windows_api/finish.test.ts | 54 ++++ .../maintenance_windows_api/finish.ts | 32 +++ 22 files changed, 1186 insertions(+), 59 deletions(-) create mode 100644 x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx create mode 100644 x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts create mode 100644 x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx create mode 100644 x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts create mode 100644 x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx create mode 100644 x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.ts create mode 100644 x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.test.tsx create mode 100644 x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx create mode 100644 x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts create mode 100644 x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts create mode 100644 x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts create mode 100644 x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts diff --git a/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx new file mode 100644 index 0000000000000..e6bd2a4071b27 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.test.tsx @@ -0,0 +1,116 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useArchiveMaintenanceWindow } from './use_archive_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/archive', () => ({ + archiveMaintenanceWindow: jest.fn(), +})); + +const { archiveMaintenanceWindow } = jest.requireMock( + '../services/maintenance_windows_api/archive' +); + +const maintenanceWindow: MaintenanceWindow = { + title: 'archive', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useArchiveMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + archiveMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: true }); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Archived maintenance window 'archive'") + ); + }); + + it('should call onError if api fails', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: true }); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to archive maintenance window.') + ); + }); + + it('should call onSuccess if api succeeds (unarchive)', async () => { + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: false }); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Unarchived maintenance window 'archive'") + ); + }); + + it('should call onError if api fails (unarchive)', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate({ maintenanceWindowId: '123', archive: false }); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to unarchive maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts new file mode 100644 index 0000000000000..2bda74f83b9bf --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_archive_maintenance_window.ts @@ -0,0 +1,56 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { archiveMaintenanceWindow } from '../services/maintenance_windows_api/archive'; + +export function useArchiveMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = ({ + maintenanceWindowId, + archive, + }: { + maintenanceWindowId: string; + archive: boolean; + }) => { + return archiveMaintenanceWindow({ http, maintenanceWindowId, archive }); + }; + + return useMutation(mutationFn, { + onSuccess: (data, { archive }) => { + const archiveToast = i18n.translate('xpack.alerting.maintenanceWindowsArchiveSuccess', { + defaultMessage: "Archived maintenance window '{title}'", + values: { + title: data.title, + }, + }); + const unarchiveToast = i18n.translate('xpack.alerting.maintenanceWindowsUnarchiveSuccess', { + defaultMessage: "Unarchived maintenance window '{title}'", + values: { + title: data.title, + }, + }); + toasts.addSuccess(archive ? archiveToast : unarchiveToast); + }, + onError: (error, { archive }) => { + const archiveToast = i18n.translate('xpack.alerting.maintenanceWindowsArchiveFailure', { + defaultMessage: 'Failed to archive maintenance window.', + }); + const unarchiveToast = i18n.translate('xpack.alerting.maintenanceWindowsUnarchiveFailure', { + defaultMessage: 'Failed to unarchive maintenance window.', + }); + toasts.addDanger(archive ? archiveToast : unarchiveToast); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts index 08c01bb080055..e710595bc6180 100644 --- a/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts +++ b/x-pack/plugins/alerting/public/hooks/use_create_maintenance_window.ts @@ -23,12 +23,12 @@ export function useCreateMaintenanceWindow() { }; return useMutation(mutationFn, { - onSuccess: (variables: MaintenanceWindow) => { + onSuccess: (data) => { toasts.addSuccess( i18n.translate('xpack.alerting.maintenanceWindowsCreateSuccess', { defaultMessage: "Created maintenance window '{title}'", values: { - title: variables.title, + title: data.title, }, }) ); diff --git a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts index 6a71bd9c64518..10b7f3402aca1 100644 --- a/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts +++ b/x-pack/plugins/alerting/public/hooks/use_find_maintenance_windows.ts @@ -30,7 +30,11 @@ export const useFindMaintenanceWindows = () => { } }; - const { isLoading, data = [] } = useQuery({ + const { + isLoading, + data = [], + refetch, + } = useQuery({ queryKey: ['findMaintenanceWindows'], queryFn, onError: onErrorFn, @@ -42,5 +46,6 @@ export const useFindMaintenanceWindows = () => { return { maintenanceWindows: data, isLoading, + refetch, }; }; diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx new file mode 100644 index 0000000000000..b80dbbae355bc --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.test.tsx @@ -0,0 +1,110 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useFinishAndArchiveMaintenanceWindow } from './use_finish_and_archive_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/finish', () => ({ + finishMaintenanceWindow: jest.fn(), +})); +jest.mock('../services/maintenance_windows_api/archive', () => ({ + archiveMaintenanceWindow: jest.fn(), +})); + +const { finishMaintenanceWindow } = jest.requireMock('../services/maintenance_windows_api/finish'); +const { archiveMaintenanceWindow } = jest.requireMock( + '../services/maintenance_windows_api/archive' +); + +const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useFinishAndArchiveMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + finishMaintenanceWindow.mockResolvedValue(maintenanceWindow); + archiveMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith( + "Cancelled and archived running maintenance window 'test'" + ) + ); + }); + + it('should call onError if finish api fails', async () => { + finishMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel and archive maintenance window.') + ); + }); + + it('should call onError if archive api fails', async () => { + archiveMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishAndArchiveMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel and archive maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts new file mode 100644 index 0000000000000..d68bf2c89e379 --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_and_archive_maintenance_window.ts @@ -0,0 +1,45 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { finishMaintenanceWindow } from '../services/maintenance_windows_api/finish'; +import { archiveMaintenanceWindow } from '../services/maintenance_windows_api/archive'; + +export function useFinishAndArchiveMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = async (maintenanceWindowId: string) => { + await finishMaintenanceWindow({ http, maintenanceWindowId }); + return archiveMaintenanceWindow({ http, maintenanceWindowId, archive: true }); + }; + + return useMutation(mutationFn, { + onSuccess: (data) => { + toasts.addSuccess( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedAndArchiveSuccess', { + defaultMessage: "Cancelled and archived running maintenance window '{title}'", + values: { + title: data.title, + }, + }) + ); + }, + onError: () => { + toasts.addDanger( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedAndArchiveFailure', { + defaultMessage: 'Failed to cancel and archive maintenance window.', + }) + ); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx new file mode 100644 index 0000000000000..ed534cb835c8d --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.test.tsx @@ -0,0 +1,85 @@ +/* + * 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 { act, renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/dom'; + +import { MaintenanceWindow } from '../pages/maintenance_windows/types'; +import { AppMockRenderer, createAppMockRenderer } from '../lib/test_utils'; +import { useFinishMaintenanceWindow } from './use_finish_maintenance_window'; + +const mockAddDanger = jest.fn(); +const mockAddSuccess = jest.fn(); + +jest.mock('../utils/kibana_react', () => { + const originalModule = jest.requireActual('../utils/kibana_react'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + notifications: { toasts: { addSuccess: mockAddSuccess, addDanger: mockAddDanger } }, + }, + }; + }, + }; +}); +jest.mock('../services/maintenance_windows_api/finish', () => ({ + finishMaintenanceWindow: jest.fn(), +})); + +const { finishMaintenanceWindow } = jest.requireMock('../services/maintenance_windows_api/finish'); + +const maintenanceWindow: MaintenanceWindow = { + title: 'cancel', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + }, +}; + +let appMockRenderer: AppMockRenderer; + +describe('useFinishMaintenanceWindow', () => { + beforeEach(() => { + jest.clearAllMocks(); + + appMockRenderer = createAppMockRenderer(); + finishMaintenanceWindow.mockResolvedValue(maintenanceWindow); + }); + + it('should call onSuccess if api succeeds', async () => { + const { result } = renderHook(() => useFinishMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + await waitFor(() => + expect(mockAddSuccess).toBeCalledWith("Cancelled running maintenance window 'cancel'") + ); + }); + + it('should call onError if api fails', async () => { + finishMaintenanceWindow.mockRejectedValue(''); + + const { result } = renderHook(() => useFinishMaintenanceWindow(), { + wrapper: appMockRenderer.AppWrapper, + }); + + await act(async () => { + await result.current.mutate('123'); + }); + + await waitFor(() => + expect(mockAddDanger).toBeCalledWith('Failed to cancel maintenance window.') + ); + }); +}); diff --git a/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.ts new file mode 100644 index 0000000000000..7e8aafa1793ad --- /dev/null +++ b/x-pack/plugins/alerting/public/hooks/use_finish_maintenance_window.ts @@ -0,0 +1,43 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { useMutation } from '@tanstack/react-query'; + +import { useKibana } from '../utils/kibana_react'; +import { finishMaintenanceWindow } from '../services/maintenance_windows_api/finish'; + +export function useFinishMaintenanceWindow() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const mutationFn = (maintenanceWindowId: string) => { + return finishMaintenanceWindow({ http, maintenanceWindowId }); + }; + + return useMutation(mutationFn, { + onSuccess: (data) => { + toasts.addSuccess( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedSuccess', { + defaultMessage: "Cancelled running maintenance window '{title}'", + values: { + title: data.title, + }, + }) + ); + }, + onError: () => { + toasts.addDanger( + i18n.translate('xpack.alerting.maintenanceWindowsFinishedFailure', { + defaultMessage: 'Failed to cancel maintenance window.', + }) + ); + }, + }); +} diff --git a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx index 67545d83aba17..897b44295d8c0 100644 --- a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx +++ b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.test.tsx @@ -79,7 +79,7 @@ describe('useUpdateMaintenanceWindow', () => { }); await waitFor(() => - expect(mockAddDanger).toBeCalledWith("Failed to update maintenance window '123'") + expect(mockAddDanger).toBeCalledWith('Failed to update maintenance window.') ); }); }); diff --git a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts index de6596b1c766d..c7dd73724b6df 100644 --- a/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts +++ b/x-pack/plugins/alerting/public/hooks/use_update_maintenance_window.ts @@ -39,13 +39,10 @@ export function useUpdateMaintenanceWindow() { }) ); }, - onError: (error, variables) => { + onError: () => { toasts.addDanger( i18n.translate('xpack.alerting.maintenanceWindowsUpdateFailure', { - defaultMessage: "Failed to update maintenance window '{id}'", - values: { - id: variables.maintenanceWindowId, - }, + defaultMessage: 'Failed to update maintenance window.', }) ); }, diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx index b3fe479c21a88..0dda6a8890529 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import moment from 'moment'; import { FIELD_TYPES, @@ -16,7 +16,10 @@ import { } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { Field } from '@kbn/es-ui-shared-plugin/static/forms/components'; import { + EuiButton, EuiButtonEmpty, + EuiCallOut, + EuiConfirmModal, EuiFlexGroup, EuiFlexItem, EuiFormLabel, @@ -33,6 +36,7 @@ import { useCreateMaintenanceWindow } from '../../../hooks/use_create_maintenanc import { useUpdateMaintenanceWindow } from '../../../hooks/use_update_maintenance_window'; import { useUiSetting } from '../../../utils/kibana_react'; import { DatePickerRangeField } from './fields/date_picker_range_field'; +import { useArchiveMaintenanceWindow } from '../../../hooks/use_archive_maintenance_window'; const UseField = getUseField({ component: Field }); @@ -56,6 +60,7 @@ export const CreateMaintenanceWindowForm = React.memo { const [defaultStartDateValue] = useState(moment().toISOString()); const [defaultEndDateValue] = useState(moment().add(30, 'minutes').toISOString()); + const [isModalVisible, setIsModalVisible] = useState(false); const { defaultTimezone, isBrowser } = useDefaultTimezone(); const isEditMode = initialValue !== undefined && maintenanceWindowId !== undefined; @@ -63,6 +68,7 @@ export const CreateMaintenanceWindowForm = React.memo { @@ -109,6 +115,35 @@ export const CreateMaintenanceWindowForm = React.memo setIsModalVisible(false), []); + const showModal = useCallback(() => setIsModalVisible(true), []); + + const modal = useMemo(() => { + let m; + if (isModalVisible) { + m = ( + { + closeModal(); + archiveMaintenanceWindow( + { maintenanceWindowId: maintenanceWindowId!, archive: true }, + { onSuccess } + ); + }} + cancelButtonText={i18n.CANCEL} + confirmButtonText={i18n.ARCHIVE_TITLE} + defaultFocusedButton="confirm" + buttonColor="danger" + > +

{i18n.ARCHIVE_CALLOUT_SUBTITLE}

+
+ ); + } + return m; + }, [closeModal, archiveMaintenanceWindow, isModalVisible, maintenanceWindowId, onSuccess]); + return (
@@ -192,6 +227,15 @@ export const CreateMaintenanceWindowForm = React.memo : null} + {isEditMode ? ( + +

{i18n.ARCHIVE_SUBTITLE}

+ + {i18n.ARCHIVE} + + {modal} +
+ ) : null} { }); test('it renders', () => { - const result = appMockRenderer.render(); + const result = appMockRenderer.render( + {}} loading={false} items={items} /> + ); expect(result.getAllByTestId('list-item')).toHaveLength(items.length); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx index 9d4fc521c3f66..705219b9baa2a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/maintenance_windows_list.tsx @@ -5,29 +5,34 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { formatDate, EuiInMemoryTable, EuiBasicTableColumn, - EuiButton, - useEuiBackgroundColor, EuiFlexGroup, EuiFlexItem, SearchFilterConfig, + EuiBadge, + useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; import { MaintenanceWindowFindResponse, SortDirection } from '../types'; import * as i18n from '../translations'; import { useEditMaintenanceWindowsNavigation } from '../../../hooks/use_navigation'; +import { STATUS_DISPLAY, STATUS_SORT } from '../constants'; import { UpcomingEventsPopover } from './upcoming_events_popover'; -import { StatusColor, STATUS_DISPLAY, STATUS_SORT } from '../constants'; import { MaintenanceWindowStatus } from '../../../../common'; import { StatusFilter } from './status_filter'; +import { TableActionsPopover } from './table_actions_popover'; +import { useFinishMaintenanceWindow } from '../../../hooks/use_finish_maintenance_window'; +import { useArchiveMaintenanceWindow } from '../../../hooks/use_archive_maintenance_window'; +import { useFinishAndArchiveMaintenanceWindow } from '../../../hooks/use_finish_and_archive_maintenance_window'; interface MaintenanceWindowsListProps { loading: boolean; items: MaintenanceWindowFindResponse[]; + refreshData: () => void; } const columns: Array> = [ @@ -39,23 +44,9 @@ const columns: Array> = [ { field: 'status', name: i18n.TABLE_STATUS, - render: (status: string) => { + render: (status: MaintenanceWindowStatus) => { return ( - {}} - > - {STATUS_DISPLAY[status].label} - + {STATUS_DISPLAY[status].label} ); }, sortable: ({ status }) => STATUS_SORT[status], @@ -108,38 +99,61 @@ const search: { filters: SearchFilterConfig[] } = { }; export const MaintenanceWindowsList = React.memo( - ({ loading, items }) => { + ({ loading, items, refreshData }) => { + const { euiTheme } = useEuiTheme(); const { navigateToEditMaintenanceWindows } = useEditMaintenanceWindowsNavigation(); - const warningBackgroundColor = useEuiBackgroundColor('warning'); - const subduedBackgroundColor = useEuiBackgroundColor('subdued'); + const onEdit = useCallback( + (id) => navigateToEditMaintenanceWindows(id), + [navigateToEditMaintenanceWindows] + ); + const { mutate: finishMaintenanceWindow, isLoading: isLoadingFinish } = + useFinishMaintenanceWindow(); + const onCancel = useCallback( + (id) => finishMaintenanceWindow(id, { onSuccess: () => refreshData() }), + [finishMaintenanceWindow, refreshData] + ); + const { mutate: archiveMaintenanceWindow, isLoading: isLoadingArchive } = + useArchiveMaintenanceWindow(); + const onArchive = useCallback( + (id: string, archive: boolean) => + archiveMaintenanceWindow( + { maintenanceWindowId: id, archive }, + { onSuccess: () => refreshData() } + ), + [archiveMaintenanceWindow, refreshData] + ); + const { mutate: finishAndArchiveMaintenanceWindow, isLoading: isLoadingFinishAndArchive } = + useFinishAndArchiveMaintenanceWindow(); + const onCancelAndArchive = useCallback( + (id: string) => finishAndArchiveMaintenanceWindow(id, { onSuccess: () => refreshData() }), + [finishAndArchiveMaintenanceWindow, refreshData] + ); + const tableCss = useMemo(() => { return css` .euiTableRow { &.running { - background-color: ${warningBackgroundColor}; - } - - &.archived { - background-color: ${subduedBackgroundColor}; + background-color: ${euiTheme.colors.highlight}; } } `; - }, [warningBackgroundColor, subduedBackgroundColor]); + }, [euiTheme.colors.highlight]); const actions: Array> = [ { name: '', - actions: [ - { - name: i18n.TABLE_ACTION_EDIT, - isPrimary: true, - description: 'Edit maintenance window', - icon: 'pencil', - type: 'icon', - onClick: (mw: MaintenanceWindowFindResponse) => navigateToEditMaintenanceWindows(mw.id), - 'data-test-subj': 'action-edit', - }, - ], + render: ({ status, id }: { status: MaintenanceWindowStatus; id: string }) => { + return ( + + ); + }, }, ]; @@ -147,7 +161,7 @@ export const MaintenanceWindowsList = React.memo( { + let appMockRenderer: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + appMockRenderer = createAppMockRenderer(); + }); + + test('it renders', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + + expect(result.getByTestId('table-actions-icon-button')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is running', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-cancel')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-cancel-and-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is upcoming', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is finished', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-edit')).toBeInTheDocument(); + expect(result.getByTestId('table-actions-archive')).toBeInTheDocument(); + }); + + test('it shows the correct actions when a maintenance window is archived', () => { + const result = appMockRenderer.render( + {}} + onCancel={() => {}} + onArchive={() => {}} + onCancelAndArchive={() => {}} + /> + ); + fireEvent.click(result.getByTestId('table-actions-icon-button')); + expect(result.getByTestId('table-actions-unarchive')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx new file mode 100644 index 0000000000000..4742ede93d53c --- /dev/null +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/table_actions_popover.tsx @@ -0,0 +1,230 @@ +/* + * 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 React, { useCallback, useMemo, useState } from 'react'; +import { + EuiButtonIcon, + EuiConfirmModal, + EuiContextMenuItem, + EuiContextMenuPanel, + EuiFlexGroup, + EuiFlexItem, + EuiPopover, +} from '@elastic/eui'; +import * as i18n from '../translations'; +import { MaintenanceWindowStatus } from '../../../../common'; + +interface TableActionsPopoverProps { + id: string; + status: MaintenanceWindowStatus; + onEdit: (id: string) => void; + onCancel: (id: string) => void; + onArchive: (id: string, archive: boolean) => void; + onCancelAndArchive: (id: string) => void; +} +type ModalType = 'cancel' | 'cancelAndArchive' | 'archive' | 'unarchive'; +type ActionType = ModalType | 'edit'; + +export const TableActionsPopover: React.FC = React.memo( + ({ id, status, onEdit, onCancel, onArchive, onCancelAndArchive }) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [isModalVisible, setIsModalVisible] = useState(false); + const [modalType, setModalType] = useState(); + + const onButtonClick = useCallback(() => { + setIsPopoverOpen((open) => !open); + }, []); + const closePopover = useCallback(() => { + setIsPopoverOpen(false); + }, []); + + const closeModal = useCallback(() => setIsModalVisible(false), []); + const showModal = useCallback((type: ModalType) => { + setModalType(type); + setIsModalVisible(true); + }, []); + + const modal = useMemo(() => { + const modals = { + cancel: { + props: { + title: i18n.CANCEL_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onCancel(id); + }, + cancelButtonText: i18n.CANCEL_MODAL_BUTTON, + confirmButtonText: i18n.CANCEL_MODAL_TITLE, + }, + subtitle: i18n.CANCEL_MODAL_SUBTITLE, + }, + cancelAndArchive: { + props: { + title: i18n.CANCEL_AND_ARCHIVE_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onCancelAndArchive(id); + }, + cancelButtonText: i18n.CANCEL_MODAL_BUTTON, + confirmButtonText: i18n.CANCEL_AND_ARCHIVE_MODAL_TITLE, + }, + subtitle: i18n.CANCEL_AND_ARCHIVE_MODAL_SUBTITLE, + }, + archive: { + props: { + title: i18n.ARCHIVE_TITLE, + onConfirm: () => { + closeModal(); + onArchive(id, true); + }, + cancelButtonText: i18n.CANCEL, + confirmButtonText: i18n.ARCHIVE_TITLE, + }, + subtitle: i18n.ARCHIVE_SUBTITLE, + }, + unarchive: { + props: { + title: i18n.UNARCHIVE_MODAL_TITLE, + onConfirm: () => { + closeModal(); + onArchive(id, false); + }, + cancelButtonText: i18n.CANCEL, + confirmButtonText: i18n.UNARCHIVE_MODAL_TITLE, + }, + subtitle: i18n.UNARCHIVE_MODAL_SUBTITLE, + }, + }; + let m; + if (isModalVisible && modalType) { + const modalProps = modals[modalType]; + m = ( + +

{modalProps.subtitle}

+
+ ); + } + return m; + }, [id, modalType, isModalVisible, closeModal, onArchive, onCancel, onCancelAndArchive]); + + const items = useMemo(() => { + const menuItems = { + edit: ( + { + closePopover(); + onEdit(id); + }} + > + {i18n.TABLE_ACTION_EDIT} + + ), + cancel: ( + { + closePopover(); + showModal('cancel'); + }} + > + {i18n.TABLE_ACTION_CANCEL} + + ), + cancelAndArchive: ( + { + closePopover(); + showModal('cancelAndArchive'); + }} + > + {i18n.TABLE_ACTION_CANCEL_AND_ARCHIVE} + + ), + archive: ( + { + closePopover(); + showModal('archive'); + }} + > + {i18n.ARCHIVE} + + ), + unarchive: ( + { + closePopover(); + showModal('unarchive'); + }} + > + {i18n.TABLE_ACTION_UNARCHIVE} + + ), + }; + const statusMenuItemsMap: Record = { + running: ['edit', 'cancel', 'cancelAndArchive'], + upcoming: ['edit', 'archive'], + finished: ['edit', 'archive'], + archived: ['unarchive'], + }; + return statusMenuItemsMap[status].map((type) => menuItems[type]); + }, [id, status, onEdit, closePopover, showModal]); + + const button = useMemo( + () => ( + + ), + [onButtonClick] + ); + + return ( + <> + + + + + + + + {modal} + + ); + } +); +TableActionsPopover.displayName = 'TableActionsPopover'; diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts index 1aed4dac0568c..27b10e804693d 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/constants.ts @@ -107,15 +107,13 @@ export const RRULE_WEEKDAYS_TO_ISO_WEEKDAYS = mapValues(invert(ISO_WEEKDAYS_TO_R Number(v) ); -export const STATUS_DISPLAY: Record = { - [MaintenanceWindowStatus.Running]: { color: 'warning', label: i18n.TABLE_STATUS_RUNNING }, +export const STATUS_DISPLAY = { + [MaintenanceWindowStatus.Running]: { color: 'primary', label: i18n.TABLE_STATUS_RUNNING }, [MaintenanceWindowStatus.Upcoming]: { color: 'warning', label: i18n.TABLE_STATUS_UPCOMING }, [MaintenanceWindowStatus.Finished]: { color: 'success', label: i18n.TABLE_STATUS_FINISHED }, - [MaintenanceWindowStatus.Archived]: { color: 'text', label: i18n.TABLE_STATUS_ARCHIVED }, + [MaintenanceWindowStatus.Archived]: { color: 'default', label: i18n.TABLE_STATUS_ARCHIVED }, }; -export type StatusColor = 'warning' | 'success' | 'text'; - export const STATUS_SORT = { [MaintenanceWindowStatus.Running]: 0, [MaintenanceWindowStatus.Upcoming]: 1, diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx index ab5828f0dffa3..fa9b54122562d 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/index.tsx @@ -31,7 +31,7 @@ export const MaintenanceWindowsPage = React.memo(() => { const { docLinks } = useKibana().services; const { navigateToCreateMaintenanceWindow } = useCreateMaintenanceWindowNavigation(); - const { isLoading, maintenanceWindows } = useFindMaintenanceWindows(); + const { isLoading, maintenanceWindows, refetch } = useFindMaintenanceWindows(); useBreadcrumbs(AlertingDeepLinkId.maintenanceWindows); @@ -39,6 +39,8 @@ export const MaintenanceWindowsPage = React.memo(() => { navigateToCreateMaintenanceWindow(); }, [navigateToCreateMaintenanceWindow]); + const refreshData = useCallback(() => refetch(), [refetch]); + const showEmptyPrompt = !isLoading && maintenanceWindows.length === 0; if (isLoading) { @@ -77,7 +79,11 @@ export const MaintenanceWindowsPage = React.memo(() => { ) : ( <> - + )} diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts index 30b83963cc5b7..65f24411a2a1a 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/translations.ts @@ -454,6 +454,102 @@ export const SAVE_MAINTENANCE_WINDOW = i18n.translate( } ); +export const TABLE_ACTION_CANCEL = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.cancel', + { + defaultMessage: 'Cancel', + } +); + +export const CANCEL_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.title', + { + defaultMessage: 'Cancel maintenance window', + } +); + +export const CANCEL_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.subtitle', + { + defaultMessage: + 'Rule notifications resume immediately. Running maintenance window events are canceled; upcoming events are unaffected.', + } +); + +export const CANCEL_MODAL_BUTTON = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelModal.button', + { + defaultMessage: 'Keep running', + } +); + +export const TABLE_ACTION_CANCEL_AND_ARCHIVE = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.cancelAndArchive', + { + defaultMessage: 'Cancel and archive', + } +); + +export const CANCEL_AND_ARCHIVE_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelAndArchiveModal.title', + { + defaultMessage: 'Cancel and archive maintenance window', + } +); + +export const CANCEL_AND_ARCHIVE_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.cancelAndArchiveModal.subtitle', + { + defaultMessage: + 'Rule notifications resume immediately. All running and upcoming maintenance window events are canceled and the window is queued for deletion.', + } +); + +export const ARCHIVE = i18n.translate('xpack.alerting.maintenanceWindows.archive', { + defaultMessage: 'Archive', +}); + +export const ARCHIVE_TITLE = i18n.translate('xpack.alerting.maintenanceWindows.archive.title', { + defaultMessage: 'Archive maintenance window', +}); + +export const ARCHIVE_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.archive.subtitle', + { + defaultMessage: + 'Upcoming maintenance window events are canceled and the window is queued for deletion.', + } +); + +export const TABLE_ACTION_UNARCHIVE = i18n.translate( + 'xpack.alerting.maintenanceWindows.table.unarchive', + { + defaultMessage: 'Unarchive', + } +); + +export const UNARCHIVE_MODAL_TITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.unarchiveModal.title', + { + defaultMessage: 'Unarchive maintenance window', + } +); + +export const UNARCHIVE_MODAL_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.unarchiveModal.subtitle', + { + defaultMessage: 'Upcoming maintenance window events are scheduled.', + } +); + +export const ARCHIVE_CALLOUT_SUBTITLE = i18n.translate( + 'xpack.alerting.maintenanceWindows.archiveCallout.subtitle', + { + defaultMessage: + 'The changes you have made here will not be saved. Are you sure you want to discard these unsaved changes and archive this maintenance window?', + } +); + export const EXPERIMENTAL_LABEL = i18n.translate( 'xpack.alerting.maintenanceWindows.badge.experimentalLabel', { diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts new file mode 100644 index 0000000000000..8f0e44eaf2eb9 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.test.ts @@ -0,0 +1,58 @@ +/* + * 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/public/mocks'; +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { archiveMaintenanceWindow } from './archive'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('archiveMaintenanceWindow', () => { + test('should call archive maintenance window api', async () => { + const apiResponse = { + title: 'test', + duration: 1, + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + http.post.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + + const result = await archiveMaintenanceWindow({ + http, + maintenanceWindowId: '123', + archive: true, + }); + expect(result).toEqual(maintenanceWindow); + expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/123/_archive", + Object { + "body": "{\\"archive\\":true}", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts new file mode 100644 index 0000000000000..fe07ebb04b38e --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/archive.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ + ...rest, + rRule, +}); + +export async function archiveMaintenanceWindow({ + http, + maintenanceWindowId, + archive, +}: { + http: HttpSetup; + maintenanceWindowId: string; + archive: boolean; +}): Promise { + const res = await http.post>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/${encodeURIComponent( + maintenanceWindowId + )}/_archive`, + { body: JSON.stringify({ archive }) } + ); + + return rewriteBodyRes(res); +} diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts new file mode 100644 index 0000000000000..a67b7246a64f5 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.test.ts @@ -0,0 +1,54 @@ +/* + * 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/public/mocks'; +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { finishMaintenanceWindow } from './finish'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe('finishMaintenanceWindow', () => { + test('should call finish maintenance window api', async () => { + const apiResponse = { + title: 'test', + duration: 1, + r_rule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + http.post.mockResolvedValueOnce(apiResponse); + + const maintenanceWindow: MaintenanceWindow = { + title: 'test', + duration: 1, + rRule: { + dtstart: '2023-03-23T19:16:21.293Z', + tzid: 'America/New_York', + freq: 3, + interval: 1, + byweekday: ['TH'], + }, + }; + + const result = await finishMaintenanceWindow({ + http, + maintenanceWindowId: '123', + }); + expect(result).toEqual(maintenanceWindow); + expect(http.post.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "/internal/alerting/rules/maintenance_window/123/_finish", + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts new file mode 100644 index 0000000000000..910fad0bee1c3 --- /dev/null +++ b/x-pack/plugins/alerting/public/services/maintenance_windows_api/finish.ts @@ -0,0 +1,32 @@ +/* + * 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 { HttpSetup } from '@kbn/core/public'; +import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; + +import { MaintenanceWindow } from '../../pages/maintenance_windows/types'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '../../../common'; + +const rewriteBodyRes: RewriteRequestCase = ({ r_rule: rRule, ...rest }) => ({ + ...rest, + rRule, +}); + +export async function finishMaintenanceWindow({ + http, + maintenanceWindowId, +}: { + http: HttpSetup; + maintenanceWindowId: string; +}): Promise { + const res = await http.post>( + `${INTERNAL_BASE_ALERTING_API_PATH}/rules/maintenance_window/${encodeURIComponent( + maintenanceWindowId + )}/_finish` + ); + + return rewriteBodyRes(res); +} From ec6bc2db15ea0117b9ee873ad234373c11f11f41 Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Fri, 21 Apr 2023 14:19:17 -0500 Subject: [PATCH 05/29] [Security Solution] add session view component to expandable flyout (#154597) --- .../alert_details_left_panel.cy.ts | 4 +- ..._details_left_panel_session_view_tab.cy.ts | 45 +++++++++++++ .../screens/document_expandable_flyout.ts | 2 + ....stories.tsx => analyze_graph.stories.tsx} | 0 .../left/components/session_view.stories.tsx | 44 +++++++++++++ .../left/components/session_view.test.tsx | 63 +++++++++++++++++++ .../flyout/left/components/session_view.tsx | 36 ++++++++++- .../public/flyout/left/components/test_ids.ts | 1 + .../flyout/left/components/translations.ts | 7 +++ .../public/flyout/left/context.tsx | 47 +++++++++++++- .../public/flyout/right/context.tsx | 3 +- 11 files changed, 243 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts rename x-pack/plugins/security_solution/public/flyout/left/components/{analyze.stories.tsx => analyze_graph.stories.tsx} (100%) create mode 100644 x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts index a5442b786040f..a15669a620d49 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel.cy.ts @@ -100,9 +100,7 @@ describe.skip('Alert details expandable flyout left panel', { testIsolation: fal it('should display content when switching buttons', () => { openVisualizeTab(); openSessionView(); - cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT) - .should('be.visible') - .and('have.text', 'Session view'); + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT).should('be.visible'); openGraphAnalyzer(); cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT).should('be.visible'); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts new file mode 100644 index 0000000000000..1d18b33350fd4 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_left_panel_session_view_tab.cy.ts @@ -0,0 +1,45 @@ +/* + * 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 { DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA } from '../../../screens/document_expandable_flyout'; +import { + expandFirstAlertExpandableFlyout, + expandDocumentDetailsExpandableFlyoutLeftSection, +} from '../../../tasks/document_expandable_flyout'; +import { cleanKibana } from '../../../tasks/common'; +import { login, visit } from '../../../tasks/login'; +import { createRule } from '../../../tasks/api_calls/rules'; +import { getNewRule } from '../../../objects/rule'; +import { ALERTS_URL } from '../../../urls/navigation'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; + +// Skipping these for now as the feature is protected behind a feature flag set to false by default +// To run the tests locally, add 'securityFlyoutEnabled' in the Cypress config.ts here https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/config.ts#L50 +describe.skip( + 'Alert details expandable flyout left panel session view', + { testIsolation: false }, + () => { + before(() => { + cleanKibana(); + login(); + createRule(getNewRule()); + visit(ALERTS_URL); + waitForAlertsToPopulate(); + expandFirstAlertExpandableFlyout(); + expandDocumentDetailsExpandableFlyoutLeftSection(); + }); + + it('should display session view no data message', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA) + .should('be.visible') + .and('contain.text', 'No data to render') + .and('contain.text', 'No process events found for this query'); + }); + + it('should display session view component', () => {}); + } +); diff --git a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts index 2f07705ab18d4..06916d70b652a 100644 --- a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts +++ b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts @@ -135,6 +135,8 @@ export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_BUTTON = getData ); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_CONTENT = getDataTestSubjectSelector(SESSION_VIEW_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_SESSION_VIEW_NO_DATA = + getDataTestSubjectSelector('sessionView:sessionViewProcessEventsEmpty'); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON = getDataTestSubjectSelector(VISUALIZE_TAB_GRAPH_ANALYZER_BUTTON_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_VISUALIZE_TAB_GRAPH_ANALYZER_CONTENT = diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/analyze.stories.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.stories.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/flyout/left/components/analyze.stories.tsx rename to x-pack/plugins/security_solution/public/flyout/left/components/analyze_graph.stories.tsx diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx new file mode 100644 index 0000000000000..40b9b7e7a5b18 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.stories.tsx @@ -0,0 +1,44 @@ +/* + * 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 React from 'react'; +import type { Story } from '@storybook/react'; +import { SessionView } from './session_view'; +import type { LeftPanelContext } from '../context'; +import { LeftFlyoutContext } from '../context'; + +export default { + component: SessionView, + title: 'Flyout/SessionView', +}; + +// TODO to get this working, we need to spent some time getting all the foundation items for storybook +// (ReduxStoreProvider, CellActionsProvider...) similarly to how it was done for the TestProvidersComponent +// see ticket https://github.com/elastic/security-team/issues/6223 +// export const Default: Story = () => { +// const contextValue = { +// getFieldsData: () => {}, +// } as unknown as LeftPanelContext; +// +// return ( +// +// +// +// ); +// }; + +export const Error: Story = () => { + const contextValue = { + getFieldsData: () => {}, + } as unknown as LeftPanelContext; + + return ( + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx new file mode 100644 index 0000000000000..d334f60ba2eb3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.test.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import type { LeftPanelContext } from '../context'; +import { LeftFlyoutContext } from '../context'; +import { TestProviders } from '../../../common/mock'; +import { SESSION_VIEW_ERROR_TEST_ID, SESSION_VIEW_TEST_ID } from './test_ids'; +import { SessionView } from './session_view'; + +jest.mock('../../../common/lib/kibana', () => { + const originalModule = jest.requireActual('../../../common/lib/kibana'); + return { + ...originalModule, + useKibana: jest.fn().mockReturnValue({ + services: { + sessionView: { + getSessionView: jest.fn().mockReturnValue(
), + }, + }, + }), + }; +}); + +describe('', () => { + it('renders session view correctly', () => { + const contextValue = { + getFieldsData: () => 'id', + } as unknown as LeftPanelContext; + + const wrapper = render( + + + + + + ); + expect(wrapper.getByTestId(SESSION_VIEW_TEST_ID)).toBeInTheDocument(); + }); + + it('should render error message on null eventId', () => { + const contextValue = { + getFieldsData: () => {}, + } as unknown as LeftPanelContext; + + const wrapper = render( + + + + + + ); + expect(wrapper.getByTestId(SESSION_VIEW_ERROR_TEST_ID)).toBeInTheDocument(); + expect(wrapper.getByText('Unable to display session view')).toBeInTheDocument(); + expect(wrapper.getByText('There was an error displaying session view')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx index e62745b905640..d48afe6e3f712 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/components/session_view.tsx @@ -7,16 +7,46 @@ import type { FC } from 'react'; import React from 'react'; -import { EuiText } from '@elastic/eui'; -import { SESSION_VIEW_TEST_ID } from './test_ids'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { getField } from '../../shared/utils'; +import { ERROR_MESSAGE, ERROR_TITLE } from '../../shared/translations'; +import { SESSION_VIEW_ERROR_MESSAGE } from './translations'; +import { SESSION_VIEW_ERROR_TEST_ID, SESSION_VIEW_TEST_ID } from './test_ids'; +import { useKibana } from '../../../common/lib/kibana'; +import { useLeftPanelContext } from '../context'; export const SESSION_VIEW_ID = 'session_view'; +const SESSION_ENTITY_ID = 'process.entry_leader.entity_id'; /** * Session view displayed in the document details expandable flyout left section under the Visualize tab */ export const SessionView: FC = () => { - return {'Session view'}; + const { sessionView } = useKibana().services; + const { getFieldsData } = useLeftPanelContext(); + + const sessionEntityId = getField(getFieldsData(SESSION_ENTITY_ID)); + + if (!sessionEntityId) { + return ( + {ERROR_TITLE(SESSION_VIEW_ERROR_MESSAGE)}} + body={

{ERROR_MESSAGE(SESSION_VIEW_ERROR_MESSAGE)}

} + data-test-subj={SESSION_VIEW_ERROR_TEST_ID} + /> + ); + } + + return ( +
+ {sessionView.getSessionView({ + sessionEntityId, + isFullScreen: true, + })} +
+ ); }; SessionView.displayName = 'SessionView'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts index 8b2804fae3e9f..40cf67fddb180 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/test_ids.ts @@ -9,6 +9,7 @@ export const ANALYZER_GRAPH_TEST_ID = 'securitySolutionDocumentDetailsFlyoutAnal export const ANALYZE_GRAPH_ERROR_TEST_ID = 'securitySolutionDocumentDetailsFlyoutAnalyzerGraphError'; export const SESSION_VIEW_TEST_ID = 'securitySolutionDocumentDetailsFlyoutSessionView'; +export const SESSION_VIEW_ERROR_TEST_ID = 'securitySolutionDocumentDetailsFlyoutSessionViewError'; export const ENTITIES_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesDetails'; export const THREAT_INTELLIGENCE_DETAILS_TEST_ID = 'securitySolutionDocumentDetailsFlyoutThreatIntelligenceDetails'; diff --git a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts index 8c59c8a101fb0..f82d34c859ddf 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/components/translations.ts @@ -13,3 +13,10 @@ export const ANALYZER_ERROR_MESSAGE = i18n.translate( defaultMessage: 'analyzer', } ); + +export const SESSION_VIEW_ERROR_MESSAGE = i18n.translate( + 'xpack.securitySolution.flyout.sessionViewErrorTitle', + { + defaultMessage: 'session view', + } +); diff --git a/x-pack/plugins/security_solution/public/flyout/left/context.tsx b/x-pack/plugins/security_solution/public/flyout/left/context.tsx index 9b564666ea487..bf6adb5cd6a07 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/left/context.tsx @@ -6,6 +6,16 @@ */ import React, { createContext, useContext, useMemo } from 'react'; +import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import { css } from '@emotion/react'; +import { SecurityPageName } from '../../../common/constants'; +import { SourcererScopeName } from '../../common/store/sourcerer/model'; +import { useSourcererDataView } from '../../common/containers/sourcerer'; +import { useTimelineEventsDetails } from '../../timelines/containers/details'; +import { useGetFieldsData } from '../../common/hooks/use_get_fields_data'; +import { useRouteSpy } from '../../common/utils/route/use_route_spy'; +import { useSpaceId } from '../../common/hooks/use_space_id'; +import { getAlertIndexAlias } from '../../timelines/components/side_panel/event_details/helpers'; import type { LeftPanelProps } from '.'; export interface LeftPanelContext { @@ -17,6 +27,10 @@ export interface LeftPanelContext { * Name of the index used in the parent's page */ indexName: string; + /** + * Retrieves searchHit values for the provided field + */ + getFieldsData: (field: string) => unknown | unknown[]; } export const LeftFlyoutContext = createContext(undefined); @@ -29,11 +43,40 @@ export type LeftPanelProviderProps = { } & Partial; export const LeftPanelProvider = ({ id, indexName, children }: LeftPanelProviderProps) => { + const currentSpaceId = useSpaceId(); + const eventIndex = indexName ? getAlertIndexAlias(indexName, currentSpaceId) ?? indexName : ''; + const [{ pageName }] = useRouteSpy(); + const sourcererScope = + pageName === SecurityPageName.detections + ? SourcererScopeName.detections + : SourcererScopeName.default; + const sourcererDataView = useSourcererDataView(sourcererScope); + const [loading, _, searchHit] = useTimelineEventsDetails({ + indexName: eventIndex, + eventId: id ?? '', + runtimeMappings: sourcererDataView.runtimeMappings, + skip: !id, + }); + const getFieldsData = useGetFieldsData(searchHit?.fields); + const contextValue = useMemo( - () => (id && indexName ? { eventId: id, indexName } : undefined), - [id, indexName] + () => (id && indexName ? { eventId: id, indexName, getFieldsData } : undefined), + [id, indexName, getFieldsData] ); + if (loading) { + return ( + + + + ); + } + return {children}; }; diff --git a/x-pack/plugins/security_solution/public/flyout/right/context.tsx b/x-pack/plugins/security_solution/public/flyout/right/context.tsx index 282c224b5a15f..2da844946cbbd 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/context.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/context.tsx @@ -19,6 +19,7 @@ import { SecurityPageName } from '../../../common/constants'; import { SourcererScopeName } from '../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import type { RightPanelProps } from '.'; +import type { GetFieldsData } from '../../common/hooks/use_get_fields_data'; import { useGetFieldsData } from '../../common/hooks/use_get_fields_data'; export interface RightPanelContext { @@ -57,7 +58,7 @@ export interface RightPanelContext { /** * Retrieves searchHit values for the provided field */ - getFieldsData: (field: string) => unknown | unknown[]; + getFieldsData: GetFieldsData; } export const RightPanelContext = createContext(undefined); From e1b3bc259aecec5aff8e69b837b5723ba215a244 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Fri, 21 Apr 2023 12:19:33 -0700 Subject: [PATCH 06/29] [DOCS] Automate email-params-test.png, add title in connector table rows (#155469) --- .../connectors/action-types/email.asciidoc | 1 + .../connectors/images/email-params-test.png | Bin 173053 -> 108808 bytes .../components/actions_connectors_list.tsx | 1 + .../stack_connectors/connector_types.ts | 53 +++++++++++++++++- 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/docs/management/connectors/action-types/email.asciidoc b/docs/management/connectors/action-types/email.asciidoc index f04888a681b6a..7d6cc75282a2f 100644 --- a/docs/management/connectors/action-types/email.asciidoc +++ b/docs/management/connectors/action-types/email.asciidoc @@ -183,6 +183,7 @@ as you're creating or editing the connector in {kib}. For example: [role="screenshot"] image::management/connectors/images/email-params-test.png[Email params test] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. Email actions have the following configuration properties. diff --git a/docs/management/connectors/images/email-params-test.png b/docs/management/connectors/images/email-params-test.png index 3745bcd3235e9319517f6190dc2bcabddab1ae0a..bc2a9706e6396dddecab3f5c80e60c59e3f91b6e 100644 GIT binary patch literal 108808 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

fj3|A}J#eWI0!XTD(c`BvtJmi0qBy-P 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

}Z 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

`?2}VWi^!PRP?03&b33z0#EH!eZd%G|t};0cxW@ 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 ADgXcg diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx index a03c34c90962e..9edde99574ca1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx @@ -234,6 +234,7 @@ const ActionsConnectorsList: React.FunctionComponent = () => { <> editItem(item, EditConnectorTabs.Configuration)} key={item.id} disabled={actionTypesIndex ? !actionTypesIndex[item.actionTypeId]?.enabled : true} diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts index 4eec4061347b9..4da22a8e807a8 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_connectors/connector_types.ts @@ -12,8 +12,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const screenshotDirectories = ['response_ops_docs', 'stack_connectors']; const pageObjects = getPageObjects(['common', 'header']); const actions = getService('actions'); - const testSubjects = getService('testSubjects'); + const browser = getService('browser'); const comboBox = getService('comboBox'); + const find = getService('find'); + const testSubjects = getService('testSubjects'); const testIndex = `test-index`; const indexDocument = `{\n` + @@ -21,13 +23,36 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { `"rule_name": "{{rule.name}}",\n` + `"alert_id": "{{alert.id}}",\n` + `"context_message": "{{context.message}}"\n`; + const emailConnectorName = 'my-email-connector'; describe('connector types', function () { + let emailConnectorId: string; + before(async () => { + ({ id: emailConnectorId } = await actions.api.createConnector({ + name: emailConnectorName, + config: { + service: 'other', + from: 'bob@example.com', + host: 'some.non.existent.com', + port: 25, + }, + secrets: { + user: 'bob', + password: 'supersecret', + }, + connectorTypeId: '.email', + })); + }); + beforeEach(async () => { await pageObjects.common.navigateToApp('connectors'); await pageObjects.header.waitUntilLoadingHasFinished(); }); + after(async () => { + await actions.api.deleteConnector(emailConnectorId); + }); + it('server log connector screenshots', async () => { await pageObjects.common.navigateToApp('connectors'); await pageObjects.header.waitUntilLoadingHasFinished(); @@ -69,5 +94,31 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const flyOutCancelButton = await testSubjects.find('euiFlyoutCloseButton'); await flyOutCancelButton.click(); }); + + it('test email connector screenshots', async () => { + const searchBox = await find.byCssSelector('[data-test-subj="actionsList"] .euiFieldSearch'); + await searchBox.click(); + await searchBox.clearValue(); + await searchBox.type('my actionTypeId:(.email)'); + await searchBox.pressKeys(browser.keys.ENTER); + const connectorList = await testSubjects.find('actionsTable'); + const emailConnector = await connectorList.findByCssSelector( + `[title="${emailConnectorName}"]` + ); + await emailConnector.click(); + const testButton = await testSubjects.find('testConnectorTab'); + await testButton.click(); + await testSubjects.setValue('comboBoxSearchInput', 'elastic@gmail.com'); + await testSubjects.setValue('subjectInput', 'Test subject'); + await testSubjects.setValue('messageTextArea', 'Enter message text'); + /* timing issue sometimes happens with the combobox so we just try to set the subjectInput again */ + await testSubjects.setValue('subjectInput', 'Test subject'); + await commonScreenshots.takeScreenshot( + 'email-params-test', + screenshotDirectories, + 1400, + 1024 + ); + }); }); } From 22b8e5b0a613f95fc768278932c230d17c894cfc Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Fri, 21 Apr 2023 13:43:08 -0600 Subject: [PATCH 07/29] [Security Solution] Add Timeline Hover Action to Security Dashboard Calculated Metrics (#154299) Brings back the Add to timeline action on alert counts via Cell Actions Areas implemented include: - D&R Dashboard - Open alerts by rule - Hosts by alert severity - Users by alert severity - Entity Analytics Dashboard - Host Risk Scores - User Risk Scores ![123](https://user-images.githubusercontent.com/6935300/231568732-2263ac45-ba50-4aab-b3a6-224182fbf92c.jpg) --------- Crafted with friendship by Co-authored-by: Pablo Neves Machado Co-authored-by: semd --- packages/kbn-cell-actions/README.md | 2 +- .../src/__stories__/cell_actions.stories.tsx | 14 +- .../src/components/cell_actions.test.tsx | 34 ++- .../src/components/cell_actions.tsx | 9 +- .../components/extra_actions_popover.test.tsx | 31 +-- .../src/components/extra_actions_popover.tsx | 12 +- .../components/hover_actions_popover.test.tsx | 93 ++++--- .../src/components/hover_actions_popover.tsx | 12 +- .../src/components/inline_actions.test.tsx | 22 +- .../src/components/inline_actions.tsx | 8 +- packages/kbn-cell-actions/src/constants.ts | 3 +- .../cell_action/add_to_new_timeline.test.ts | 247 ++++++++++++++++++ .../cell_action/add_to_new_timeline.ts | 117 +++++++++ .../actions/add_to_timeline/constants.ts | 23 ++ .../public/actions/add_to_timeline/index.ts | 1 + .../public/actions/constants.ts | 1 + .../public/actions/register.ts | 15 +- .../security_solution/public/actions/types.ts | 31 ++- .../ml/__snapshots__/entity.test.tsx.snap | 2 +- .../public/common/components/ml/entity.tsx | 2 +- .../score/__snapshots__/score.test.tsx.snap | 4 +- .../common/components/ml/score/score.tsx | 2 +- .../common/components/tables/helpers.tsx | 2 +- .../alerts_by_type_panel/columns.tsx | 2 +- .../host_risk_score_table/columns.tsx | 2 +- .../hosts/components/hosts_table/columns.tsx | 6 +- .../components/network_dns_table/columns.tsx | 2 +- .../network_top_countries_table/columns.tsx | 2 +- .../network_top_n_flow_table/columns.tsx | 4 +- .../explore/network/pages/details/index.tsx | 2 +- .../user_risk_score_table/columns.tsx | 2 +- .../host_alerts_table/host_alerts_table.tsx | 139 ++++++++-- .../rule_alerts_table/rule_alerts_table.tsx | 29 +- .../user_alerts_table/user_alerts_table.tsx | 137 ++++++++-- .../entity_analytics/risk_score/columns.tsx | 34 ++- .../risk_score/index.test.tsx | 31 ++- .../field_renderers/field_renderers.tsx | 2 +- 37 files changed, 885 insertions(+), 196 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.test.ts create mode 100644 x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts diff --git a/packages/kbn-cell-actions/README.md b/packages/kbn-cell-actions/README.md index 7cacff6becda6..e1ae1a73a8369 100644 --- a/packages/kbn-cell-actions/README.md +++ b/packages/kbn-cell-actions/README.md @@ -6,7 +6,7 @@ Example: ```JSX [...] - + Hover me diff --git a/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx b/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx index 657dcc93416b8..49f812cd9005a 100644 --- a/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx +++ b/packages/kbn-cell-actions/src/__stories__/cell_actions.stories.tsx @@ -50,8 +50,8 @@ export const DefaultWithControls = CellActionsTemplate.bind({}); DefaultWithControls.argTypes = { mode: { - options: [CellActionsMode.HOVER, CellActionsMode.INLINE], - defaultValue: CellActionsMode.HOVER, + options: [CellActionsMode.HOVER_DOWN, CellActionsMode.INLINE], + defaultValue: CellActionsMode.HOVER_DOWN, control: { type: 'radio', }, @@ -72,8 +72,14 @@ export const CellActionInline = ({}: {}) => ( ); -export const CellActionHoverPopup = ({}: {}) => ( - +export const CellActionHoverPopoverDown = ({}: {}) => ( + + Hover me + +); + +export const CellActionHoverPopoverRight = ({}: {}) => ( + Hover me ); diff --git a/packages/kbn-cell-actions/src/components/cell_actions.test.tsx b/packages/kbn-cell-actions/src/components/cell_actions.test.tsx index ec266889fe255..36d0482ebf84d 100644 --- a/packages/kbn-cell-actions/src/components/cell_actions.test.tsx +++ b/packages/kbn-cell-actions/src/components/cell_actions.test.tsx @@ -15,6 +15,11 @@ import { CellActionsProvider } from '../context/cell_actions_context'; const TRIGGER_ID = 'test-trigger-id'; const FIELD = { name: 'name', value: '123', type: 'text' }; +jest.mock('./hover_actions_popover', () => ({ + HoverActionsPopover: jest.fn((props) => ( + {props.anchorPosition} + )), +})); describe('CellActions', () => { it('renders', async () => { const getActionsPromise = Promise.resolve([]); @@ -54,13 +59,33 @@ describe('CellActions', () => { expect(queryByTestId('inlineActions')).toBeInTheDocument(); }); - it('renders HoverActionsPopover when mode is HOVER', async () => { + it('renders HoverActionsPopover when mode is HOVER_DOWN', async () => { const getActionsPromise = Promise.resolve([]); const getActions = () => getActionsPromise; - const { queryByTestId } = render( + const { getByTestId } = render( + + + Field value + + + ); + + await act(async () => { + await getActionsPromise; + }); + + expect(getByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toHaveTextContent('downCenter'); + }); + + it('renders HoverActionsPopover when mode is HOVER_RIGHT', async () => { + const getActionsPromise = Promise.resolve([]); + const getActions = () => getActionsPromise; + + const { getByTestId } = render( - + Field value @@ -70,6 +95,7 @@ describe('CellActions', () => { await getActionsPromise; }); - expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toBeInTheDocument(); + expect(getByTestId('hoverActionsPopover')).toHaveTextContent('rightCenter'); }); }); diff --git a/packages/kbn-cell-actions/src/components/cell_actions.tsx b/packages/kbn-cell-actions/src/components/cell_actions.tsx index f6d3288ed6f7d..df6f957575c20 100644 --- a/packages/kbn-cell-actions/src/components/cell_actions.tsx +++ b/packages/kbn-cell-actions/src/components/cell_actions.tsx @@ -36,11 +36,17 @@ export const CellActions: React.FC = ({ [field, triggerId, metadata] ); + const anchorPosition = useMemo( + () => (mode === CellActionsMode.HOVER_DOWN ? 'downCenter' : 'rightCenter'), + [mode] + ); + const dataTestSubj = `cellActions-renderContent-${field.name}`; - if (mode === CellActionsMode.HOVER) { + if (mode === CellActionsMode.HOVER_DOWN || mode === CellActionsMode.HOVER_RIGHT) { return (

= ({ {children} {}, + actions: [], + button: , +}; describe('ExtraActionsPopOver', () => { it('renders', () => { - const { queryByTestId } = render( - {}} - actions={[]} - button={} - /> - ); + const { queryByTestId } = render(); expect(queryByTestId('extraActionsPopOver')).toBeInTheDocument(); }); @@ -33,11 +33,10 @@ describe('ExtraActionsPopOver', () => { const action = { ...makeAction('test-action'), execute: executeAction }; const { getByLabelText } = render( } /> ); @@ -56,13 +55,7 @@ describe('ExtraActionsPopOverWithAnchor', () => { it('renders', () => { const { queryByTestId } = render( - {}} - actions={[]} - anchorRef={{ current: anchorElement }} - /> + ); expect(queryByTestId('extraActionsPopOverWithAnchor')).toBeInTheDocument(); @@ -74,7 +67,7 @@ describe('ExtraActionsPopOverWithAnchor', () => { const action = { ...makeAction('test-action'), execute: executeAction }; const { getByLabelText } = render( void; @@ -32,6 +33,7 @@ interface ActionsPopOverProps { } export const ExtraActionsPopOver: React.FC = ({ + anchorPosition, actions, actionContext, isOpen, @@ -43,7 +45,7 @@ export const ExtraActionsPopOver: React.FC = ({ isOpen={isOpen} closePopover={closePopOver} panelPaddingSize="xs" - anchorPosition={'downCenter'} + anchorPosition={anchorPosition} hasArrow repositionOnScroll ownFocus @@ -59,11 +61,15 @@ export const ExtraActionsPopOver: React.FC = ({ ); interface ExtraActionsPopOverWithAnchorProps - extends Pick { + extends Pick< + ActionsPopOverProps, + 'anchorPosition' | 'actionContext' | 'closePopOver' | 'isOpen' | 'actions' + > { anchorRef: React.RefObject; } export const ExtraActionsPopOverWithAnchor = ({ + anchorPosition, anchorRef, actionContext, isOpen, @@ -77,7 +83,7 @@ export const ExtraActionsPopOverWithAnchor = ({ isOpen={isOpen} closePopover={closePopOver} panelPaddingSize="xs" - anchorPosition={'downCenter'} + anchorPosition={anchorPosition} hasArrow={false} repositionOnScroll ownFocus diff --git a/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx b/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx index b30ca63e52ec0..de68ccd6fca51 100644 --- a/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx +++ b/packages/kbn-cell-actions/src/components/hover_actions_popover.test.tsx @@ -13,11 +13,17 @@ import { makeAction } from '../mocks/helpers'; import { CellActionExecutionContext } from '../types'; import { HoverActionsPopover } from './hover_actions_popover'; -describe('HoverActionsPopover', () => { - const actionContext = { +const defaultProps = { + anchorPosition: 'rightCenter' as const, + disabledActionTypes: [], + visibleCellActions: 4, + actionContext: { trigger: { id: 'triggerId' }, field: { name: 'fieldName' }, - } as CellActionExecutionContext; + } as CellActionExecutionContext, + showActionTooltips: false, +}; +describe('HoverActionsPopover', () => { const TestComponent = () => ; jest.useFakeTimers(); @@ -25,13 +31,7 @@ describe('HoverActionsPopover', () => { const getActions = () => Promise.resolve([]); const { queryByTestId } = render( - + ); expect(queryByTestId('hoverActionsPopover')).toBeInTheDocument(); @@ -44,12 +44,7 @@ describe('HoverActionsPopover', () => { const { queryByLabelText, getByTestId } = render( - + @@ -70,12 +65,7 @@ describe('HoverActionsPopover', () => { const { queryByLabelText, getByTestId } = render( - + @@ -101,12 +91,7 @@ describe('HoverActionsPopover', () => { const { getByTestId } = render( - + @@ -127,12 +112,7 @@ describe('HoverActionsPopover', () => { const { getByTestId, getByLabelText } = render( - + @@ -162,12 +142,7 @@ describe('HoverActionsPopover', () => { const { getByTestId, queryByLabelText } = render( - + @@ -191,6 +166,44 @@ describe('HoverActionsPopover', () => { expect(queryByLabelText('test-action-2')).toBeInTheDocument(); expect(queryByLabelText('test-action-3')).toBeInTheDocument(); }); + it('does not add css positioning when anchorPosition = downCenter', async () => { + const getActionsPromise = Promise.resolve([makeAction('test-action')]); + const getActions = () => getActionsPromise; + + const { getByLabelText, getByTestId } = render( + + + + + + ); + + await hoverElement(getByTestId('test-component'), async () => { + await getActionsPromise; + jest.runAllTimers(); + }); + + expect(Object.values(getByLabelText('Actions').style).includes('margin-top')).toEqual(false); + }); + it('adds css positioning when anchorPosition = rightCenter', async () => { + const getActionsPromise = Promise.resolve([makeAction('test-action')]); + const getActions = () => getActionsPromise; + + const { getByLabelText, getByTestId } = render( + + + + + + ); + + await hoverElement(getByTestId('test-component'), async () => { + await getActionsPromise; + jest.runAllTimers(); + }); + + expect(Object.values(getByLabelText('Actions').style).includes('margin-top')).toEqual(true); + }); }); const hoverElement = async (element: Element, waitForChange: () => Promise) => { diff --git a/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx b/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx index 62ea3e766eaf7..dd087aa755307 100644 --- a/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx +++ b/packages/kbn-cell-actions/src/components/hover_actions_popover.tsx @@ -36,6 +36,7 @@ const hoverContentWrapperCSS = css` const HOVER_INTENT_DELAY = 100; // ms interface Props { + anchorPosition: 'downCenter' | 'rightCenter'; children: React.ReactNode; visibleCellActions: number; actionContext: CellActionExecutionContext; @@ -44,6 +45,7 @@ interface Props { } export const HoverActionsPopover: React.FC = ({ + anchorPosition, children, visibleCellActions, actionContext, @@ -115,12 +117,17 @@ export const HoverActionsPopover: React.FC = ({ ); }, [onMouseEnter, closeExtraActions, children]); + const panelStyle = useMemo( + () => (anchorPosition === 'rightCenter' ? { marginTop: euiThemeVars.euiSizeS } : {}), + [anchorPosition] + ); + return ( <>
= ({
{ - const actionContext = { trigger: { id: 'triggerId' } } as CellActionExecutionContext; it('renders', async () => { const getActionsPromise = Promise.resolve([]); const getActions = () => getActionsPromise; const { queryByTestId } = render( - + ); @@ -47,12 +48,7 @@ describe('InlineActions', () => { const getActions = () => getActionsPromise; const { queryAllByRole } = render( - + ); diff --git a/packages/kbn-cell-actions/src/components/inline_actions.tsx b/packages/kbn-cell-actions/src/components/inline_actions.tsx index cd2cfcc88edf0..c46fb5c57af76 100644 --- a/packages/kbn-cell-actions/src/components/inline_actions.tsx +++ b/packages/kbn-cell-actions/src/components/inline_actions.tsx @@ -17,6 +17,7 @@ import { useLoadActions } from '../hooks/use_load_actions'; interface InlineActionsProps { actionContext: CellActionExecutionContext; + anchorPosition: 'rightCenter' | 'downCenter'; showActionTooltips: boolean; visibleCellActions: number; disabledActionTypes: string[]; @@ -24,6 +25,7 @@ interface InlineActionsProps { export const InlineActions: React.FC = ({ actionContext, + anchorPosition, showActionTooltips, visibleCellActions, disabledActionTypes, @@ -47,10 +49,9 @@ export const InlineActions: React.FC = ({ data-test-subj="inlineActions" className={`inlineActions ${isPopoverOpen ? 'inlineActions-popoverOpen' : ''}`} > - {visibleActions.map((action, index) => ( - + {visibleActions.map((action) => ( + = ({ { + const addToTimelineCellActionFactory = createAddToNewTimelineCellActionFactory({ + store, + services, + }); + const addToTimelineAction = addToTimelineCellActionFactory({ id: 'testAddToTimeline', order: 1 }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return display name', () => { + expect(addToTimelineAction.getDisplayName(context)).toEqual('Investigate in timeline'); + }); + + it('should return icon type', () => { + expect(addToTimelineAction.getIconType(context)).toEqual('timeline'); + }); + + describe('isCompatible', () => { + it('should return true if everything is okay', async () => { + expect(await addToTimelineAction.isCompatible(context)).toEqual(true); + }); + it('should return false if field not allowed', async () => { + expect( + await addToTimelineAction.isCompatible({ + ...context, + field: { ...context.field, name: 'signal.reason' }, + }) + ).toEqual(false); + }); + }); + + describe('execute', () => { + it('should execute normally', async () => { + await addToTimelineAction.execute(context); + expect(mockDispatch).toHaveBeenCalledWith(defaultAddProviderAction); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + + it('should show warning if no provider added', async () => { + await addToTimelineAction.execute({ + ...context, + field: { + ...context.field, + type: GEO_FIELD_TYPE, + }, + }); + expect(mockDispatch).not.toHaveBeenCalled(); + expect(mockWarningToast).toHaveBeenCalled(); + }); + + describe('should execute correctly when negateFilters is provided', () => { + it('should not exclude if negateFilters is false', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + negateFilters: false, + }, + }); + expect(mockDispatch).toHaveBeenCalledWith(defaultAddProviderAction); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + + it('should exclude if negateFilters is true', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + negateFilters: true, + }, + }); + expect(mockDispatch).toHaveBeenCalledWith({ + ...defaultAddProviderAction, + payload: { + ...defaultAddProviderAction.payload, + providers: [{ ...defaultAddProviderAction.payload.providers[0], excluded: true }], + }, + }); + expect(mockWarningToast).not.toHaveBeenCalled(); + }); + }); + + it('should clear the timeline', async () => { + await addToTimelineAction.execute(context); + expect(mockDispatch.mock.calls[0][0].type).toEqual(timelineActions.createTimeline.type); + }); + + it('should add the providers to the timeline', async () => { + await addToTimelineAction.execute({ + ...context, + metadata: { + andFilters: [{ field: 'kibana.alert.severity', value: 'low' }], + }, + }); + + expect(mockDispatch).toBeCalledWith({ + ...defaultAddProviderAction, + payload: { + ...defaultAddProviderAction.payload, + providers: [ + { + ...defaultAddProviderAction.payload.providers[0], + id: 'event-field-default-timeline-1-user_name-0-the-value', + queryMatch: defaultAddProviderAction.payload.providers[0].queryMatch, + and: [ + { + enabled: true, + excluded: false, + id: 'event-field-default-timeline-1-kibana_alert_severity-0-low', + kqlQuery: '', + name: 'kibana.alert.severity', + queryMatch: { + field: 'kibana.alert.severity', + operator: ':', + value: 'low', + }, + and: [], + }, + ], + }, + ], + }, + }); + }); + }); + + describe('getToastMessage', () => { + it('handles empty input', () => { + const result = getToastMessage({ queryMatch: { value: null } } as unknown as DataProvider); + expect(result).toEqual(''); + }); + it('handles array input', () => { + const result = getToastMessage({ + queryMatch: { value: ['hello', 'world'] }, + } as unknown as DataProvider); + expect(result).toEqual('hello, world alerts'); + }); + + it('handles single filter', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [{ queryMatch: { field: 'kibana.alert.severity', value: 'critical' } }], + } as unknown as DataProvider); + expect(result).toEqual(`critical severity alerts from ${value}`); + }); + + it('handles multiple filters', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [ + { + queryMatch: { field: 'kibana.alert.workflow_status', value: 'open' }, + }, + { + queryMatch: { field: 'kibana.alert.severity', value: 'critical' }, + }, + ], + } as unknown as DataProvider); + expect(result).toEqual(`open, critical severity alerts from ${value}`); + }); + + it('ignores unrelated filters', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [ + { + queryMatch: { field: 'kibana.alert.workflow_status', value: 'open' }, + }, + { + queryMatch: { field: 'kibana.alert.severity', value: 'critical' }, + }, + // currently only supporting the above fields + { + queryMatch: { field: 'user.name', value: 'something' }, + }, + ], + } as unknown as DataProvider); + expect(result).toEqual(`open, critical severity alerts from ${value}`); + }); + + it('returns entity only when unrelated filters are passed', () => { + const result = getToastMessage({ + queryMatch: { value }, + and: [{ queryMatch: { field: 'user.name', value: 'something' } }], + } as unknown as DataProvider); + expect(result).toEqual(`${value} alerts`); + }); + + it('returns entity only when no filters are passed', () => { + const result = getToastMessage({ queryMatch: { value }, and: [] } as unknown as DataProvider); + expect(result).toEqual(`${value} alerts`); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts new file mode 100644 index 0000000000000..886162803bbf1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_new_timeline.ts @@ -0,0 +1,117 @@ +/* + * 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 { createCellActionFactory, type CellActionTemplate } from '@kbn/cell-actions'; +import { timelineActions } from '../../../timelines/store/timeline'; +import { addProvider } from '../../../timelines/store/timeline/actions'; +import type { DataProvider } from '../../../../common/types'; +import { TimelineId } from '../../../../common/types'; +import type { SecurityAppStore } from '../../../common/store'; +import { fieldHasCellActions } from '../../utils'; +import { + ADD_TO_NEW_TIMELINE, + ADD_TO_TIMELINE_FAILED_TEXT, + ADD_TO_TIMELINE_FAILED_TITLE, + ADD_TO_TIMELINE_ICON, + ADD_TO_TIMELINE_SUCCESS_TITLE, + ALERTS_COUNT, + SEVERITY, +} from '../constants'; +import { createDataProviders, isValidDataProviderField } from '../data_provider'; +import { SecurityCellActionType } from '../../constants'; +import type { StartServices } from '../../../types'; +import type { SecurityCellAction } from '../../types'; +import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; + +const severityField = 'kibana.alert.severity'; +const statusField = 'kibana.alert.workflow_status'; + +export const getToastMessage = ({ queryMatch: { value }, and = [] }: DataProvider) => { + if (value == null) { + return ''; + } + const fieldValue = Array.isArray(value) ? value.join(', ') : value.toString(); + + const descriptors = and.reduce((msg, { queryMatch }) => { + if (Array.isArray(queryMatch.value)) { + return msg; + } + if (queryMatch.field === severityField) { + msg.push(SEVERITY(queryMatch.value.toString())); + } + if (queryMatch.field === statusField) { + msg.push(queryMatch.value.toString()); + } + return msg; + }, []); + + return ALERTS_COUNT(fieldValue, descriptors.join(', ')); +}; + +export const createAddToNewTimelineCellActionFactory = createCellActionFactory( + ({ + store, + services, + }: { + store: SecurityAppStore; + services: StartServices; + }): CellActionTemplate => { + const { notifications: notificationsService } = services; + + return { + type: SecurityCellActionType.ADD_TO_TIMELINE, + getIconType: () => ADD_TO_TIMELINE_ICON, + getDisplayName: () => ADD_TO_NEW_TIMELINE, + getDisplayNameTooltip: () => ADD_TO_NEW_TIMELINE, + isCompatible: async ({ field }) => + fieldHasCellActions(field.name) && isValidDataProviderField(field.name, field.type), + execute: async ({ field, metadata }) => { + const dataProviders = + createDataProviders({ + contextId: TimelineId.active, + fieldType: field.type, + values: field.value, + field: field.name, + negate: metadata?.negateFilters === true, + }) ?? []; + + for (const andFilter of metadata?.andFilters ?? []) { + const andDataProviders = + createDataProviders({ + contextId: TimelineId.active, + field: andFilter.field, + values: andFilter.value, + }) ?? []; + if (andDataProviders) { + for (const dataProvider of dataProviders) { + dataProvider.and.push(...andDataProviders); + } + } + } + + if (dataProviders.length > 0) { + // clear timeline + store.dispatch( + timelineActions.createTimeline({ + ...timelineDefaults, + id: TimelineId.active, + }) + ); + store.dispatch(addProvider({ id: TimelineId.active, providers: dataProviders })); + notificationsService.toasts.addSuccess({ + title: ADD_TO_TIMELINE_SUCCESS_TITLE(getToastMessage(dataProviders[0])), + }); + } else { + notificationsService.toasts.addWarning({ + title: ADD_TO_TIMELINE_FAILED_TITLE, + text: ADD_TO_TIMELINE_FAILED_TEXT, + }); + } + }, + }; + } +); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts index c122d7e312e4d..0396cad110367 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/constants.ts @@ -15,6 +15,29 @@ export const ADD_TO_TIMELINE = i18n.translate( defaultMessage: 'Add to timeline', } ); +export const ADD_TO_NEW_TIMELINE = i18n.translate( + 'xpack.securitySolution.actions.cellValue.addToNewTimeline.displayName', + { + defaultMessage: 'Investigate in timeline', + } +); + +export const SEVERITY = (level: string) => + i18n.translate('xpack.securitySolution.actions.addToTimeline.severityLevel', { + values: { level }, + defaultMessage: `{level} severity`, + }); + +export const ALERTS_COUNT = (entity: string, description: string) => + description !== '' + ? i18n.translate('xpack.securitySolution.actions.addToTimeline.descriptiveAlertsCountMessage', { + values: { description, entity }, + defaultMessage: '{description} alerts from {entity}', + }) + : i18n.translate('xpack.securitySolution.actions.addToTimeline.alertsCountMessage', { + values: { entity }, + defaultMessage: '{entity} alerts', + }); export const ADD_TO_TIMELINE_SUCCESS_TITLE = (value: string) => i18n.translate('xpack.securitySolution.actions.addToTimeline.addedFieldMessage', { diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts index c639dde1e2337..72e6eee17e4d4 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/index.ts @@ -6,4 +6,5 @@ */ export { createAddToTimelineCellActionFactory } from './cell_action/add_to_timeline'; +export { createAddToNewTimelineCellActionFactory } from './cell_action/add_to_new_timeline'; export { createAddToTimelineLensAction } from './lens/add_to_timeline'; diff --git a/x-pack/plugins/security_solution/public/actions/constants.ts b/x-pack/plugins/security_solution/public/actions/constants.ts index 94c2601222847..eff71171fbcea 100644 --- a/x-pack/plugins/security_solution/public/actions/constants.ts +++ b/x-pack/plugins/security_solution/public/actions/constants.ts @@ -7,6 +7,7 @@ export enum SecurityCellActionsTrigger { DEFAULT = 'security-default-cellActions', DETAILS_FLYOUT = 'security-detailsFlyout-cellActions', + ALERTS_COUNT = 'security-alertsCount-cellActions', } export enum SecurityCellActionType { diff --git a/x-pack/plugins/security_solution/public/actions/register.ts b/x-pack/plugins/security_solution/public/actions/register.ts index 2df581342f614..bfea8d27163c4 100644 --- a/x-pack/plugins/security_solution/public/actions/register.ts +++ b/x-pack/plugins/security_solution/public/actions/register.ts @@ -13,6 +13,7 @@ import { createFilterInCellActionFactory, createFilterOutCellActionFactory } fro import { createAddToTimelineLensAction, createAddToTimelineCellActionFactory, + createAddToNewTimelineCellActionFactory, } from './add_to_timeline'; import { createShowTopNCellActionFactory } from './show_top_n'; import { @@ -52,6 +53,7 @@ const registerCellActions = ( filterIn: createFilterInCellActionFactory({ store, services }), filterOut: createFilterOutCellActionFactory({ store, services }), addToTimeline: createAddToTimelineCellActionFactory({ store, services }), + addToNewTimeline: createAddToNewTimelineCellActionFactory({ store, services }), showTopN: createShowTopNCellActionFactory({ store, history, services }), copyToClipboard: createCopyToClipboardCellActionFactory({ services }), toggleColumn: createToggleColumnCellActionFactory({ store }), @@ -77,6 +79,13 @@ const registerCellActions = ( ], services, }); + + registerCellActionsTrigger({ + triggerId: SecurityCellActionsTrigger.ALERTS_COUNT, + cellActions, + actionsOrder: ['addToNewTimeline'], + services, + }); }; const registerCellActionsTrigger = ({ @@ -95,8 +104,10 @@ const registerCellActionsTrigger = ({ actionsOrder.forEach((actionName, order) => { const actionFactory = cellActions[actionName]; - const action = actionFactory({ id: `${triggerId}-${actionName}`, order }); + if (actionFactory) { + const action = actionFactory({ id: `${triggerId}-${actionName}`, order }); - uiActions.addTriggerAction(triggerId, enhanceActionWithTelemetry(action, services)); + uiActions.addTriggerAction(triggerId, enhanceActionWithTelemetry(action, services)); + } }); }; diff --git a/x-pack/plugins/security_solution/public/actions/types.ts b/x-pack/plugins/security_solution/public/actions/types.ts index 1d15896ae6b35..e0cd6e764f3b8 100644 --- a/x-pack/plugins/security_solution/public/actions/types.ts +++ b/x-pack/plugins/security_solution/public/actions/types.ts @@ -6,6 +6,12 @@ */ import type { CellAction, CellActionExecutionContext, CellActionFactory } from '@kbn/cell-actions'; +import type { QueryOperator } from '../../common/types'; +export interface AndFilter { + field: string; + value: string | string[]; + operator?: QueryOperator; +} export interface SecurityMetadata extends Record { /** @@ -35,6 +41,11 @@ export interface SecurityMetadata extends Record { */ component: string; }; + /** + * `metadata.andFilters` is used by the addToTimelineAction to add + * an "and" query to the main data provider + */ + andFilters?: AndFilter[]; } export interface SecurityCellActionExecutionContext extends CellActionExecutionContext { @@ -42,13 +53,15 @@ export interface SecurityCellActionExecutionContext extends CellActionExecutionC } export type SecurityCellAction = CellAction; -// All security cell actions names -export type SecurityCellActionName = - | 'filterIn' - | 'filterOut' - | 'addToTimeline' - | 'showTopN' - | 'copyToClipboard' - | 'toggleColumn'; +export interface SecurityCellActions { + filterIn?: CellActionFactory; + filterOut?: CellActionFactory; + addToTimeline?: CellActionFactory; + addToNewTimeline?: CellActionFactory; + showTopN?: CellActionFactory; + copyToClipboard?: CellActionFactory; + toggleColumn?: CellActionFactory; +} -export type SecurityCellActions = Record; +// All security cell actions names +export type SecurityCellActionName = keyof SecurityCellActions; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap index 941078e41f917..4cc7aad784cc8 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/__snapshots__/entity.test.tsx.snap @@ -10,7 +10,7 @@ exports[`entity_draggable renders correctly against snapshot 1`] = ` "value": "entity-value", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx index 00a8c0cdb3f39..a50ea1e1f47f5 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/entity.tsx @@ -23,7 +23,7 @@ export const EntityComponent: React.FC = ({ entityName, entityValue }) => aggregatable: true, }} triggerId={SecurityCellActionsTrigger.DEFAULT} - mode={CellActionsMode.HOVER} + mode={CellActionsMode.HOVER_DOWN} visibleCellActions={5} > {`${entityName}: "${entityValue}"`} diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap index 08e1bbe2bfc80..2d9d6a69af1f0 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/score.test.tsx.snap @@ -10,7 +10,7 @@ exports[`draggable_score renders correctly against snapshot 1`] = ` "value": "du", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > @@ -28,7 +28,7 @@ exports[`draggable_score renders correctly against snapshot when the index is no "value": "du", } } - mode="hover" + mode="hover-down" triggerId="security-default-cellActions" visibleCellActions={5} > diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx index 3ad058dd2ab33..4e5fc8b29190b 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/score.tsx @@ -26,7 +26,7 @@ export const ScoreComponent = ({ return ( 0) { return ( 0) { return ( [ return ( { title={ diff --git a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx index d8d5abb4981c0..e6af3c9a873f7 100644 --- a/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/components/user_risk_score_table/columns.tsx @@ -41,7 +41,7 @@ export const getUserRiskScoreColumns = ({ return ( [ name: i18n.ALERTS_TEXT, 'data-test-subj': 'hostSeverityAlertsTable-totalAlerts', render: (totalAlerts: number, { hostName }) => ( - handleClick({ hostName })} + - - + handleClick({ hostName })} + > + + + ), }, { @@ -157,13 +176,30 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_CRITICAL_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'critical' })} + - - + handleClick({ hostName, severity: 'critical' })} + > + + + ), }, @@ -172,9 +208,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_HIGH_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'high' })}> - - + + handleClick({ hostName, severity: 'high' })} + > + + + ), }, @@ -183,12 +239,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_MEDIUM_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'medium' })} + - - + handleClick({ hostName, severity: 'medium' })} + > + + + ), }, @@ -197,9 +270,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_LOW_LABEL, render: (count: number, { hostName }) => ( - handleClick({ hostName, severity: 'low' })}> - - + + handleClick({ hostName, severity: 'low' })} + > + + + ), }, diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx index 8b0f38d0e479f..6c60bfc727b46 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/rule_alerts_table/rule_alerts_table.tsx @@ -21,6 +21,8 @@ import { import { FormattedRelative } from '@kbn/i18n-react'; import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; +import { CellActionsMode } from '@kbn/cell-actions'; +import { SecurityCellActionsTrigger } from '../../../../actions/constants'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { HeaderSection } from '../../../../common/components/header_section'; @@ -36,6 +38,7 @@ import { HoverVisibilityContainer } from '../../../../common/components/hover_vi import { BUTTON_CLASS as INSPECT_BUTTON_CLASS } from '../../../../common/components/inspect'; import { LastUpdatedAt } from '../../../../common/components/last_updated_at'; import { FormattedCount } from '../../../../common/components/formatted_number'; +import { SecurityCellActions } from '../../../../common/components/cell_actions'; export interface RuleAlertsTableProps { signalIndexName: string | null; @@ -95,13 +98,27 @@ export const getTableColumns: GetTableColumns = ({ name: i18n.RULE_ALERTS_COLUMN_ALERT_COUNT, 'data-test-subj': 'severityRuleAlertsTable-alertCount', render: (alertCount: number, { name }) => ( - openRuleInAlertsPage(name)} + - - + openRuleInAlertsPage(name)} + > + + + ), }, { diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx index ddaf75cba0f8a..914c3c93ff240 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/user_alerts_table/user_alerts_table.tsx @@ -20,6 +20,8 @@ import { } from '@elastic/eui'; import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; +import { CellActionsMode } from '@kbn/cell-actions'; +import { SecurityCellActionsTrigger } from '../../../../actions/constants'; import { useNavigateToAlertsPageWithFilters } from '../../../../common/hooks/use_navigate_to_alerts_page_with_filters'; import { FormattedCount } from '../../../../common/components/formatted_number'; import { HeaderSection } from '../../../../common/components/header_section'; @@ -32,6 +34,7 @@ import * as i18n from '../translations'; import { ITEMS_PER_PAGE, SEVERITY_COLOR } from '../utils'; import type { UserAlertsItem } from './use_user_alerts_items'; import { useUserAlertsItems } from './use_user_alerts_items'; +import { SecurityCellActions } from '../../../../common/components/cell_actions'; interface UserAlertsTableProps { signalIndexName: string | null; @@ -142,13 +145,27 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.ALERTS_TEXT, 'data-test-subj': 'userSeverityAlertsTable-totalAlerts', render: (totalAlerts: number, { userName }) => ( - handleClick({ userName })} + - - + handleClick({ userName })} + > + + + ), }, { @@ -156,13 +173,30 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_CRITICAL_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'critical' })} + - - + handleClick({ userName, severity: 'critical' })} + > + + + ), }, @@ -171,9 +205,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_HIGH_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'high' })}> - - + + handleClick({ userName, severity: 'high' })} + > + + + ), }, @@ -182,12 +236,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_MEDIUM_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'medium' })} + - - + handleClick({ userName, severity: 'medium' })} + > + + + ), }, @@ -196,9 +267,29 @@ const getTableColumns: GetTableColumns = (handleClick) => [ name: i18n.STATUS_LOW_LABEL, render: (count: number, { userName }) => ( - handleClick({ userName, severity: 'low' })}> - - + + handleClick({ userName, severity: 'low' })} + > + + + ), }, diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx index eee34e79d99c6..27b069c45de75 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/columns.tsx @@ -140,17 +140,31 @@ export const getRiskScoreColumns = ( truncateText: false, mobileOptions: { show: true }, render: (alertCount: number, risk) => ( - - openEntityOnAlertsPage( - riskEntity === RiskScoreEntity.host ? risk.host.name : risk.user.name - ) - } + - - + + openEntityOnAlertsPage( + riskEntity === RiskScoreEntity.host ? risk.host.name : risk.user.name + ) + } + > + + + ), }, ]; diff --git a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx index 80a750a8bce14..7869a234ccd50 100644 --- a/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/entity_analytics/risk_score/index.test.tsx @@ -5,11 +5,10 @@ * 2.0. */ -import { render, fireEvent } from '@testing-library/react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; import React from 'react'; import { TestProviders } from '../../../../common/mock'; import { EntityAnalyticsRiskScores } from '.'; -import type { UserRiskScore } from '../../../../../common/search_strategy'; import { RiskScoreEntity, RiskSeverity } from '../../../../../common/search_strategy'; import type { SeverityCount } from '../../../../explore/components/risk_score/severity/types'; import { useRiskScore, useRiskScoreKpi } from '../../../../explore/containers/risk_score'; @@ -146,17 +145,17 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( expect(queryByTestId('entity_analytics_content')).not.toBeInTheDocument(); }); - it('renders alerts count', () => { + it('renders alerts count', async () => { mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); mockUseRiskScoreKpi.mockReturnValue({ severityCount: mockSeverityCount, loading: false, }); const alertsCount = 999; - const data: UserRiskScore[] = [ + const data = [ { '@timestamp': '1234567899', - user: { + [riskEntity]: { name: 'testUsermame', risk: { rule_risks: [], @@ -176,10 +175,12 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( ); - expect(queryByTestId('risk-score-alerts')).toHaveTextContent(alertsCount.toString()); + await waitFor(() => { + expect(queryByTestId('risk-score-alerts')).toHaveTextContent(alertsCount.toString()); + }); }); - it('navigates to alerts page with filters when alerts count is clicked', () => { + it('navigates to alerts page with filters when alerts count is clicked', async () => { mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() }); mockUseRiskScoreKpi.mockReturnValue({ severityCount: mockSeverityCount, @@ -211,13 +212,15 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( fireEvent.click(getByTestId('risk-score-alerts')); - expect(mockOpenAlertsPageWithFilters.mock.calls[0][0]).toEqual([ - { - title: riskEntity === RiskScoreEntity.host ? 'Host' : 'User', - fieldName: riskEntity === RiskScoreEntity.host ? 'host.name' : 'user.name', - selectedOptions: [name], - }, - ]); + await waitFor(() => { + expect(mockOpenAlertsPageWithFilters.mock.calls[0][0]).toEqual([ + { + title: riskEntity === RiskScoreEntity.host ? 'Host' : 'User', + fieldName: riskEntity === RiskScoreEntity.host ? 'host.name' : 'user.name', + selectedOptions: [name], + }, + ]); + }); }); } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx index 6238392e888be..fdbee5da33e9f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.tsx @@ -317,7 +317,7 @@ export const MoreContainer = React.memo( Date: Fri, 21 Apr 2023 22:45:37 +0200 Subject: [PATCH 08/29] [Security Solution] Store expandable flyout state in the url (#154703) --- .github/CODEOWNERS | 1 + package.json | 1 + packages/kbn-expandable-flyout/index.ts | 8 +- .../kbn-expandable-flyout/src/context.tsx | 78 ++++++++++---- packages/kbn-expandable-flyout/src/reducer.ts | 3 + packages/kbn-url-state/README.md | 45 ++++++++ packages/kbn-url-state/index.test.ts | 101 ++++++++++++++++++ packages/kbn-url-state/index.ts | 9 ++ packages/kbn-url-state/jest.config.js | 13 +++ packages/kbn-url-state/kibana.jsonc | 5 + packages/kbn-url-state/package.json | 6 ++ packages/kbn-url-state/tsconfig.json | 21 ++++ packages/kbn-url-state/use_sync_to_url.ts | 87 +++++++++++++++ tsconfig.base.json | 2 + .../alert_details_url_sync.cy.ts | 55 ++++++++++ .../screens/document_expandable_flyout.ts | 3 + .../app/home/template_wrapper/index.tsx | 15 ++- .../public/common/hooks/use_url_state.ts | 3 + .../use_sync_flyout_state_with_url.test.tsx | 77 +++++++++++++ .../url/use_sync_flyout_state_with_url.tsx | 45 ++++++++ .../plugins/security_solution/tsconfig.json | 5 +- yarn.lock | 4 + 22 files changed, 563 insertions(+), 24 deletions(-) create mode 100644 packages/kbn-url-state/README.md create mode 100644 packages/kbn-url-state/index.test.ts create mode 100644 packages/kbn-url-state/index.ts create mode 100644 packages/kbn-url-state/jest.config.js create mode 100644 packages/kbn-url-state/kibana.jsonc create mode 100644 packages/kbn-url-state/package.json create mode 100644 packages/kbn-url-state/tsconfig.json create mode 100644 packages/kbn-url-state/use_sync_to_url.ts create mode 100644 x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts create mode 100644 x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e6cd6ff041cd0..9b3a1b7a7909b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -683,6 +683,7 @@ src/plugins/unified_search @elastic/kibana-visualizations x-pack/plugins/upgrade_assistant @elastic/platform-deployment-management x-pack/plugins/drilldowns/url_drilldown @elastic/kibana-app-services src/plugins/url_forwarding @elastic/kibana-visualizations +packages/kbn-url-state @elastic/security-threat-hunting-investigations src/plugins/usage_collection @elastic/kibana-core test/plugin_functional/plugins/usage_collection @elastic/kibana-core packages/kbn-user-profile-components @elastic/kibana-security diff --git a/package.json b/package.json index 58b87303d4a02..b7dc6a0b647d6 100644 --- a/package.json +++ b/package.json @@ -672,6 +672,7 @@ "@kbn/upgrade-assistant-plugin": "link:x-pack/plugins/upgrade_assistant", "@kbn/url-drilldown-plugin": "link:x-pack/plugins/drilldowns/url_drilldown", "@kbn/url-forwarding-plugin": "link:src/plugins/url_forwarding", + "@kbn/url-state": "link:packages/kbn-url-state", "@kbn/usage-collection-plugin": "link:src/plugins/usage_collection", "@kbn/usage-collection-test-plugin": "link:test/plugin_functional/plugins/usage_collection", "@kbn/user-profile-components": "link:packages/kbn-user-profile-components", diff --git a/packages/kbn-expandable-flyout/index.ts b/packages/kbn-expandable-flyout/index.ts index e2ce15d85a399..cc423eb275090 100644 --- a/packages/kbn-expandable-flyout/index.ts +++ b/packages/kbn-expandable-flyout/index.ts @@ -7,7 +7,13 @@ */ export { ExpandableFlyout } from './src'; -export { ExpandableFlyoutProvider, useExpandableFlyoutContext } from './src/context'; +export { + ExpandableFlyoutProvider, + useExpandableFlyoutContext, + type ExpandableFlyoutContext, +} from './src/context'; + +export type { ExpandableFlyoutApi } from './src/context'; export type { ExpandableFlyoutProps } from './src'; export type { FlyoutPanel } from './src/types'; diff --git a/packages/kbn-expandable-flyout/src/context.tsx b/packages/kbn-expandable-flyout/src/context.tsx index 89e8210e9578f..b7ad721a2b9fd 100644 --- a/packages/kbn-expandable-flyout/src/context.tsx +++ b/packages/kbn-expandable-flyout/src/context.tsx @@ -6,7 +6,15 @@ * Side Public License, v 1. */ -import React, { createContext, useCallback, useContext, useMemo, useReducer } from 'react'; +import React, { + createContext, + useCallback, + useContext, + useEffect, + useImperativeHandle, + useMemo, + useReducer, +} from 'react'; import { ActionType } from './actions'; import { reducer, State } from './reducer'; import type { FlyoutPanel } from './types'; @@ -59,19 +67,44 @@ export const ExpandableFlyoutContext = createContext & { + getState: () => State; +}; + export interface ExpandableFlyoutProviderProps { /** * React children */ children: React.ReactNode; + /** + * Triggered whenever flyout state changes. You can use it to store it's state somewhere for instance. + */ + onChanges?: (state: State) => void; + /** + * Triggered whenever flyout is closed. This is independent from the onChanges above. + */ + onClosePanels?: () => void; } /** * Wrap your plugin with this context for the ExpandableFlyout React component. */ -export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderProps) => { +export const ExpandableFlyoutProvider = React.forwardRef< + ExpandableFlyoutApi, + ExpandableFlyoutProviderProps +>(({ children, onChanges = () => {}, onClosePanels = () => {} }, ref) => { const [state, dispatch] = useReducer(reducer, initialState); + useEffect(() => { + const closed = !state.right; + if (closed) { + // manual close is singalled via separate callback + return; + } + + onChanges(state); + }, [state, onChanges]); + const openPanels = useCallback( ({ right, @@ -87,40 +120,45 @@ export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderP const openRightPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openRightPanel, payload: panel }), - [dispatch] + [] ); const openLeftPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openLeftPanel, payload: panel }), - [dispatch] + [] ); const openPreviewPanel = useCallback( (panel: FlyoutPanel) => dispatch({ type: ActionType.openPreviewPanel, payload: panel }), - [dispatch] + [] ); - const closeRightPanel = useCallback( - () => dispatch({ type: ActionType.closeRightPanel }), - [dispatch] - ); + const closeRightPanel = useCallback(() => dispatch({ type: ActionType.closeRightPanel }), []); - const closeLeftPanel = useCallback( - () => dispatch({ type: ActionType.closeLeftPanel }), - [dispatch] - ); + const closeLeftPanel = useCallback(() => dispatch({ type: ActionType.closeLeftPanel }), []); - const closePreviewPanel = useCallback( - () => dispatch({ type: ActionType.closePreviewPanel }), - [dispatch] - ); + const closePreviewPanel = useCallback(() => dispatch({ type: ActionType.closePreviewPanel }), []); const previousPreviewPanel = useCallback( () => dispatch({ type: ActionType.previousPreviewPanel }), - [dispatch] + [] ); - const closePanels = useCallback(() => dispatch({ type: ActionType.closeFlyout }), [dispatch]); + const closePanels = useCallback(() => { + dispatch({ type: ActionType.closeFlyout }); + onClosePanels(); + }, [onClosePanels]); + + useImperativeHandle( + ref, + () => { + return { + openFlyout: openPanels, + getState: () => state, + }; + }, + [openPanels, state] + ); const contextValue = useMemo( () => ({ @@ -154,7 +192,7 @@ export const ExpandableFlyoutProvider = ({ children }: ExpandableFlyoutProviderP {children} ); -}; +}); /** * Retrieve context's properties diff --git a/packages/kbn-expandable-flyout/src/reducer.ts b/packages/kbn-expandable-flyout/src/reducer.ts index 4901eccfc6bb4..bb0ff125f546e 100644 --- a/packages/kbn-expandable-flyout/src/reducer.ts +++ b/packages/kbn-expandable-flyout/src/reducer.ts @@ -105,5 +105,8 @@ export function reducer(state: State, action: Action) { preview: [], }; } + + default: + return state; } } diff --git a/packages/kbn-url-state/README.md b/packages/kbn-url-state/README.md new file mode 100644 index 0000000000000..e7b131e3743d5 --- /dev/null +++ b/packages/kbn-url-state/README.md @@ -0,0 +1,45 @@ +# @kbn/url-state - utils for syncing state to URL + +This package provides: + +- a React hook called `useSyncToUrl` that can be used to synchronize state to the URL. This can be useful when you want to make a portion of state shareable. + +## useSyncToUrl + +The `useSyncToUrl` hook takes three arguments: + +``` +key (string): The key to use in the URL to store the state. +restore (function): A function that is called with the deserialized value from the URL. You should use this function to update your state based on the value from the URL. +cleanupOnHistoryNavigation (optional boolean, default: true): If true, the hook will clear the URL state when the user navigates using the browser's history API. +``` + +### Example usage: + +``` +import React, { useState } from 'react'; +import { useSyncToUrl } from '@kbn/url-state'; + +function MyComponent() { + const [count, setCount] = useState(0); + + useSyncToUrl('count', (value) => { + setCount(value); + }); + + const handleClick = () => { + setCount((prevCount) => prevCount + 1); + }; + + return ( +
+

Count: {count}

+ +
+ ); +} +``` + +In this example, the count state is synced to the URL using the `useSyncToUrl` hook. +Whenever the count state changes, the hook will update the URL with the new value. +When the user copies the updated url or refreshes the page, `restore` function will be called to update the count state. \ No newline at end of file diff --git a/packages/kbn-url-state/index.test.ts b/packages/kbn-url-state/index.test.ts new file mode 100644 index 0000000000000..33dc285e100e5 --- /dev/null +++ b/packages/kbn-url-state/index.test.ts @@ -0,0 +1,101 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; +import { useSyncToUrl } from '.'; +import { encode } from '@kbn/rison'; + +describe('useSyncToUrl', () => { + let originalLocation: Location; + let originalHistory: History; + + beforeEach(() => { + originalLocation = window.location; + originalHistory = window.history; + delete (window as any).location; + delete (window as any).history; + + window.location = { + ...originalLocation, + search: '', + }; + window.history = { + ...originalHistory, + replaceState: jest.fn(), + }; + }); + + afterEach(() => { + window.location = originalLocation; + window.history = originalHistory; + }); + + it('should restore the value from the query string on mount', () => { + const key = 'testKey'; + const restoredValue = { test: 'value' }; + const encodedValue = encode(restoredValue); + const restore = jest.fn(); + + window.location.search = `?${key}=${encodedValue}`; + + renderHook(() => useSyncToUrl(key, restore)); + + expect(restore).toHaveBeenCalledWith(restoredValue); + }); + + it('should sync the value to the query string', () => { + const key = 'testKey'; + const valueToSerialize = { test: 'value' }; + + const { result } = renderHook(() => useSyncToUrl(key, jest.fn())); + + act(() => { + result.current(valueToSerialize); + }); + + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + '/?testKey=%28test%3Avalue%29' + ); + }); + + it('should clear the value from the query string on unmount', () => { + const key = 'testKey'; + + const { unmount } = renderHook(() => useSyncToUrl(key, jest.fn())); + + act(() => { + unmount(); + }); + + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + expect.any(String) + ); + }); + + it('should clear the value from the query string when history back or forward is pressed', () => { + const key = 'testKey'; + const restore = jest.fn(); + + renderHook(() => useSyncToUrl(key, restore, true)); + + act(() => { + window.dispatchEvent(new Event('popstate')); + }); + + expect(window.history.replaceState).toHaveBeenCalledTimes(1); + expect(window.history.replaceState).toHaveBeenCalledWith( + { path: expect.any(String) }, + '', + '/?' + ); + }); +}); diff --git a/packages/kbn-url-state/index.ts b/packages/kbn-url-state/index.ts new file mode 100644 index 0000000000000..73568222fb4c0 --- /dev/null +++ b/packages/kbn-url-state/index.ts @@ -0,0 +1,9 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export { useSyncToUrl } from './use_sync_to_url'; diff --git a/packages/kbn-url-state/jest.config.js b/packages/kbn-url-state/jest.config.js new file mode 100644 index 0000000000000..256a51239206c --- /dev/null +++ b/packages/kbn-url-state/jest.config.js @@ -0,0 +1,13 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-url-state'], +}; diff --git a/packages/kbn-url-state/kibana.jsonc b/packages/kbn-url-state/kibana.jsonc new file mode 100644 index 0000000000000..b0ab56d6af8b7 --- /dev/null +++ b/packages/kbn-url-state/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/url-state", + "owner": "@elastic/security-threat-hunting-investigations" +} diff --git a/packages/kbn-url-state/package.json b/packages/kbn-url-state/package.json new file mode 100644 index 0000000000000..2cd753f16b872 --- /dev/null +++ b/packages/kbn-url-state/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/url-state", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-url-state/tsconfig.json b/packages/kbn-url-state/tsconfig.json new file mode 100644 index 0000000000000..3bd03b7f37b84 --- /dev/null +++ b/packages/kbn-url-state/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/rison", + ] +} diff --git a/packages/kbn-url-state/use_sync_to_url.ts b/packages/kbn-url-state/use_sync_to_url.ts new file mode 100644 index 0000000000000..e38f9bc688a8b --- /dev/null +++ b/packages/kbn-url-state/use_sync_to_url.ts @@ -0,0 +1,87 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import { useCallback, useEffect } from 'react'; +import { encode, decode } from '@kbn/rison'; + +// https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event +const POPSTATE_EVENT = 'popstate' as const; + +/** + * Sync any object with browser query string using @knb/rison + * @param key query string param to use + * @param restore use this to handle restored state + * @param cleanupOnHistoryNavigation use history events to cleanup state on back / forward naviation. true by default + */ +export const useSyncToUrl = ( + key: string, + restore: (data: TValueToSerialize) => void, + cleanupOnHistoryNavigation = true +) => { + useEffect(() => { + const params = new URLSearchParams(window.location.search); + const param = params.get(key); + + if (!param) { + return; + } + + const decodedQuery = decode(param); + + if (!decodedQuery) { + return; + } + + // Only restore the value if it is not falsy + restore(decodedQuery as unknown as TValueToSerialize); + }, [key, restore]); + + /** + * Synces value with the url state, under specified key. If payload is undefined, the value will be removed from the query string althogether. + */ + const syncValueToQueryString = useCallback( + (valueToSerialize?: TValueToSerialize) => { + const searchParams = new URLSearchParams(window.location.search); + + if (valueToSerialize) { + const serializedPayload = encode(valueToSerialize); + searchParams.set(key, serializedPayload); + } else { + searchParams.delete(key); + } + + const newSearch = searchParams.toString(); + + // Update query string without unnecessary re-render + const newUrl = `${window.location.pathname}?${newSearch}`; + window.history.replaceState({ path: newUrl }, '', newUrl); + }, + [key] + ); + + // Clear remove state from the url on unmount / when history back or forward is pressed + useEffect(() => { + const clearState = () => { + syncValueToQueryString(undefined); + }; + + if (cleanupOnHistoryNavigation) { + window.addEventListener(POPSTATE_EVENT, clearState); + } + + return () => { + clearState(); + + if (cleanupOnHistoryNavigation) { + window.removeEventListener(POPSTATE_EVENT, clearState); + } + }; + }, [cleanupOnHistoryNavigation, syncValueToQueryString]); + + return syncValueToQueryString; +}; diff --git a/tsconfig.base.json b/tsconfig.base.json index 6bede7391b146..60c1f23d037b8 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1360,6 +1360,8 @@ "@kbn/url-drilldown-plugin/*": ["x-pack/plugins/drilldowns/url_drilldown/*"], "@kbn/url-forwarding-plugin": ["src/plugins/url_forwarding"], "@kbn/url-forwarding-plugin/*": ["src/plugins/url_forwarding/*"], + "@kbn/url-state": ["packages/kbn-url-state"], + "@kbn/url-state/*": ["packages/kbn-url-state/*"], "@kbn/usage-collection-plugin": ["src/plugins/usage_collection"], "@kbn/usage-collection-plugin/*": ["src/plugins/usage_collection/*"], "@kbn/usage-collection-test-plugin": ["test/plugin_functional/plugins/usage_collection"], diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts new file mode 100644 index 0000000000000..b7580642ba564 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_url_sync.cy.ts @@ -0,0 +1,55 @@ +/* + * 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 { getNewRule } from '../../../objects/rule'; +import { cleanKibana } from '../../../tasks/common'; +import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule'; +import { expandFirstAlertExpandableFlyout } from '../../../tasks/document_expandable_flyout'; +import { login, visit } from '../../../tasks/login'; +import { createRule } from '../../../tasks/api_calls/rules'; +import { ALERTS_URL } from '../../../urls/navigation'; +import { + DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON, + DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE, +} from '../../../screens/document_expandable_flyout'; + +// Skipping these for now as the feature is protected behind a feature flag set to false by default +// To run the tests locally, add 'securityFlyoutEnabled' in the Cypress config.ts here https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/config.ts#L50 +describe.skip('Expandable flyout state sync', { testIsolation: false }, () => { + const rule = getNewRule(); + + before(() => { + cleanKibana(); + login(); + createRule(rule); + visit(ALERTS_URL); + waitForAlertsToPopulate(); + expandFirstAlertExpandableFlyout(); + }); + + it('should serialize its state to url', () => { + cy.url().should('include', 'eventFlyout'); + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + }); + + it('should reopen the flyout after browser refresh', () => { + cy.reload(); + + cy.url().should('include', 'eventFlyout'); + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + }); + + it('should clear the url state when flyout is closed', () => { + cy.reload(); + + cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('be.visible').and('have.text', rule.name); + + cy.get(DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON).click(); + + cy.url().should('not.include', 'eventFlyout'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts index 06916d70b652a..5645a7de9f6ac 100644 --- a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts +++ b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts @@ -335,3 +335,6 @@ export const DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_ROW_CELL_ADD_TO_TIMELINE = getDataTestSubjectSelector('actionItem-security-detailsFlyout-cellActions-addToTimeline'); export const DOCUMENT_DETAILS_FLYOUT_TABLE_TAB_ROW_CELL_COPY_TO_CLIPBOARD = getDataTestSubjectSelector('actionItem-security-detailsFlyout-cellActions-copyToClipboard'); + +export const DOCUMENT_DETAILS_FLYOUT_CLOSE_BUTTON = + getDataTestSubjectSelector('euiFlyoutCloseButton'); diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index 264df10831fb7..a70a915e6aed4 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -27,6 +27,7 @@ import { useShowTimeline } from '../../../common/utils/timeline/use_show_timelin import { useShowPagesWithEmptyView } from '../../../common/utils/empty_view/use_show_pages_with_empty_view'; import { useIsPolicySettingsBarVisible } from '../../../management/pages/policy/view/policy_hooks'; import { useIsGroupedNavigationEnabled } from '../../../common/components/navigation/helpers'; +import { useSyncFlyoutStateWithUrl } from '../../../flyout/url/use_sync_flyout_state_with_url'; const NO_DATA_PAGE_MAX_WIDTH = 950; @@ -75,6 +76,8 @@ export const SecuritySolutionTemplateWrapper: React.FC + )} - {}} /> + {}} + handleOnFlyoutClosed={handleFlyoutChangedOrClosed} + /> ); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts b/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts index a5230c9ac599f..30d914f5ccc83 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts @@ -15,6 +15,9 @@ import { useQueryTimelineByIdOnUrlChange } from './timeline/use_query_timeline_b import { useInitFlyoutFromUrlParam } from './flyout/use_init_flyout_url_param'; import { useSyncFlyoutUrlParam } from './flyout/use_sync_flyout_url_param'; +// NOTE: the expandable flyout package url state is handled here: +// x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx + export const useUrlState = () => { useSyncGlobalQueryString(); useInitSearchBarFromUrlParams(); diff --git a/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx new file mode 100644 index 0000000000000..984b2a2e223dc --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.test.tsx @@ -0,0 +1,77 @@ +/* + * 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 { ExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { useSyncToUrl } from '@kbn/url-state'; +import { renderHook } from '@testing-library/react-hooks'; +import { useSyncFlyoutStateWithUrl } from './use_sync_flyout_state_with_url'; + +jest.mock('@kbn/url-state'); + +describe('useSyncFlyoutStateWithUrl', () => { + it('should return an array containing flyoutApi ref and handleFlyoutChanges function', () => { + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [flyoutApi, handleFlyoutChanges] = result.current; + + expect(flyoutApi.current).toBeNull(); + expect(typeof handleFlyoutChanges).toBe('function'); + }); + + it('should open flyout when relevant url state is detected in the query string', () => { + jest.useFakeTimers(); + + jest.mocked(useSyncToUrl).mockImplementation((_urlKey, callback) => { + setTimeout(() => callback({ mocked: { flyout: 'state' } }), 0); + return jest.fn(); + }); + + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [flyoutApi, handleFlyoutChanges] = result.current; + + const flyoutApiMock: ExpandableFlyoutApi = { + openFlyout: jest.fn(), + getState: () => ({ left: undefined, right: undefined, preview: [] }), + }; + + expect(typeof handleFlyoutChanges).toBe('function'); + expect(flyoutApi.current).toBeNull(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (flyoutApi as any).current = flyoutApiMock; + + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + + expect(flyoutApiMock.openFlyout).toHaveBeenCalledTimes(1); + expect(flyoutApiMock.openFlyout).toHaveBeenCalledWith({ mocked: { flyout: 'state' } }); + }); + + it('should sync flyout state to url whenever handleFlyoutChanges is called by the consumer', () => { + const syncStateToUrl = jest.fn(); + jest.mocked(useSyncToUrl).mockImplementation((_urlKey, callback) => { + setTimeout(() => callback({ mocked: { flyout: 'state' } }), 0); + return syncStateToUrl; + }); + + const { result } = renderHook(() => useSyncFlyoutStateWithUrl()); + const [_flyoutApi, handleFlyoutChanges] = result.current; + + handleFlyoutChanges(); + + expect(syncStateToUrl).toHaveBeenCalledTimes(1); + expect(syncStateToUrl).toHaveBeenLastCalledWith(undefined); + + handleFlyoutChanges({ left: undefined, right: undefined, preview: [] }); + + expect(syncStateToUrl).toHaveBeenLastCalledWith({ + left: undefined, + right: undefined, + preview: undefined, + }); + expect(syncStateToUrl).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx new file mode 100644 index 0000000000000..be1b28147f63d --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/url/use_sync_flyout_state_with_url.tsx @@ -0,0 +1,45 @@ +/* + * 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 { useCallback, useRef } from 'react'; +import type { ExpandableFlyoutApi, ExpandableFlyoutContext } from '@kbn/expandable-flyout'; +import { useSyncToUrl } from '@kbn/url-state'; +import last from 'lodash/last'; + +const URL_KEY = 'eventFlyout' as const; + +type FlyoutState = Parameters[0]; + +/** + * Sync flyout state with the url and open it when relevant url state is detected in the query string + * @returns [ref, flyoutChangesHandler] + */ +export const useSyncFlyoutStateWithUrl = () => { + const flyoutApi = useRef(null); + + const syncStateToUrl = useSyncToUrl(URL_KEY, (data) => { + flyoutApi.current?.openFlyout(data); + }); + + // This should be bound to flyout changed and closed events. + // When flyout is closed, url state is cleared + const handleFlyoutChanges = useCallback( + (state?: ExpandableFlyoutContext['panels']) => { + if (!state) { + return syncStateToUrl(undefined); + } + + return syncStateToUrl({ + ...state, + preview: last(state.preview), + }); + }, + [syncStateToUrl] + ); + + return [flyoutApi, handleFlyoutChanges] as const; +}; diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 622785a43b4e8..6b14dd7b616bf 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -23,7 +23,9 @@ ], "kbn_references": [ "@kbn/core", - { "path": "../../../src/setup_node_env/tsconfig.json" }, + { + "path": "../../../src/setup_node_env/tsconfig.json" + }, "@kbn/data-plugin", "@kbn/embeddable-plugin", "@kbn/files-plugin", @@ -153,5 +155,6 @@ "@kbn/security-solution-side-nav", "@kbn/core-lifecycle-browser", "@kbn/ecs", + "@kbn/url-state" ] } diff --git a/yarn.lock b/yarn.lock index 5ab19eee75bd9..38b7781f7b17a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5457,6 +5457,10 @@ version "0.0.0" uid "" +"@kbn/url-state@link:packages/kbn-url-state": + version "0.0.0" + uid "" + "@kbn/usage-collection-plugin@link:src/plugins/usage_collection": version "0.0.0" uid "" From 4eeec1865f845fd7a0c07875e726b656c0a3278a Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Fri, 21 Apr 2023 15:45:55 -0500 Subject: [PATCH 09/29] [Security Solution] add threat intelligence overview to expandable flyout (#155328) --- ...ert_details_right_panel_overview_tab.cy.ts | 36 +++ .../screens/document_expandable_flyout.ts | 12 + .../right/components/insights_section.tsx | 2 + .../insights_subsection.stories.tsx | 38 +++ .../components/insights_subsection.test.tsx | 67 ++++++ .../right/components/insights_subsection.tsx | 79 +++++++ .../insights_summary_panel.stories.tsx | 156 +++++++++++++ .../insights_summary_panel.test.tsx | 90 ++++++++ .../components/insights_summary_panel.tsx | 106 +++++++++ .../flyout/right/components/test_ids.ts | 12 + .../threat_intelligence_overview.test.tsx | 195 ++++++++++++++++ .../threat_intelligence_overview.tsx | 91 ++++++++ .../flyout/right/components/translations.ts | 42 ++++ .../use_fetch_threat_intelligence.test.tsx | 216 ++++++++++++++++++ .../hooks/use_fetch_threat_intelligence.ts | 108 +++++++++ 15 files changed, 1250 insertions(+) create mode 100644 x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts index c98c84b800079..57ea1a2d4efdb 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts @@ -29,6 +29,10 @@ import { DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_VIEW_ALL_ENTITIES_BUTTON, DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT, DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_ANALYZER_TREE, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES, + DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON, } from '../../../screens/document_expandable_flyout'; import { expandFirstAlertExpandableFlyout, @@ -180,6 +184,38 @@ describe.skip( .click(); cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); }); + + // TODO work on getting proper IoC data to make the threat intelligence section work here + it.skip('should display threat intelligence section', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER) + .scrollIntoView() + .should('be.visible') + .and('have.text', 'Threat Intelligence'); + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT) + .should('be.visible') + .within(() => { + // threat match detected + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES) + .eq(0) + .should('be.visible') + .and('have.text', '1 threat match detected'); // TODO + + // field with threat enrichement + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES) + .eq(1) + .should('be.visible') + .and('have.text', '1 field enriched with threat intelligence'); // TODO + }); + }); + + // TODO work on getting proper IoC data to make the threat intelligence section work here + // and improve when we can navigate Threat Intelligence to sub tab directly + it.skip('should navigate to left panel, entities tab when view all fields of threat intelligence is clicked', () => { + cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON) + .should('be.visible') + .click(); + cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB_ENTITIES_CONTENT).should('be.visible'); + }); }); describe('visualizations section', () => { diff --git a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts index 5645a7de9f6ac..dafd16932d194 100644 --- a/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts +++ b/x-pack/plugins/security_solution/cypress/screens/document_expandable_flyout.ts @@ -73,6 +73,10 @@ import { ENTITIES_VIEW_ALL_BUTTON_TEST_ID, VISUALIZATIONS_SECTION_HEADER_TEST_ID, ANALYZER_TREE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID, } from '../../public/flyout/right/components/test_ids'; import { getClassSelector, @@ -303,6 +307,14 @@ export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_HEADER = getDataTestSubjectSelector(ENTITY_PANEL_HEADER_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_ENTITY_PANEL_CONTENT = getDataTestSubjectSelector(ENTITY_PANEL_CONTENT_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_HEADER = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_CONTENT = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VALUES = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID); +export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON = + getDataTestSubjectSelector(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID); export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_VISUALIZATIONS_SECTION_HEADER = getDataTestSubjectSelector(VISUALIZATIONS_SECTION_HEADER_TEST_ID); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx index 8409676b610b0..9efb979ba7a26 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_section.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { ThreatIntelligenceOverview } from './threat_intelligence_overview'; import { INSIGHTS_TEST_ID } from './test_ids'; import { INSIGHTS_TITLE } from './translations'; import { EntitiesOverview } from './entities_overview'; @@ -25,6 +26,7 @@ export const InsightsSection: React.FC = ({ expanded = fal return ( + ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx new file mode 100644 index 0000000000000..c1fc9dfe8a7f8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.stories.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { Story } from '@storybook/react'; +import { InsightsSubSection } from './insights_subsection'; + +export default { + component: InsightsSubSection, + title: 'Flyout/InsightsSubSection', +}; + +const title = 'Title'; +const children =
{'hello'}
; + +export const Basic: Story = () => { + return {children}; +}; + +export const Loading: Story = () => { + return ( + + {null} + + ); +}; + +export const NoTitle: Story = () => { + return {children}; +}; + +export const NoChildren: Story = () => { + return {null}; +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx new file mode 100644 index 0000000000000..271953c8e8105 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.test.tsx @@ -0,0 +1,67 @@ +/* + * 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 React from 'react'; +import { render } from '@testing-library/react'; +import { InsightsSubSection } from './insights_subsection'; + +const title = 'Title'; +const dataTestSubj = 'test'; +const children =
{'hello'}
; + +describe('', () => { + it('should render children component', () => { + const { getByTestId } = render( + + {children} + + ); + + const titleDataTestSubj = `${dataTestSubj}Title`; + const contentDataTestSubj = `${dataTestSubj}Content`; + + expect(getByTestId(titleDataTestSubj)).toHaveTextContent(title); + expect(getByTestId(contentDataTestSubj)).toBeInTheDocument(); + }); + + it('should render loading component', () => { + const { getByTestId } = render( + + {children} + + ); + + const loadingDataTestSubj = `${dataTestSubj}Loading`; + expect(getByTestId(loadingDataTestSubj)).toBeInTheDocument(); + }); + + it('should render null if error', () => { + const { container } = render( + + {children} + + ); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null if no title', () => { + const { container } = render({children}); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null if no children', () => { + const { container } = render( + + {null} + + ); + + expect(container).toBeEmptyDOMElement(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx new file mode 100644 index 0000000000000..4b5c1a541e316 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_subsection.tsx @@ -0,0 +1,79 @@ +/* + * 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 React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSpacer, EuiTitle } from '@elastic/eui'; + +export interface InsightsSectionProps { + /** + * Renders a loading spinner if true + */ + loading?: boolean; + /** + * Returns a null component if true + */ + error?: boolean; + /** + * Title at the top of the component + */ + title: string; + /** + * Content of the component + */ + children: React.ReactNode; + /** + * Prefix data-test-subj to use for the elements + */ + ['data-test-subj']?: string; +} + +/** + * Presentational component to handle loading and error in the subsections of the Insights section. + * Should be used for Entities, Threat Intelligence, Prevalence, Correlations and Results + */ +export const InsightsSubSection: React.FC = ({ + loading = false, + error = false, + title, + 'data-test-subj': dataTestSubj, + children, +}) => { + const loadingDataTestSubj = `${dataTestSubj}Loading`; + // showing the loading in this component instead of SummaryPanel because we're hiding the entire section if no data + + if (loading) { + return ( + + + + + + ); + } + + // hide everything + if (error || !title || !children) { + return null; + } + + const titleDataTestSubj = `${dataTestSubj}Title`; + const contentDataTestSubj = `${dataTestSubj}Content`; + + return ( + <> + +
{title}
+
+ + + {children} + + + ); +}; + +InsightsSubSection.displayName = 'InsightsSubSection'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx new file mode 100644 index 0000000000000..5637d3c036860 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.stories.tsx @@ -0,0 +1,156 @@ +/* + * 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 React from 'react'; +import type { Story } from '@storybook/react'; +import { css } from '@emotion/react'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; + +export default { + component: InsightsSummaryPanel, + title: 'Flyout/InsightsSummaryPanel', +}; + +export const Default: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + { + icon: 'warning', + value: 2, + text: 'this is test for orange', + color: 'rgb(255,126,98)', + }, + { + icon: 'warning', + value: 3, + text: 'this is test for yellow', + color: 'rgb(241,216,11)', + }, + ]; + + return ( +
+ +
+ ); +}; + +export const InvalidColor: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for an invalid color (abc)', + color: 'abc', + }, + ]; + + return ( +
+ +
+ ); +}; + +export const NoColor: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + }, + { + icon: 'warning', + value: 2, + text: 'this is test for orange', + }, + { + icon: 'warning', + value: 3, + text: 'this is test for yellow', + }, + ]; + + return ( +
+ +
+ ); +}; + +export const LongText: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is an extremely long text to verify it is properly cut off and and we show three dots at the end', + color: 'abc', + }, + ]; + + return ( +
+ +
+ ); +}; +export const LongNumber: Story = () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is an extremely long value to verify it is properly cut off and and we show three dots at the end', + color: 'abc', + }, + ]; + + return ( +
+ +
+ ); +}; + +export const NoData: Story = () => { + const data: InsightsSummaryPanelData[] = []; + + return ( +
+ +
+ ); +}; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx new file mode 100644 index 0000000000000..9ecbbcc7fc0a5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.test.tsx @@ -0,0 +1,90 @@ +/* + * 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 React from 'react'; +import { render } from '@testing-library/react'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { + INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID, +} from './test_ids'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; + +describe('', () => { + it('should render by default', () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 1, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + ]; + + const { getByTestId } = render( + + + + ); + + const iconTestId = `${INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID}0`; + const valueTestId = `${INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID}0`; + const colorTestId = `${INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID}0`; + expect(getByTestId(iconTestId)).toBeInTheDocument(); + expect(getByTestId(valueTestId)).toHaveTextContent('1 this is a test for red'); + expect(getByTestId(colorTestId)).toBeInTheDocument(); + }); + + it('should only render null when data is null', () => { + const data = null as unknown as InsightsSummaryPanelData[]; + + const { container } = render(); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should handle big number in a compact notation', () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is a test for red', + color: 'rgb(189,39,30)', + }, + ]; + + const { getByTestId } = render( + + + + ); + + const valueTestId = `${INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID}0`; + expect(getByTestId(valueTestId)).toHaveTextContent('160k this is a test for red'); + }); + + it(`should not show the colored dot if color isn't provided`, () => { + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: 160000, + text: 'this is a test for no color', + }, + ]; + + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId(INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx new file mode 100644 index 0000000000000..306eaa101b804 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/insights_summary_panel.tsx @@ -0,0 +1,106 @@ +/* + * 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 { VFC } from 'react'; +import React from 'react'; +import { css } from '@emotion/react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiHealth, EuiPanel } from '@elastic/eui'; +import { FormattedCount } from '../../../common/components/formatted_number'; + +export interface InsightsSummaryPanelData { + /** + * Icon to display on the left side of each row + */ + icon: string; + /** + * Number of results/entries found + */ + value: number; + /** + * Text corresponding of the number of results/entries + */ + text: string; + /** + * Optional parameter for now, will be used to display a dot on the right side + * (corresponding to some sort of severity?) + */ + color?: string; // TODO remove optional when we have guidance on what the colors will actually be +} + +export interface InsightsSummaryPanelProps { + /** + * Array of data to display in each row + */ + data: InsightsSummaryPanelData[]; + /** + * Prefix data-test-subj because this component will be used in multiple places + */ + ['data-test-subj']?: string; +} + +/** + * Panel showing summary information as an icon, a count and text as well as a severity colored dot. + * Should be used for Entities, Threat Intelligence, Prevalence, Correlations and Results components under the Insights section. + * The colored dot is currently optional but will ultimately be mandatory (waiting on PM and UIUX). + */ +export const InsightsSummaryPanel: VFC = ({ + data, + 'data-test-subj': dataTestSubj, +}) => { + if (!data || data.length === 0) { + return null; + } + + const iconDataTestSubj = `${dataTestSubj}Icon`; + const valueDataTestSubj = `${dataTestSubj}Value`; + const colorDataTestSubj = `${dataTestSubj}Color`; + + return ( + + + {data.map((row, index) => ( + + + + + + {row.text} + + {row.color && ( + + + + )} + + ))} + + + ); +}; + +InsightsSummaryPanel.displayName = 'InsightsSummaryPanel'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts index e89c16b3f5f12..9ee38ba2940cb 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/test_ids.ts @@ -84,6 +84,18 @@ export const ENTITIES_HOST_OVERVIEW_IP_TEST_ID = export const ENTITIES_HOST_OVERVIEW_RISK_LEVEL_TEST_ID = 'securitySolutionDocumentDetailsFlyoutEntitiesHostOverviewRiskLevel'; +/* Insights Threat Intelligence */ + +export const INSIGHTS_THREAT_INTELLIGENCE_TEST_ID = + 'securitySolutionDocumentDetailsFlyoutInsightsThreatIntelligence'; +export const INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Title`; +export const INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Content`; +export const INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}ViewAllButton`; +export const INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Loading`; +export const INSIGHTS_THREAT_INTELLIGENCE_ICON_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Icon`; +export const INSIGHTS_THREAT_INTELLIGENCE_VALUE_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Value`; +export const INSIGHTS_THREAT_INTELLIGENCE_COLOR_TEST_ID = `${INSIGHTS_THREAT_INTELLIGENCE_TEST_ID}Color`; + /* Visualizations section*/ export const VISUALIZATIONS_SECTION_TEST_ID = 'securitySolutionDocumentDetailsVisualizationsTitle'; export const VISUALIZATIONS_SECTION_HEADER_TEST_ID = diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx new file mode 100644 index 0000000000000..ecf17f2c7e822 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.test.tsx @@ -0,0 +1,195 @@ +/* + * 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 React from 'react'; +import { render } from '@testing-library/react'; +import { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context'; +import { RightPanelContext } from '../context'; +import { + INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID, + INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID, +} from './test_ids'; +import { TestProviders } from '../../../common/mock'; +import { ThreatIntelligenceOverview } from './threat_intelligence_overview'; +import { LeftPanelInsightsTabPath, LeftPanelKey } from '../../left'; +import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence'; + +jest.mock('../hooks/use_fetch_threat_intelligence'); + +const panelContextValue = { + eventId: 'event id', + indexName: 'indexName', + dataFormattedForFieldBrowser: [], +} as unknown as RightPanelContext; + +const renderThreatIntelligenceOverview = (contextValue: RightPanelContext) => ( + + + + + +); + +describe('', () => { + it('should render 1 match detected and 1 field enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 1, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID)).toHaveTextContent( + 'Threat Intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '1 threat match detected' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '1 field enriched with threat intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID)).toBeInTheDocument(); + }); + + it('should render 2 matches detected and 2 fields enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 2, + threatEnrichmentsCount: 2, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_TITLE_TEST_ID)).toHaveTextContent( + 'Threat Intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '2 threat matches detected' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '2 fields enriched with threat intelligence' + ); + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID)).toBeInTheDocument(); + }); + + it('should render 0 field enriched', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 0, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '0 field enriched with threat intelligence' + ); + }); + + it('should render 0 match detected', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 0, + threatEnrichmentsCount: 2, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_CONTENT_TEST_ID)).toHaveTextContent( + '0 threat match detected' + ); + }); + + it('should render loading', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: true, + }); + + const { getByTestId } = render(renderThreatIntelligenceOverview(panelContextValue)); + + expect(getByTestId(INSIGHTS_THREAT_INTELLIGENCE_LOADING_TEST_ID)).toBeInTheDocument(); + }); + + it('should render null when eventId is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + }); + const contextValue = { + ...panelContextValue, + eventId: null, + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null when dataFormattedForFieldBrowser is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + error: true, + }); + const contextValue = { + ...panelContextValue, + dataFormattedForFieldBrowser: null, + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should render null when no enrichment found is null', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 0, + threatEnrichmentsCount: 0, + }); + const contextValue = { + ...panelContextValue, + dataFormattedForFieldBrowser: [], + } as unknown as RightPanelContext; + + const { container } = render(renderThreatIntelligenceOverview(contextValue)); + + expect(container).toBeEmptyDOMElement(); + }); + + it('should navigate to left section Insights tab when clicking on button', () => { + (useFetchThreatIntelligence as jest.Mock).mockReturnValue({ + loading: false, + threatMatchesCount: 1, + threatEnrichmentsCount: 1, + }); + const flyoutContextValue = { + openLeftPanel: jest.fn(), + } as unknown as ExpandableFlyoutContext; + + const { getByTestId } = render( + + + + + + + + ); + + getByTestId(INSIGHTS_THREAT_INTELLIGENCE_VIEW_ALL_BUTTON_TEST_ID).click(); + expect(flyoutContextValue.openLeftPanel).toHaveBeenCalledWith({ + id: LeftPanelKey, + path: LeftPanelInsightsTabPath, + params: { + id: panelContextValue.eventId, + indexName: panelContextValue.indexName, + }, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx new file mode 100644 index 0000000000000..63f0862a68b3a --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/components/threat_intelligence_overview.tsx @@ -0,0 +1,91 @@ +/* + * 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 React, { useCallback } from 'react'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; +import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence'; +import { InsightsSubSection } from './insights_subsection'; +import type { InsightsSummaryPanelData } from './insights_summary_panel'; +import { InsightsSummaryPanel } from './insights_summary_panel'; +import { useRightPanelContext } from '../context'; +import { INSIGHTS_THREAT_INTELLIGENCE_TEST_ID } from './test_ids'; +import { + VIEW_ALL, + THREAT_INTELLIGENCE_TITLE, + THREAT_INTELLIGENCE_TEXT, + THREAT_MATCH_DETECTED, + THREAT_ENRICHMENT, + THREAT_MATCHES_DETECTED, + THREAT_ENRICHMENTS, +} from './translations'; +import { LeftPanelKey, LeftPanelInsightsTabPath } from '../../left'; + +/** + * Threat Intelligence section under Insights section, overview tab. + * The component fetches the necessary data, then pass it down to the InsightsSubSection component for loading and error state, + * and the SummaryPanel component for data rendering. + */ +export const ThreatIntelligenceOverview: React.FC = () => { + const { eventId, indexName, dataFormattedForFieldBrowser } = useRightPanelContext(); + const { openLeftPanel } = useExpandableFlyoutContext(); + + const goToThreatIntelligenceTab = useCallback(() => { + openLeftPanel({ + id: LeftPanelKey, + path: LeftPanelInsightsTabPath, + params: { + id: eventId, + indexName, + }, + }); + }, [eventId, openLeftPanel, indexName]); + + const { loading, threatMatchesCount, threatEnrichmentsCount } = useFetchThreatIntelligence({ + dataFormattedForFieldBrowser, + }); + + const data: InsightsSummaryPanelData[] = [ + { + icon: 'image', + value: threatMatchesCount, + text: threatMatchesCount <= 1 ? THREAT_MATCH_DETECTED : THREAT_MATCHES_DETECTED, + }, + { + icon: 'warning', + value: threatEnrichmentsCount, + text: threatMatchesCount <= 1 ? THREAT_ENRICHMENT : THREAT_ENRICHMENTS, + }, + ]; + + const error: boolean = + !eventId || + !dataFormattedForFieldBrowser || + (threatMatchesCount === 0 && threatEnrichmentsCount === 0); + + return ( + + + + {VIEW_ALL(THREAT_INTELLIGENCE_TEXT)} + + + ); +}; + +ThreatIntelligenceOverview.displayName = 'ThreatIntelligenceOverview'; diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts index 1a6a2da7344c4..d5b9f0d1928b3 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts +++ b/x-pack/plugins/security_solution/public/flyout/right/components/translations.ts @@ -101,11 +101,18 @@ export const HIGHLIGHTED_FIELDS_TITLE = i18n.translate( { defaultMessage: 'Highlighted fields' } ); +/* Insights section */ + export const ENTITIES_TITLE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.entitiesTitle', { defaultMessage: 'Entities' } ); +export const THREAT_INTELLIGENCE_TITLE = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.threatIntelligenceTitle', + { defaultMessage: 'Threat Intelligence' } +); + export const INSIGHTS_TITLE = i18n.translate( 'xpack.securitySolution.flyout.documentDetails.insightsTitle', { defaultMessage: 'Insights' } @@ -131,6 +138,41 @@ export const ENTITIES_TEXT = i18n.translate( } ); +export const THREAT_INTELLIGENCE_TEXT = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligenceText', + { + defaultMessage: 'fields of threat intelligence', + } +); + +export const THREAT_MATCH_DETECTED = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatMatch', + { + defaultMessage: `threat match detected`, + } +); + +export const THREAT_MATCHES_DETECTED = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatMatches', + { + defaultMessage: `threat matches detected`, + } +); + +export const THREAT_ENRICHMENT = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatEnrichment', + { + defaultMessage: `field enriched with threat intelligence`, + } +); + +export const THREAT_ENRICHMENTS = i18n.translate( + 'xpack.securitySolution.flyout.documentDetails.overviewTab.threatIntelligence.threatEnrichments', + { + defaultMessage: `fields enriched with threat intelligence`, + } +); + export const VIEW_ALL = (text: string) => i18n.translate('xpack.securitySolution.flyout.documentDetails.overviewTab.viewAllButton', { values: { text }, diff --git a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx new file mode 100644 index 0000000000000..ab57dcebe32af --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.test.tsx @@ -0,0 +1,216 @@ +/* + * 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 { RenderHookResult } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; +import type { + UseThreatIntelligenceParams, + UseThreatIntelligenceValue, +} from './use_fetch_threat_intelligence'; +import { useFetchThreatIntelligence } from './use_fetch_threat_intelligence'; +import { useInvestigationTimeEnrichment } from '../../../common/containers/cti/event_enrichment'; + +jest.mock('../../../common/containers/cti/event_enrichment'); + +const dataFormattedForFieldBrowser = [ + { + category: 'kibana', + field: 'kibana.alert.rule.uuid', + isObjectArray: false, + originalValue: ['uuid'], + values: ['uuid'], + }, + { + category: 'threat', + field: 'threat.enrichments', + isObjectArray: true, + originalValue: ['{"indicator.file.hash.sha256":["sha256"]}'], + values: ['{"indicator.file.hash.sha256":["sha256"]}'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.file.hash.sha256', + isObjectArray: false, + originalValue: ['sha256'], + values: ['sha256'], + }, +]; + +describe('useFetchThreatIntelligence', () => { + let hookResult: RenderHookResult; + + it('should render 1 match detected and 1 field enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 2, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(1); + expect(hookResult.result.current.threatMatchesCount).toEqual(1); + expect(hookResult.result.current.threatEnrichments).toHaveLength(1); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(1); + }); + + it('should render 2 matches detected and 2 fields enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.3'], + 'matched.type': ['indicator_match_rule'], + }, + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.4'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 4, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(2); + expect(hookResult.result.current.threatMatchesCount).toEqual(2); + expect(hookResult.result.current.threatEnrichments).toHaveLength(2); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(2); + }); + + it('should render 0 field enriched', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.1'], + 'matched.type': ['indicator_match_rule'], + }, + ], + totalCount: 1, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toHaveLength(1); + expect(hookResult.result.current.threatMatchesCount).toEqual(1); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); + + it('should render 0 match detected', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [ + { + 'threat.indicator.file.hash.sha256': 'sha256', + 'matched.atomic': ['sha256'], + 'matched.field': ['file.hash.sha256'], + 'matched.id': ['matched.id.2'], + 'matched.type': ['investigation_time'], + 'event.type': ['indicator'], + }, + ], + totalCount: 1, + }, + loading: false, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toHaveLength(1); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(1); + }); + + it('should return loading true', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: undefined, + loading: true, + }); + + hookResult = renderHook(() => useFetchThreatIntelligence({ dataFormattedForFieldBrowser })); + expect(hookResult.result.current.loading).toEqual(true); + expect(hookResult.result.current.error).toEqual(false); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); + + it('should return error true', () => { + (useInvestigationTimeEnrichment as jest.Mock).mockReturnValue({ + result: { + enrichments: [], + totalCount: 0, + }, + loading: false, + }); + + hookResult = renderHook(() => + useFetchThreatIntelligence({ dataFormattedForFieldBrowser: null }) + ); + expect(hookResult.result.current.loading).toEqual(false); + expect(hookResult.result.current.error).toEqual(true); + expect(hookResult.result.current.threatMatches).toEqual(undefined); + expect(hookResult.result.current.threatMatchesCount).toEqual(0); + expect(hookResult.result.current.threatEnrichments).toEqual(undefined); + expect(hookResult.result.current.threatEnrichmentsCount).toEqual(0); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts new file mode 100644 index 0000000000000..4f3d23b082664 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_fetch_threat_intelligence.ts @@ -0,0 +1,108 @@ +/* + * 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 { useMemo } from 'react'; +import { groupBy } from 'lodash'; +import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; +import type { CtiEnrichment } from '../../../../common/search_strategy'; +import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; +import { + filterDuplicateEnrichments, + getEnrichmentFields, + parseExistingEnrichments, + timelineDataToEnrichment, +} from '../../../common/components/event_details/cti_details/helpers'; +import { useInvestigationTimeEnrichment } from '../../../common/containers/cti/event_enrichment'; +import { ENRICHMENT_TYPES } from '../../../../common/cti/constants'; + +export interface UseThreatIntelligenceParams { + /** + * An array of field objects with category and value + */ + dataFormattedForFieldBrowser: TimelineEventsDetailsItem[] | null; +} + +export interface UseThreatIntelligenceValue { + /** + * Returns true while the threat intelligence data is being queried + */ + loading: boolean; + /** + * Returns true if the dataFormattedForFieldBrowser property is null + */ + error: boolean; + /** + * Threat matches (from an indicator match rule) + */ + threatMatches: CtiEnrichment[]; + /** + * Threat matches count + */ + threatMatchesCount: number; + /** + * Threat enrichments (from the real time query) + */ + threatEnrichments: CtiEnrichment[]; + /** + * Threat enrichments count + */ + threatEnrichmentsCount: number; +} + +/** + * Hook to retrieve threat intelligence data for the expandable flyout right and left sections. + */ +export const useFetchThreatIntelligence = ({ + dataFormattedForFieldBrowser, +}: UseThreatIntelligenceParams): UseThreatIntelligenceValue => { + const { isAlert } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser); + + // retrieve the threat enrichment fields with value for the current document + // (see https://github.com/elastic/kibana/blob/main/x-pack/plugins/security_solution/common/cti/constants.ts#L35) + const eventFields = useMemo( + () => getEnrichmentFields(dataFormattedForFieldBrowser || []), + [dataFormattedForFieldBrowser] + ); + + // retrieve existing enrichment fields and their value + const existingEnrichments = useMemo( + () => + isAlert + ? parseExistingEnrichments(dataFormattedForFieldBrowser || []).map((enrichmentData) => + timelineDataToEnrichment(enrichmentData) + ) + : [], + [dataFormattedForFieldBrowser, isAlert] + ); + + // api call to retrieve all documents that match the eventFields + const { result: response, loading } = useInvestigationTimeEnrichment(eventFields); + + // combine existing enrichment and enrichment from the api response + // also removes the investigation-time enrichments if the exact indicator already exists + const allEnrichments = useMemo(() => { + if (loading || !response?.enrichments) { + return existingEnrichments; + } + return filterDuplicateEnrichments([...existingEnrichments, ...response.enrichments]); + }, [loading, response, existingEnrichments]); + + // separate threat matches (from indicator-match rule) from threat enrichments (realtime query) + const { + [ENRICHMENT_TYPES.IndicatorMatchRule]: threatMatches, + [ENRICHMENT_TYPES.InvestigationTime]: threatEnrichments, + } = groupBy(allEnrichments, 'matched.type'); + + return { + loading, + error: !dataFormattedForFieldBrowser, + threatMatches, + threatMatchesCount: (threatMatches || []).length, + threatEnrichments, + threatEnrichmentsCount: (threatEnrichments || []).length, + }; +}; From f0106b18179f997e40c704c118e45816c97e2e04 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 21 Apr 2023 14:51:12 -0600 Subject: [PATCH 10/29] [dashboard][maps] fix 'by value' map does not fill dashboard panel on initial page load (#155554) Fixes https://github.com/elastic/kibana/issues/155553 Following steps in issue now renders map as expected Screen Shot 2023-04-21 at 1 18 54 PM --- .../maps/public/connected_components/mb_map/mb_map.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx index da4765ca094ec..71858ecb02459 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx @@ -283,11 +283,10 @@ export class MbMap extends Component { } _initResizerChecker() { + this.state.mbMap?.resize(); // ensure map is sized for container prior to monitoring this._checker = new ResizeChecker(this._containerRef!); this._checker.on('resize', () => { - if (this.state.mbMap) { - this.state.mbMap.resize(); - } + this.state.mbMap?.resize(); }); } From 8039fec178b92f059e7c64cfab0dd0fcdfc87e19 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Fri, 21 Apr 2023 16:53:25 -0400 Subject: [PATCH 11/29] [Embeddable] Always send value input from edit panel action (#155283) ensures that the by value input is passed into the editors regardless of whether the panel is by value or by reference. --- .../lib/actions/edit_panel_action.test.tsx | 30 ++----------------- .../public/lib/actions/edit_panel_action.ts | 5 +--- x-pack/plugins/maps/public/render_app.tsx | 3 +- 3 files changed, 4 insertions(+), 34 deletions(-) diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx index 926b6782b5633..d2fb4e701e4b4 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.test.tsx @@ -7,7 +7,7 @@ */ import { EditPanelAction } from './edit_panel_action'; -import { Embeddable, EmbeddableInput, SavedObjectEmbeddableInput } from '../embeddables'; +import { Embeddable, EmbeddableInput } from '../embeddables'; import { ViewMode } from '../types'; import { ContactCardEmbeddable } from '../test_samples'; import { embeddablePluginMock } from '../../mocks'; @@ -42,7 +42,7 @@ test('is compatible when edit url is available, in edit mode and editable', asyn ).toBe(true); }); -test('redirects to app using state transfer with by value mode', async () => { +test('redirects to app using state transfer', async () => { applicationMock.currentAppId$ = of('superCoolCurrentApp'); const testPath = '/test-path'; const action = new EditPanelAction( @@ -78,32 +78,6 @@ test('redirects to app using state transfer with by value mode', async () => { }); }); -test('redirects to app using state transfer without by value mode', async () => { - applicationMock.currentAppId$ = of('superCoolCurrentApp'); - const testPath = '/test-path'; - const action = new EditPanelAction( - getFactory, - applicationMock, - stateTransferMock, - () => testPath - ); - const embeddable = new EditableEmbeddable( - { id: '123', viewMode: ViewMode.EDIT, savedObjectId: '1234' } as SavedObjectEmbeddableInput, - true - ); - embeddable.getOutput = jest.fn(() => ({ editApp: 'ultraVisualize', editPath: '/123' })); - await action.execute({ embeddable }); - expect(stateTransferMock.navigateToEditor).toHaveBeenCalledWith('ultraVisualize', { - path: '/123', - state: { - originatingApp: 'superCoolCurrentApp', - embeddableId: '123', - valueInput: undefined, - originatingPath: testPath, - }, - }); -}); - test('getHref returns the edit urls', async () => { const action = new EditPanelAction(getFactory, applicationMock, stateTransferMock); expect(action.getHref).toBeDefined(); diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts index 1dcecf4ac894d..ba59d92cbef60 100644 --- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts +++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts @@ -17,7 +17,6 @@ import { IEmbeddable, EmbeddableEditorState, EmbeddableStateTransfer, - SavedObjectEmbeddableInput, EmbeddableInput, Container, } from '../..'; @@ -124,13 +123,11 @@ export class EditPanelAction implements Action { if (app && path) { if (this.currentAppId) { - const byValueMode = !(embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId; - const originatingPath = this.getOriginatingPath?.(); const state: EmbeddableEditorState = { originatingApp: this.currentAppId, - valueInput: byValueMode ? this.getExplicitInput({ embeddable }) : undefined, + valueInput: this.getExplicitInput({ embeddable }), embeddableId: embeddable.id, searchSessionId: embeddable.getInput().searchSessionId, originatingPath, diff --git a/x-pack/plugins/maps/public/render_app.tsx b/x-pack/plugins/maps/public/render_app.tsx index 01eba4ee7905e..c4bddf5a71541 100644 --- a/x-pack/plugins/maps/public/render_app.tsx +++ b/x-pack/plugins/maps/public/render_app.tsx @@ -86,8 +86,7 @@ export async function renderApp( mapEmbeddableInput = { savedObjectId: routeProps.match.params.savedMapId, } as MapByReferenceInput; - } - if (valueInput) { + } else if (valueInput) { mapEmbeddableInput = valueInput as MapByValueInput; } From 9b6c6bcd9c8eb251e3f186d8e5038a18035de465 Mon Sep 17 00:00:00 2001 From: Philippe Oberti Date: Fri, 21 Apr 2023 15:59:20 -0500 Subject: [PATCH 12/29] [Security Solution] expandable flyout small cleanup (#155515) --- .../components/highlighted_fields.test.tsx | 24 +++++-------------- .../right/components/highlighted_fields.tsx | 2 +- .../right/components/mitre_attack.test.tsx | 8 ++----- .../flyout/right/components/reason.test.tsx | 24 +++++-------------- .../right/components/risk_score.test.tsx | 16 ++++--------- .../flyout/right/components/severity.test.tsx | 24 +++++-------------- 6 files changed, 25 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx index 52a85b66a3108..0045f30cecebc 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.test.tsx @@ -54,7 +54,7 @@ describe('', () => { browserFields: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -62,11 +62,7 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if browserFields is null', () => { @@ -78,7 +74,7 @@ describe('', () => { browserFields: null, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -86,11 +82,7 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if eventId is null', () => { @@ -102,7 +94,7 @@ describe('', () => { browserFields: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( @@ -110,10 +102,6 @@ describe('', () => { ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx index af14a20788466..eaae42100ded3 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/highlighted_fields.tsx @@ -33,7 +33,7 @@ export const HighlightedFields: FC = () => { }, [eventId, indexName, openRightPanel, scopeId]); if (!dataFormattedForFieldBrowser || !browserFields || !eventId) { - return <>; + return null; } return ( diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx index f0bc9bd993f66..24e52e4ba5915 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/mitre_attack.test.tsx @@ -33,16 +33,12 @@ describe('', () => { }, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx index d2a7c90011d68..b7050d1df0fa0 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/reason.test.tsx @@ -37,17 +37,13 @@ describe('', () => { dataAsNestedObject: {}, } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render null if dataAsNestedObject is null', () => { @@ -55,17 +51,13 @@ describe('', () => { dataFormattedForFieldBrowser: [], } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render null if renderer is null', () => { const panelContextValue = { @@ -73,16 +65,12 @@ describe('', () => { dataFormattedForFieldBrowser: [], } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx index 3b9364df1dd04..554b6c90db32a 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/risk_score.test.tsx @@ -38,17 +38,13 @@ describe('', () => { getFieldsData: jest.fn(), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid', () => { @@ -56,16 +52,12 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => 123), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx index e329459c6cbc1..92c8b0a382638 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/severity.test.tsx @@ -38,17 +38,13 @@ describe('', () => { getFieldsData: jest.fn(), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid array', () => { @@ -56,17 +52,13 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => ['abc']), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); it('should render empty component if getFieldsData is invalid string', () => { @@ -74,16 +66,12 @@ describe('', () => { getFieldsData: jest.fn().mockImplementation(() => 'abc'), } as unknown as RightPanelContext; - const { baseElement } = render( + const { container } = render( ); - expect(baseElement).toMatchInlineSnapshot(` - -
- - `); + expect(container).toBeEmptyDOMElement(); }); }); From 9bb127f26c7edec602f9da0efb58861cb47af9cc Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Fri, 21 Apr 2023 15:01:19 -0600 Subject: [PATCH 13/29] [RAM] Add window maintenance column to rule logs and rule tables (#155333) ## Summary Resolves: https://github.com/elastic/kibana/issues/153775 This PR adds the `maintenance_window_id` column to the following tables: ## O11Y/Security solutions Alerts table ![o11y-alerts-table](https://user-images.githubusercontent.com/74562234/233227165-03c105d9-3890-4462-91ec-cd7c6ad26d7b.png) ## Rule details alerts table ![rule_details_maintenance_window_ids](https://user-images.githubusercontent.com/74562234/233226920-6f903ddf-401f-49e7-bb9c-9a36334fc7ce.png) ## Rule run event log ![rule_event_log_maintenance_window](https://user-images.githubusercontent.com/74562234/233226784-c6e804e6-eabe-4500-b51a-aae7aafbcff1.png) ## To test: 1. Set `ENABLE_MAINTENANCE_WINDOWS` to true in `x-pack/plugins/alerting/public/plugin.ts` 2. Create 1 or more active maintenance windows in stack management 3. Create a rule, trigger some alerts 4. Go to the rule details page alerts table, assert the `maintenance window` column is there, and renders the maintenance window ids 5. Go to the rule details page event log table, assert the `maintenance window` column can enabled, and renders the maintenance window ids 6. Create a O11Y rule, trigger some alerts 7. Go to the O11Y alerts table, assert that the `maintenance_window_id` field can be enabled, and renders the maintenance window ids ### 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: Lisa Cawley Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/alerting/common/alert_summary.ts | 1 + .../alerting/common/execution_log_types.ts | 1 + .../lib/alert_summary_from_event_log.ts | 4 + .../alerting_event_logger.mock.ts | 1 + .../alerting_event_logger.test.ts | 45 ++++++++++ .../alerting_event_logger.ts | 29 +++++- .../lib/get_execution_log_aggregation.test.ts | 45 +++++++--- .../lib/get_execution_log_aggregation.ts | 38 +++++--- .../routes/get_global_execution_logs.test.ts | 2 + .../routes/get_rule_execution_log.test.ts | 2 + .../tests/get_execution_log.test.ts | 11 ++- .../server/task_runner/task_runner.test.ts | 14 ++- .../server/task_runner/task_runner.ts | 3 + .../public/application/constants/index.ts | 1 + .../event_log_list_cell_renderer.test.tsx | 10 +++ .../event_log_list_cell_renderer.tsx | 4 + .../sections/rule_details/components/rule.tsx | 1 + .../components/rule_alert_list.tsx | 15 ++++ .../components/rule_event_log_list_table.tsx | 14 +++ .../sections/rule_details/components/types.ts | 1 + .../tests/alerting/group1/event_log.ts | 7 +- .../alerting/group1/get_alert_summary.ts | 88 +++++++++++++++++++ 22 files changed, 307 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/alerting/common/alert_summary.ts b/x-pack/plugins/alerting/common/alert_summary.ts index b1563882d4f21..a465d6af2daed 100644 --- a/x-pack/plugins/alerting/common/alert_summary.ts +++ b/x-pack/plugins/alerting/common/alert_summary.ts @@ -38,4 +38,5 @@ export interface AlertStatus { actionGroupId?: string; activeStartDate?: string; flapping: boolean; + maintenanceWindowIds?: string[]; } diff --git a/x-pack/plugins/alerting/common/execution_log_types.ts b/x-pack/plugins/alerting/common/execution_log_types.ts index 2d5e34df8f766..7a35bea0df619 100644 --- a/x-pack/plugins/alerting/common/execution_log_types.ts +++ b/x-pack/plugins/alerting/common/execution_log_types.ts @@ -63,6 +63,7 @@ export interface IExecutionLog { rule_id: string; space_ids: string[]; rule_name: string; + maintenance_window_ids: string[]; } export interface IExecutionErrors { diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts index 7c3df21a3281d..9dc4a4fec5f8e 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts @@ -86,6 +86,10 @@ export function alertSummaryFromEventLog(params: AlertSummaryFromEventLogParams) status.flapping = true; } + if (event?.kibana?.alert?.maintenance_window_ids?.length) { + status.maintenanceWindowIds = event.kibana.alert.maintenance_window_ids as string[]; + } + switch (action) { case EVENT_LOG_ACTIONS.newInstance: status.activeStartDate = timeStamp; diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts index ff62ddaea7ff4..00a9bf7221ef3 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.mock.ts @@ -15,6 +15,7 @@ const createAlertingEventLoggerMock = () => { setRuleName: jest.fn(), setExecutionSucceeded: jest.fn(), setExecutionFailed: jest.fn(), + setMaintenanceWindowIds: jest.fn(), logTimeout: jest.fn(), logAlert: jest.fn(), logAction: jest.fn(), diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index 2711f921e81ec..62c8d9eed29fd 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -284,6 +284,51 @@ describe('AlertingEventLogger', () => { }); }); + describe('setMaintenanceWindowIds()', () => { + test('should throw error if alertingEventLogger has not been initialized', () => { + expect(() => + alertingEventLogger.setMaintenanceWindowIds([]) + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + test('should throw error if event is null', () => { + alertingEventLogger.initialize(context); + expect(() => + alertingEventLogger.setMaintenanceWindowIds([]) + ).toThrowErrorMatchingInlineSnapshot(`"AlertingEventLogger not initialized"`); + }); + + it('should update event maintenance window IDs correctly', () => { + alertingEventLogger.initialize(context); + alertingEventLogger.start(); + alertingEventLogger.setMaintenanceWindowIds([]); + + const event = initializeExecuteRecord(contextWithScheduleDelay); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + kibana: { + ...event.kibana, + alert: { + ...event.kibana?.alert, + maintenance_window_ids: [], + }, + }, + }); + + alertingEventLogger.setMaintenanceWindowIds(['test-id-1', 'test-id-2']); + expect(alertingEventLogger.getEvent()).toEqual({ + ...event, + kibana: { + ...event.kibana, + alert: { + ...event.kibana?.alert, + maintenance_window_ids: ['test-id-1', 'test-id-2'], + }, + }, + }); + }); + }); + describe('logTimeout()', () => { test('should throw error if alertingEventLogger has not been initialized', () => { expect(() => alertingEventLogger.logTimeout()).toThrowErrorMatchingInlineSnapshot( diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts index 37029b4c96703..60d9d21f16113 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -138,6 +138,14 @@ export class AlertingEventLogger { updateEvent(this.event, { message, outcome: 'success', alertingOutcome: 'success' }); } + public setMaintenanceWindowIds(maintenanceWindowIds: string[]) { + if (!this.isInitialized || !this.event) { + throw new Error('AlertingEventLogger not initialized'); + } + + updateEvent(this.event, { maintenanceWindowIds }); + } + public setExecutionFailed(message: string, errorMessage: string) { if (!this.isInitialized || !this.event) { throw new Error('AlertingEventLogger not initialized'); @@ -351,11 +359,22 @@ interface UpdateEventOpts { reason?: string; metrics?: RuleRunMetrics; timings?: TaskRunnerTimings; + maintenanceWindowIds?: string[]; } export function updateEvent(event: IEvent, opts: UpdateEventOpts) { - const { message, outcome, error, ruleName, status, reason, metrics, timings, alertingOutcome } = - opts; + const { + message, + outcome, + error, + ruleName, + status, + reason, + metrics, + timings, + alertingOutcome, + maintenanceWindowIds, + } = opts; if (!event) { throw new Error('Cannot update event because it is not initialized.'); } @@ -431,4 +450,10 @@ export function updateEvent(event: IEvent, opts: UpdateEventOpts) { ...timings, }; } + + if (maintenanceWindowIds) { + event.kibana = event.kibana || {}; + event.kibana.alert = event.kibana.alert || {}; + event.kibana.alert.maintenance_window_ids = maintenanceWindowIds; + } } diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts index 6a57baaacef27..bb38fb7a98bfa 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.test.ts @@ -270,7 +270,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -283,6 +283,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -477,7 +478,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -490,6 +491,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -684,7 +686,7 @@ describe('getExecutionLogAggregation', () => { }, }, executionDuration: { max: { field: 'event.duration' } }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -697,6 +699,7 @@ describe('getExecutionLogAggregation', () => { 'kibana.space_ids', 'rule.name', 'kibana.alerting.outcome', + 'kibana.alert.maintenance_window_ids', ], }, }, @@ -774,7 +777,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -861,7 +864,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -880,6 +883,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -958,6 +964,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -981,6 +988,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1022,7 +1030,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1112,7 +1120,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1131,6 +1139,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1209,6 +1220,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -1232,6 +1244,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1273,7 +1286,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1355,7 +1368,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1374,6 +1387,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1452,6 +1468,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -1475,6 +1492,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -1516,7 +1534,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1603,7 +1621,7 @@ describe('formatExecutionLogResult', () => { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -1622,6 +1640,9 @@ describe('formatExecutionLogResult', () => { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -1700,6 +1721,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: [], }, { id: '61bb867b-661a-471f-bf92-23471afa10b3', @@ -1723,6 +1745,7 @@ describe('formatExecutionLogResult', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); diff --git a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts index b65499de20d45..44e1f7bbe98c1 100644 --- a/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts +++ b/x-pack/plugins/alerting/server/lib/get_execution_log_aggregation.ts @@ -41,6 +41,7 @@ const NUMBER_OF_NEW_ALERTS_FIELD = 'kibana.alert.rule.execution.metrics.alert_co const NUMBER_OF_RECOVERED_ALERTS_FIELD = 'kibana.alert.rule.execution.metrics.alert_counts.recovered'; const EXECUTION_UUID_FIELD = 'kibana.alert.rule.execution.uuid'; +const MAINTENANCE_WINDOW_IDS_FIELD = 'kibana.alert.maintenance_window_ids'; const Millis2Nanos = 1000 * 1000; @@ -82,7 +83,8 @@ interface IExecutionUuidAggBucket extends estypes.AggregationsStringTermsBucketK numActiveAlerts: estypes.AggregationsMaxAggregate; numRecoveredAlerts: estypes.AggregationsMaxAggregate; numNewAlerts: estypes.AggregationsMaxAggregate; - outcomeAndMessage: estypes.AggregationsTopHitsAggregate; + outcomeMessageAndMaintenanceWindow: estypes.AggregationsTopHitsAggregate; + maintenanceWindowIds: estypes.AggregationsTopHitsAggregate; }; actionExecution: { actionOutcomes: IActionExecution; @@ -401,7 +403,7 @@ export function getExecutionLogAggregation({ field: DURATION_FIELD, }, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { top_hits: { size: 1, _source: { @@ -414,6 +416,7 @@ export function getExecutionLogAggregation({ SPACE_ID_FIELD, RULE_NAME_FIELD, ALERTING_OUTCOME_FIELD, + MAINTENANCE_WINDOW_IDS_FIELD, ], }, }, @@ -485,20 +488,30 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio const actionExecutionError = actionExecutionOutcomes.find((subBucket) => subBucket?.key === 'failure')?.doc_count ?? 0; - const outcomeAndMessage = bucket?.ruleExecution?.outcomeAndMessage?.hits?.hits[0]?._source ?? {}; - let status = outcomeAndMessage.kibana?.alerting?.outcome ?? ''; + const outcomeMessageAndMaintenanceWindow = + bucket?.ruleExecution?.outcomeMessageAndMaintenanceWindow?.hits?.hits[0]?._source ?? {}; + let status = outcomeMessageAndMaintenanceWindow.kibana?.alerting?.outcome ?? ''; if (isEmpty(status)) { - status = outcomeAndMessage.event?.outcome ?? ''; + status = outcomeMessageAndMaintenanceWindow.event?.outcome ?? ''; } - const outcomeMessage = outcomeAndMessage.message ?? ''; - const outcomeErrorMessage = outcomeAndMessage.error?.message ?? ''; + const outcomeMessage = outcomeMessageAndMaintenanceWindow.message ?? ''; + const outcomeErrorMessage = outcomeMessageAndMaintenanceWindow.error?.message ?? ''; const message = status === 'failure' ? `${outcomeMessage} - ${outcomeErrorMessage}` : outcomeMessage; - const version = outcomeAndMessage.kibana?.version ?? ''; - - const ruleId = outcomeAndMessage ? outcomeAndMessage?.rule?.id ?? '' : ''; - const spaceIds = outcomeAndMessage ? outcomeAndMessage?.kibana?.space_ids ?? [] : []; - const ruleName = outcomeAndMessage ? outcomeAndMessage.rule?.name ?? '' : ''; + const version = outcomeMessageAndMaintenanceWindow.kibana?.version ?? ''; + + const ruleId = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow?.rule?.id ?? '' + : ''; + const spaceIds = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow?.kibana?.space_ids ?? [] + : []; + const maintenanceWindowIds = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow.kibana?.alert?.maintenance_window_ids ?? [] + : []; + const ruleName = outcomeMessageAndMaintenanceWindow + ? outcomeMessageAndMaintenanceWindow.rule?.name ?? '' + : ''; return { id: bucket?.key ?? '', timestamp: bucket?.ruleExecution?.executeStartTime.value_as_string ?? '', @@ -520,6 +533,7 @@ function formatExecutionLogAggBucket(bucket: IExecutionUuidAggBucket): IExecutio rule_id: ruleId, space_ids: spaceIds, rule_name: ruleName, + maintenance_window_ids: maintenanceWindowIds, }; } diff --git a/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts index 3ee2b0d1816ba..f6e1c3417a42f 100644 --- a/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_global_execution_logs.test.ts @@ -48,6 +48,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: ['namespace'], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -71,6 +72,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: ['namespace'], + maintenance_window_ids: ['test-id-1'], }, ], }; diff --git a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts index eb22a6429809a..a0dd57da558eb 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_execution_log.test.ts @@ -49,6 +49,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: ['namespace'], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -72,6 +73,7 @@ describe('getRuleExecutionLogRoute', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule_name', space_ids: ['namespace'], + maintenance_window_ids: ['test-id-1'], }, ], }; diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts index cebe9fa963971..33fb19c40f7fd 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts @@ -133,7 +133,7 @@ const aggregateResults = { numRecoveredAlerts: { value: 0.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -242,7 +242,7 @@ const aggregateResults = { numRecoveredAlerts: { value: 5.0, }, - outcomeAndMessage: { + outcomeMessageAndMaintenanceWindow: { hits: { total: { value: 1, @@ -261,6 +261,9 @@ const aggregateResults = { }, kibana: { version: '8.2.0', + alert: { + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], + }, alerting: { outcome: 'success', }, @@ -389,6 +392,7 @@ describe('getExecutionLogForRule()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -412,6 +416,7 @@ describe('getExecutionLogForRule()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); @@ -725,6 +730,7 @@ describe('getGlobalExecutionLogWithAuth()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: [], }, { id: '41b2755e-765a-4044-9745-b03875d5e79a', @@ -748,6 +754,7 @@ describe('getGlobalExecutionLogWithAuth()', () => { rule_id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', rule_name: 'rule-name', space_ids: [], + maintenance_window_ids: ['254699b0-dfb2-11ed-bb3d-c91b918d0260'], }, ], }); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index ea37e5d8594d6..216cf19373cfa 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -678,11 +678,14 @@ describe('Task Runner', () => { 'Updating rule task for test rule with id 1 - {"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"} - {"outcome":"succeeded","outcomeOrder":0,"outcomeMsg":null,"warning":null,"alertsCount":{"active":1,"new":1,"recovered":0,"ignored":0}}' ); + const maintenanceWindowIds = ['test-id-1', 'test-id-2']; + testAlertingEventLogCalls({ activeAlerts: 1, newAlerts: 1, status: 'active', logAlert: 2, + maintenanceWindowIds, }); expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( 1, @@ -690,7 +693,7 @@ describe('Task Runner', () => { action: EVENT_LOG_ACTIONS.newInstance, group: 'default', state: { start: DATE_1970, duration: '0' }, - maintenanceWindowIds: ['test-id-1', 'test-id-2'], + maintenanceWindowIds, }) ); expect(alertingEventLogger.logAlert).toHaveBeenNthCalledWith( @@ -699,7 +702,7 @@ describe('Task Runner', () => { action: EVENT_LOG_ACTIONS.activeInstance, group: 'default', state: { start: DATE_1970, duration: '0' }, - maintenanceWindowIds: ['test-id-1', 'test-id-2'], + maintenanceWindowIds, }) ); @@ -3113,6 +3116,7 @@ describe('Task Runner', () => { errorMessage = 'GENERIC ERROR MESSAGE', executionStatus = 'succeeded', setRuleName = true, + maintenanceWindowIds, logAlert = 0, logAction = 0, hasReachedAlertLimit = false, @@ -3126,6 +3130,7 @@ describe('Task Runner', () => { generatedActions?: number; executionStatus?: 'succeeded' | 'failed' | 'not-reached'; setRuleName?: boolean; + maintenanceWindowIds?: string[]; logAlert?: number; logAction?: number; errorReason?: string; @@ -3140,6 +3145,11 @@ describe('Task Runner', () => { expect(alertingEventLogger.setRuleName).not.toHaveBeenCalled(); } expect(alertingEventLogger.getStartAndDuration).toHaveBeenCalled(); + if (maintenanceWindowIds?.length) { + expect(alertingEventLogger.setMaintenanceWindowIds).toHaveBeenCalledWith( + maintenanceWindowIds + ); + } if (status === 'error') { expect(alertingEventLogger.done).toHaveBeenCalledWith({ metrics: null, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index 41426724187e1..b4ee45eea902b 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -332,6 +332,9 @@ export class TaskRunner< const maintenanceWindowIds = activeMaintenanceWindows.map( (maintenanceWindow) => maintenanceWindow.id ); + if (maintenanceWindowIds.length) { + this.alertingEventLogger.setMaintenanceWindowIds(maintenanceWindowIds); + } const { updatedRuleTypeState } = await this.timer.runWithTimer( TaskRunnerTimerSpan.RuleTypeRun, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 19b66ff62d524..22ead5ca4d2fe 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -68,6 +68,7 @@ export const RULE_EXECUTION_LOG_COLUMN_IDS = [ 'es_search_duration', 'schedule_delay', 'timed_out', + 'maintenance_window_ids', ] as const; export const RULE_EXECUTION_LOG_DURATION_COLUMNS = [ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx index 0ec383c84f6e6..32e5c0e83d297 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/event_log/event_log_list_cell_renderer.test.tsx @@ -111,6 +111,16 @@ describe('rule_event_log_list_cell_renderer', () => { expect(wrapper.find(EuiIcon).props().color).toEqual('gray'); }); + it('renders maintenance window correctly', () => { + const wrapper = shallow( + + ); + expect(wrapper.text()).toEqual('test-id-1, test-id-2'); + }); + it('links to rules on the correct space', () => { const wrapper1 = shallow( {value ? 'true' : 'false'}; } + if (columnId === 'maintenance_window_ids') { + return <>{Array.isArray(value) ? value.join(', ') : ''}; + } + return <>{value}; }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx index 81c5e0d7d7e90..8b99cc910d8da 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.tsx @@ -197,6 +197,7 @@ export function alertToListItem( isMuted, sortPriority, flapping: alert.flapping, + ...(alert.maintenanceWindowIds ? { maintenanceWindowIds: alert.maintenanceWindowIds } : {}), }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx index 3b3142181d8e7..5a6fad3b939a2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_alert_list.tsx @@ -91,6 +91,21 @@ const alertsTableColumns = ( width: '80px', 'data-test-subj': 'alertsTableCell-duration', }, + { + field: 'maintenanceWindowIds', + width: '250px', + render: (value: string[]) => { + return Array.isArray(value) ? value.join(', ') : ''; + }, + name: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.alertsList.columns.maintenanceWindowIds', + { + defaultMessage: 'Maintenance windows', + } + ), + sortable: false, + 'data-test-subj': 'alertsTableCell-maintenanceWindowIds', + }, { field: '', align: RIGHT_ALIGNMENT, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx index 7df89d799b2ae..7965f58fa8420 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_event_log_list_table.tsx @@ -610,6 +610,20 @@ export const RuleEventLogListTable = ( ), isSortable: getIsColumnSortable('timed_out'), }, + { + id: 'maintenance_window_ids', + displayAsText: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.eventLogColumn.maintenanceWindowIds', + { + defaultMessage: 'Maintenance windows', + } + ), + actions: { + showSortAsc: false, + showSortDesc: false, + }, + isSortable: getIsColumnSortable('maintenance_window_ids'), + }, ], [getPaginatedRowIndex, onFlyoutOpen, onFilterChange, hasRuleNames, showFromAllSpaces, logs] ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts index c469b61690c3a..f66f648051715 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/types.ts @@ -14,4 +14,5 @@ export interface AlertListItem { isMuted: boolean; sortPriority: number; flapping: boolean; + maintenanceWindowIds?: string[]; } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts index fac54e789bd30..4e3a3fb70a455 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/event_log.ts @@ -1293,7 +1293,12 @@ export default function eventLogTests({ getService }: FtrProviderContext) { }); }); - const actionsToCheck = ['new-instance', 'active-instance', 'recovered-instance']; + const actionsToCheck = [ + 'new-instance', + 'active-instance', + 'recovered-instance', + 'execute', + ]; events.forEach((event) => { if (actionsToCheck.includes(event?.event?.action || '')) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts index 75fd92ec61eaa..6f8de29437621 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts @@ -268,6 +268,94 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo expect(actualAlerts).to.eql(expectedAlerts); }); + it('handles multi-alert status during maintenance window', async () => { + // pattern of when the rule should fire + const pattern = { + alertA: [true, true, true, true], + alertB: [true, true, false, false], + alertC: [true, true, true, true], + }; + + const { body: createdRule } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestRuleData({ + rule_type_id: 'test.patternFiring', + params: { pattern }, + schedule: { interval: '1s' }, + }) + ) + .expect(200); + + objectRemover.add(Spaces.space1.id, createdRule.id, 'rule', 'alerting'); + + const { body: createdMaintenanceWindow } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .send({ + title: 'test-maintenance-window', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: new Date().toISOString(), + tzid: 'UTC', + freq: 2, // weekly + }, + }); + + objectRemover.add( + Spaces.space1.id, + createdMaintenanceWindow.id, + 'rules/maintenance_window', + 'alerting', + true + ); + + await alertUtils.muteInstance(createdRule.id, 'alertC'); + await alertUtils.muteInstance(createdRule.id, 'alertD'); + await waitForEvents(createdRule.id, ['new-instance', 'recovered-instance']); + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdRule.id}/_alert_summary` + ); + + const actualAlerts = checkAndCleanActualAlerts(response.body.alerts, [ + 'alertA', + 'alertB', + 'alertC', + ]); + + const expectedAlerts = { + alertA: { + status: 'Active', + muted: false, + actionGroupId: 'default', + activeStartDate: actualAlerts.alertA.activeStartDate, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertB: { + status: 'OK', + muted: false, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertC: { + status: 'Active', + muted: true, + actionGroupId: 'default', + activeStartDate: actualAlerts.alertC.activeStartDate, + flapping: false, + maintenanceWindowIds: [createdMaintenanceWindow.id], + }, + alertD: { + status: 'OK', + muted: true, + flapping: false, + }, + }; + expect(actualAlerts).to.eql(expectedAlerts); + }); + describe('legacy', () => { it('handles multi-alert status', async () => { // pattern of when the alert should fire From fdc23f570e46d48d9910926349554277c2e49b59 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Fri, 21 Apr 2023 16:15:25 -0500 Subject: [PATCH 14/29] [data view field editor] Move preview response and value formatter to controller (#153799) ## Summary This is a refactor that moves logic from hooks to a controller class for the data view field editor preview. Functionality is unaffected. --- .../helpers/setup_environment.tsx | 2 +- .../field_editor/form_fields/script_field.tsx | 8 +- .../components/field_editor/form_schema.ts | 4 +- .../field_editor_flyout_content_container.tsx | 2 +- .../components/preview/field_preview.tsx | 5 +- .../preview/field_preview_context.tsx | 161 ++++++------------ ...w_controller.ts => preview_controller.tsx} | 67 +++++++- .../public/components/preview/types.ts | 7 +- 8 files changed, 136 insertions(+), 120 deletions(-) rename src/plugins/data_view_field_editor/public/components/preview/{preview_controller.ts => preview_controller.tsx} (60%) diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx index e356c76ddb989..e76cf6960cc23 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/helpers/setup_environment.tsx @@ -149,7 +149,7 @@ export const WithFieldEditorDependencies = }; const mergedDependencies = merge({}, dependencies, overridingDependencies); - const previewController = new PreviewController({ dataView, search }); + const previewController = new PreviewController({ dataView, search, fieldFormats }); return ( diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx index fddf14c864743..077471b44c237 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_fields/script_field.tsx @@ -24,7 +24,7 @@ import { } from '../../../shared_imports'; import type { RuntimeFieldPainlessError } from '../../../types'; import { painlessErrorToMonacoMarker } from '../../../lib'; -import { useFieldPreviewContext, Context } from '../../preview'; +import { useFieldPreviewContext } from '../../preview'; import { schema } from '../form_schema'; import type { FieldFormInternal } from '../field_editor'; import { useStateSelector } from '../../../state_utils'; @@ -57,6 +57,7 @@ const mapReturnTypeToPainlessContext = (runtimeType: RuntimeType): PainlessConte const currentDocumentSelector = (state: PreviewState) => state.documents[state.currentIdx]; const currentDocumentIsLoadingSelector = (state: PreviewState) => state.isLoadingDocuments; +const currentErrorSelector = (state: PreviewState) => state.previewResponse?.error; const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Props) => { const { @@ -66,14 +67,15 @@ const ScriptFieldComponent = ({ existingConcreteFields, links, placeholder }: Pr const editorValidationSubscription = useRef(); const fieldCurrentValue = useRef(''); - const { error, isLoadingPreview, isPreviewAvailable, controller } = useFieldPreviewContext(); + const { isLoadingPreview, isPreviewAvailable, controller } = useFieldPreviewContext(); + const error = useStateSelector(controller.state$, currentErrorSelector); const currentDocument = useStateSelector(controller.state$, currentDocumentSelector); const isFetchingDoc = useStateSelector(controller.state$, currentDocumentIsLoadingSelector); const [validationData$, nextValidationData$] = useBehaviorSubject< | { isFetchingDoc: boolean; isLoadingPreview: boolean; - error: Context['error']; + error: PreviewState['previewResponse']['error']; } | undefined >(undefined); diff --git a/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts b/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts index 391f54581f258..45ecac570dd99 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts +++ b/src/plugins/data_view_field_editor/public/components/field_editor/form_schema.ts @@ -10,8 +10,8 @@ import { i18n } from '@kbn/i18n'; import { EuiComboBoxOptionOption } from '@elastic/eui'; import { fieldValidators, FieldConfig, RuntimeType, ValidationFunc } from '../../shared_imports'; -import type { Context } from '../preview'; import { RUNTIME_FIELD_OPTIONS } from './constants'; +import type { PreviewState } from '../preview/types'; const { containsCharsField, emptyField, numberGreaterThanField } = fieldValidators; const i18nTexts = { @@ -25,7 +25,7 @@ const i18nTexts = { // Validate the painless **script** const painlessScriptValidator: ValidationFunc = async ({ customData: { provider } }) => { - const previewError = (await provider()) as Context['error']; + const previewError = (await provider()) as PreviewState['previewResponse']['error']; if (previewError && previewError.code === 'PAINLESS_SCRIPT_ERROR') { return { diff --git a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx index 60903fae03ea1..c763d08ae470b 100644 --- a/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx +++ b/src/plugins/data_view_field_editor/public/components/field_editor_flyout_content_container.tsx @@ -85,7 +85,7 @@ export const FieldEditorFlyoutContentContainer = ({ fieldFormats, uiSettings, }: Props) => { - const [controller] = useState(() => new PreviewController({ dataView, search })); + const [controller] = useState(() => new PreviewController({ dataView, search, fieldFormats })); const [isSaving, setIsSaving] = useState(false); const { fields } = dataView; diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx index 672f0a747991d..005a2c634cf84 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview.tsx @@ -16,6 +16,7 @@ import { DocumentsNavPreview } from './documents_nav_preview'; import { FieldPreviewError } from './field_preview_error'; import { PreviewListItem } from './field_list/field_list_item'; import { PreviewFieldList } from './field_list/field_list'; +import { useStateSelector } from '../../state_utils'; import './field_preview.scss'; @@ -28,12 +29,12 @@ export const FieldPreview = () => { value: { name, script, format }, }, isLoadingPreview, - fields, - error, documents: { fetchDocError }, reset, isPreviewAvailable, + controller, } = useFieldPreviewContext(); + const { fields, error } = useStateSelector(controller.state$, (state) => state.previewResponse); // To show the preview we at least need a name to be defined, the script or the format // and an first response from the _execute API diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx index f554025ce9f4b..c4cdfad7d4fec 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx @@ -20,7 +20,6 @@ import { renderToString } from 'react-dom/server'; import useDebounce from 'react-use/lib/useDebounce'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { BehaviorSubject } from 'rxjs'; import { RuntimePrimitiveTypes } from '../../shared_imports'; import { useStateSelector } from '../../state_utils'; @@ -32,7 +31,6 @@ import type { Context, Params, EsDocument, - ScriptErrorCodes, FetchDocError, FieldPreview, PreviewState, @@ -101,17 +99,11 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro notifications, api: { getFieldPreview }, }, - fieldFormats, fieldName$, } = useFieldEditorContext(); const fieldPreview$ = useRef(new BehaviorSubject(undefined)); - /** Response from the Painless _execute API */ - const [previewResponse, setPreviewResponse] = useState<{ - fields: Context['fields']; - error: Context['error']; - }>({ fields: [], error: null }); const [initialPreviewComplete, setInitialPreviewComplete] = useState(false); /** Possible error while fetching sample documents */ @@ -169,45 +161,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro ); }, [type, script, currentDocId]); - const setPreviewError = useCallback((error: Context['error']) => { - setPreviewResponse((prev) => ({ - ...prev, - error, - })); - }, []); - - const clearPreviewError = useCallback((errorCode: ScriptErrorCodes) => { - setPreviewResponse((prev) => { - const error = prev.error === null || prev.error?.code === errorCode ? null : prev.error; - return { - ...prev, - error, - }; - }); - }, []); - - const valueFormatter = useCallback( - (value: unknown) => { - if (format?.id) { - const formatter = fieldFormats.getInstance(format.id, format.params); - if (formatter) { - return formatter.getConverterFor('html')(value) ?? JSON.stringify(value); - } - } - - if (type) { - const fieldType = castEsToKbnFieldTypeName(type); - const defaultFormatterForType = fieldFormats.getDefaultInstance(fieldType); - if (defaultFormatterForType) { - return defaultFormatterForType.getConverterFor('html')(value) ?? JSON.stringify(value); - } - } - - return defaultValueFormatter(value); - }, - [format, type, fieldFormats] - ); - const fetchSampleDocuments = useCallback( async (limit: number = 50) => { if (typeof limit !== 'number') { @@ -217,7 +170,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro lastExecutePainlessRequestParams.current.documentId = undefined; setIsFetchingDocument(true); - setPreviewResponse({ fields: [], error: null }); + controller.setPreviewResponse({ fields: [], error: null }); const [response, searchError] = await search .search({ @@ -335,14 +288,14 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const updateSingleFieldPreview = useCallback( (fieldName: string, values: unknown[]) => { const [value] = values; - const formattedValue = valueFormatter(value); + const formattedValue = controller.valueFormatter({ value, type, format }); - setPreviewResponse({ + controller.setPreviewResponse({ fields: [{ key: fieldName, value, formattedValue }], error: null, }); }, - [valueFormatter] + [controller, type, format] ); const updateCompositeFieldPreview = useCallback( @@ -359,7 +312,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro updatedFieldsInScript.push(fieldName); const [value] = values; - const formattedValue = valueFormatter(value); + const formattedValue = controller.valueFormatter({ value, type, format }); return { key: parentName @@ -375,12 +328,12 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro .sort((a, b) => a.key.localeCompare(b.key)); fieldPreview$.current.next(fields); - setPreviewResponse({ + controller.setPreviewResponse({ fields, error: null, }); }, - [valueFormatter, parentName, name, fieldPreview$, fieldName$] + [parentName, name, fieldPreview$, fieldName$, controller, type, format] ); const updatePreview = useCallback(async () => { @@ -437,7 +390,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const { values, error } = response.data; if (error) { - setPreviewResponse({ + controller.setPreviewResponse({ fields: [ { key: name ?? '', @@ -474,6 +427,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro updateSingleFieldPreview, updateCompositeFieldPreview, currentDocIndex, + controller, ]); const reset = useCallback(() => { @@ -482,7 +436,7 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro previewCount.current = 0; controller.setDocuments([]); - setPreviewResponse({ fields: [], error: null }); + controller.setPreviewResponse({ fields: [], error: null }); setIsLoadingPreview(false); setIsFetchingDocument(false); }, [controller]); @@ -490,8 +444,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro const ctx = useMemo( () => ({ controller, - fields: previewResponse.fields, - error: previewResponse.error, fieldPreview$: fieldPreview$.current, isPreviewAvailable, isLoadingPreview, @@ -521,7 +473,6 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro [ controller, currentIdx, - previewResponse, fieldPreview$, fetchDocError, params, @@ -583,74 +534,70 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro * Whenever the name or the format changes we immediately update the preview */ useEffect(() => { - setPreviewResponse((prev) => { - const { fields } = prev; - - let updatedFields: Context['fields'] = fields.map((field) => { - let key = name ?? ''; - - if (type === 'composite') { - // restore initial key segement (the parent name), which was not returned - const { 1: fieldName } = field.key.split('.'); - key = `${name ?? ''}.${fieldName}`; - } + const { previewResponse: prev } = controller.state$.getValue(); + const { fields } = prev; - return { - ...field, - key, - }; - }); + let updatedFields: PreviewState['previewResponse']['fields'] = fields.map((field) => { + let key = name ?? ''; - // If the user has entered a name but not yet any script we will display - // the field in the preview with just the name - if (updatedFields.length === 0 && name !== null) { - updatedFields = [ - { key: name, value: undefined, formattedValue: undefined, type: undefined }, - ]; + if (type === 'composite') { + // restore initial key segement (the parent name), which was not returned + const { 1: fieldName } = field.key.split('.'); + key = `${name ?? ''}.${fieldName}`; } return { - ...prev, - fields: updatedFields, + ...field, + key, }; }); - }, [name, type, parentName]); + + // If the user has entered a name but not yet any script we will display + // the field in the preview with just the name + if (updatedFields.length === 0 && name !== null) { + updatedFields = [{ key: name, value: undefined, formattedValue: undefined, type: undefined }]; + } + + controller.setPreviewResponse({ + ...prev, + fields: updatedFields, + }); + }, [name, type, parentName, controller]); /** * Whenever the format changes we immediately update the preview */ useEffect(() => { - setPreviewResponse((prev) => { - const { fields } = prev; + const { previewResponse: prev } = controller.state$.getValue(); + const { fields } = prev; - return { - ...prev, - fields: fields.map((field) => { - const nextValue = - script === null && Boolean(document) - ? get(document?._source, name ?? '') ?? get(document?.fields, name ?? '') // When there is no script we try to read the value from _source/fields - : field?.value; + controller.setPreviewResponse({ + ...prev, + fields: fields.map((field) => { + const nextValue = + script === null && Boolean(document) + ? get(document?._source, name ?? '') ?? get(document?.fields, name ?? '') // When there is no script we try to read the value from _source/fields + : field?.value; - const formattedValue = valueFormatter(nextValue); + const formattedValue = controller.valueFormatter({ value: nextValue, type, format }); - return { - ...field, - value: nextValue, - formattedValue, - }; - }), - }; + return { + ...field, + value: nextValue, + formattedValue, + }; + }), }); - }, [name, script, document, valueFormatter]); + }, [name, script, document, controller, type, format]); useEffect(() => { if (script?.source === undefined) { // Whenever the source is not defined ("Set value" is toggled off or the // script is empty) we clear the error and update the params cache. lastExecutePainlessRequestParams.current.script = undefined; - setPreviewError(null); + controller.setPreviewError(null); } - }, [script?.source, setPreviewError]); + }, [script?.source, controller]); // Handle the validation state coming from the Painless DiagnosticAdapter // (see @kbn-monaco/src/painless/diagnostics_adapter.ts) @@ -677,16 +624,16 @@ export const FieldPreviewProvider: FunctionComponent<{ controller: PreviewContro }), }, }; - setPreviewError(error); + controller.setPreviewError(error); // Make sure to update the lastExecutePainlessRequestParams cache so when the user updates // the script and fixes the syntax the "updatePreview()" will run lastExecutePainlessRequestParams.current.script = script?.source; } else { // Clear possible previous syntax error - clearPreviewError('PAINLESS_SYNTAX_ERROR'); + controller.clearPreviewError('PAINLESS_SYNTAX_ERROR'); } - }, [scriptEditorValidation, script?.source, setPreviewError, clearPreviewError]); + }, [scriptEditorValidation, script?.source, controller]); /** * Whenever updatePreview() changes (meaning whenever a param changes) diff --git a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx similarity index 60% rename from src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts rename to src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx index b572827eac06d..8e9f6156c7d7b 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/preview_controller.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/preview_controller.tsx @@ -9,13 +9,23 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { ISearchStart } from '@kbn/data-plugin/public'; import { BehaviorSubject } from 'rxjs'; +import { castEsToKbnFieldTypeName } from '@kbn/field-types'; +import { renderToString } from 'react-dom/server'; +import React from 'react'; import { PreviewState } from './types'; import { BehaviorObservable } from '../../state_utils'; -import { EsDocument } from './types'; +import { EsDocument, ScriptErrorCodes, Params } from './types'; +import type { FieldFormatsStart } from '../../shared_imports'; + +export const defaultValueFormatter = (value: unknown) => { + const content = typeof value === 'object' ? JSON.stringify(value) : String(value) ?? '-'; + return renderToString(<>{content}); +}; interface PreviewControllerDependencies { dataView: DataView; search: ISearchStart; + fieldFormats: FieldFormatsStart; } const previewStateDefault: PreviewState = { @@ -30,12 +40,14 @@ const previewStateDefault: PreviewState = { documentSource: 'cluster', /** Keep track if the script painless syntax is being validated and if it is valid */ scriptEditorValidation: { isValidating: false, isValid: true, message: null }, + previewResponse: { fields: [], error: null }, }; export class PreviewController { - constructor({ dataView, search }: PreviewControllerDependencies) { + constructor({ dataView, search, fieldFormats }: PreviewControllerDependencies) { this.dataView = dataView; this.search = search; + this.fieldFormats = fieldFormats; this.internalState$ = new BehaviorSubject({ ...previewStateDefault, @@ -44,10 +56,13 @@ export class PreviewController { this.state$ = this.internalState$ as BehaviorObservable; } + // dependencies // @ts-ignore private dataView: DataView; // @ts-ignore private search: ISearchStart; + private fieldFormats: FieldFormatsStart; + private internalState$: BehaviorSubject; state$: BehaviorObservable; @@ -104,4 +119,52 @@ export class PreviewController { setCustomId = (customId?: string) => { this.updateState({ customId }); }; + + setPreviewError = (error: PreviewState['previewResponse']['error']) => { + this.updateState({ + previewResponse: { ...this.internalState$.getValue().previewResponse, error }, + }); + }; + + setPreviewResponse = (previewResponse: PreviewState['previewResponse']) => { + this.updateState({ previewResponse }); + }; + + clearPreviewError = (errorCode: ScriptErrorCodes) => { + const { previewResponse: prev } = this.internalState$.getValue(); + const error = prev.error === null || prev.error?.code === errorCode ? null : prev.error; + this.updateState({ + previewResponse: { + ...prev, + error, + }, + }); + }; + + valueFormatter = ({ + value, + format, + type, + }: { + value: unknown; + format: Params['format']; + type: Params['type']; + }) => { + if (format?.id) { + const formatter = this.fieldFormats.getInstance(format.id, format.params); + if (formatter) { + return formatter.getConverterFor('html')(value) ?? JSON.stringify(value); + } + } + + if (type) { + const fieldType = castEsToKbnFieldTypeName(type); + const defaultFormatterForType = this.fieldFormats.getDefaultInstance(fieldType); + if (defaultFormatterForType) { + return defaultFormatterForType.getConverterFor('html')(value) ?? JSON.stringify(value); + } + } + + return defaultValueFormatter(value); + }; } diff --git a/src/plugins/data_view_field_editor/public/components/preview/types.ts b/src/plugins/data_view_field_editor/public/components/preview/types.ts index 347e0a709cf28..b4280f54786ac 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/types.ts +++ b/src/plugins/data_view_field_editor/public/components/preview/types.ts @@ -55,6 +55,11 @@ export interface PreviewState { isValid: boolean; message: string | null; }; + /** Response from the Painless _execute API */ + previewResponse: { + fields: FieldPreview[]; + error: PreviewError | null; + }; } export interface FetchDocError { @@ -108,9 +113,7 @@ export type ChangeSet = Record; export interface Context { controller: PreviewController; - fields: FieldPreview[]; fieldPreview$: BehaviorSubject; - error: PreviewError | null; fieldTypeInfo?: FieldTypeInfo[]; initialPreviewComplete: boolean; params: { From 11155329cc350c1b7a9cbaefcde34a802d803951 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Fri, 21 Apr 2023 16:01:43 -0700 Subject: [PATCH 15/29] [Security Solution][Exceptions] - Add exception list duplication options with and without expired items (#154991) ## Summary Adds the following: - Add the option to duplicate from the shared exception list management actions dropdowns - User can select to include exception items with expired TTL - User can select to not include exception items with expired TTL - Cypress tests added for both options --- .../__snapshots__/list_header.test.tsx.snap | 21 ++ .../src/list_header/index.tsx | 3 + .../src/list_header/list_header.test.tsx | 7 + .../__snapshots__/menu_items.test.tsx.snap | 43 ++- .../src/list_header/menu_items/index.tsx | 11 + .../menu_items/menu_items.test.tsx | 34 ++- .../src/translations.ts | 6 + .../index.mock.ts | 17 ++ .../index.test.ts | 66 +++++ .../index.ts | 34 +++ .../src/request/index.ts | 1 + .../src/typescript_types/index.ts | 23 +- .../tsconfig.json | 4 +- .../src/api/index.ts | 29 ++ .../src/use_api/index.ts | 34 ++- .../tsconfig.json | 1 + .../lists/public/exceptions/api.test.ts | 27 ++ .../public/exceptions/hooks/use_api.test.ts | 58 ++++ .../routes/duplicate_exception_list_route.ts | 94 +++++++ x-pack/plugins/lists/server/routes/index.ts | 1 + .../lists/server/routes/init_routes.ts | 2 + .../duplicate_exception_list.test.ts | 121 ++++++++ .../duplicate_exception_list.ts | 53 ++-- .../exception_lists/exception_list_client.ts | 10 +- .../exception_list_client_types.ts | 7 +- .../rules/bulk_actions/request_schema.test.ts | 5 +- .../api/rules/bulk_actions/request_schema.ts | 1 + .../rule_management/constants.ts | 1 + .../bulk_duplicate_rules.cy.ts | 132 +++++++++ .../indicator_match_rule.cy.ts | 4 +- .../alerts_table_flow/add_exception.cy.ts | 4 +- .../all_exception_lists_read_only.cy.ts | 9 +- .../manage_shared_exception_list.cy.ts | 264 ++++++++++++------ .../cypress/objects/exception.ts | 9 + .../cypress/screens/alerts_detection_rules.ts | 7 + .../cypress/screens/exceptions.ts | 11 +- .../cypress/screens/rule_details.ts | 6 + .../cypress/tasks/alerts_detection_rules.ts | 22 +- .../cypress/tasks/api_calls/exceptions.ts | 14 +- .../cypress/tasks/exceptions.ts | 16 ++ .../cypress/tasks/exceptions_table.ts | 47 +++- .../cypress/tasks/rule_details.ts | 7 + ...bulk_duplicate_exceptions_confirmation.tsx | 24 +- .../rules_table/bulk_actions/translations.tsx | 20 +- .../bulk_actions/use_bulk_actions.tsx | 8 +- .../rules_table/use_rules_table_actions.tsx | 8 +- .../rules/rule_actions_overflow/index.tsx | 8 +- .../exceptions_list_card/index.test.tsx | 109 ++++++++ .../components/exceptions_list_card/index.tsx | 31 +- .../index.test.tsx | 40 +++ .../index.tsx | 75 +++++ .../export_exceptions_list_modal/index.tsx | 50 ---- .../hooks/use_exceptions_list.card/index.tsx | 86 ++++-- .../hooks/use_list_detail_view/index.ts | 32 ++- .../pages/list_detail_view/index.tsx | 81 ++++-- .../exceptions/pages/shared_lists/index.tsx | 42 ++- .../translations/list_details_view.ts | 14 + .../exceptions/translations/shared_list.ts | 61 +++- .../api/rules/bulk_actions/route.ts | 3 + .../actions/duplicate_exceptions.test.ts | 163 +++++++++++ .../logic/actions/duplicate_exceptions.ts | 59 +++- .../translations/translations/fr-FR.json | 5 - .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - .../group10/perform_bulk_action.ts | 220 ++++++++++++++- .../tests/duplicate_exception_list.ts | 207 ++++++++++++++ .../security_and_spaces/tests/index.ts | 1 + 67 files changed, 2297 insertions(+), 326 deletions(-) create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.mock.ts create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.test.ts create mode 100644 packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts create mode 100644 x-pack/plugins/lists/server/routes/duplicate_exception_list_route.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/duplicate_exception_list.test.ts create mode 100644 x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_duplicate_rules.cy.ts create mode 100644 x-pack/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/exceptions/components/expired_exceptions_list_items_modal/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/exceptions/components/export_exceptions_list_modal/index.tsx create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts create mode 100644 x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap index 871e3ea311cd6..b72c08b8d74ac 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap @@ -1876,6 +1876,27 @@ Object { data-test-subj="RightSideMenuItemsMenuActionsActionItem2" disabled="" type="button" + > + + + + Duplicate exception list + + + + + +
diff --git a/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts b/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts index 6d1f8c115fab3..e2688d804b3d2 100644 --- a/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts +++ b/x-pack/plugins/security_solution/public/exceptions/translations/list_details_view.ts @@ -167,3 +167,17 @@ export const EXCEPTION_EXPORT_ERROR_DESCRIPTION = i18n.translate( defaultMessage: 'An error occurred exporting a list', } ); + +export const DUPLICATE_EXCEPTION_LIST = i18n.translate( + 'xpack.securitySolution.exceptionsTable.duplicateExceptionList', + { + defaultMessage: 'Duplicate exception list', + } +); + +export const EXCEPTION_DUPLICATE_ERROR_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptionsTable.duplicateListDescription', + { + defaultMessage: 'An error occurred duplicating a list', + } +); diff --git a/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts b/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts index 6ab1ca6df8464..eb18283577369 100644 --- a/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts +++ b/x-pack/plugins/security_solution/public/exceptions/translations/shared_list.ts @@ -125,6 +125,19 @@ export const EXCEPTION_EXPORT_ERROR = i18n.translate( } ); +export const EXCEPTION_LIST_DUPLICATED_SUCCESSFULLY = (listName: string) => + i18n.translate('xpack.securitySolution.exceptions.list.duplicate_success', { + values: { listName }, + defaultMessage: 'Exception list "{listName}" duplicated successfully', + }); + +export const EXCEPTION_DUPLICATE_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.duplicateError', + { + defaultMessage: 'Exception list duplication error', + } +); + export const EXCEPTION_DELETE_ERROR = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.all.exceptions.deleteError', { @@ -371,29 +384,59 @@ export const SORT_BY_CREATE_AT = i18n.translate( } ); -export const EXPORT_MODAL_CANCEL_BUTTON = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalCancelButton', +export const EXPIRED_EXCEPTIONS_MODAL_CANCEL_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalCancelButton', { defaultMessage: 'Cancel', } ); -export const EXPORT_MODAL_TITLE = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalTitle', +export const EXPIRED_EXCEPTIONS_MODAL_EXPORT_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalExportTitle', { - defaultMessage: 'Export exception list', + defaultMessage: 'Export exception list?', } ); -export const EXPORT_MODAL_INCLUDE_SWITCH_LABEL = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel', +export const EXPIRED_EXCEPTIONS_MODAL_DUPLICATE_TITLE = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalDuplicateTitle', + { + defaultMessage: 'Duplicate exception list?', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_DUPLICATE_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeDuplicateDescription', + { + defaultMessage: + 'You’re duplicating an exception list. Switch the toggle off to exclude expired exceptions.', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_EXPORT_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeExportDescription', + { + defaultMessage: + 'You’re exporting an exception list. Switch the toggle off to exclude expired exceptions.', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_INCLUDE_SWITCH_LABEL = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalIncludeSwitchLabel', { defaultMessage: 'Include expired exceptions', } ); -export const EXPORT_MODAL_CONFIRM_BUTTON = i18n.translate( - 'xpack.securitySolution.exceptions.exportModalConfirmButton', +export const EXPIRED_EXCEPTIONS_MODAL_CONFIRM_DUPLICATE_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalConfirmDuplicateButton', + { + defaultMessage: 'Duplicate', + } +); + +export const EXPIRED_EXCEPTIONS_MODAL_CONFIRM_EXPORT_BUTTON = i18n.translate( + 'xpack.securitySolution.exceptions.expiredExceptionModalConfirmExportButton', { defaultMessage: 'Export', } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts index 27b9111b0434d..974ea9cc7ecc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts @@ -432,8 +432,10 @@ export const performBulkActionRoute = ( } let shouldDuplicateExceptions = true; + let shouldDuplicateExpiredExceptions = true; if (body.duplicate !== undefined) { shouldDuplicateExceptions = body.duplicate.include_exceptions; + shouldDuplicateExpiredExceptions = body.duplicate.include_expired_exceptions; } const duplicateRuleToCreate = await duplicateRule({ @@ -449,6 +451,7 @@ export const performBulkActionRoute = ( ? await duplicateExceptions({ ruleId: rule.params.ruleId, exceptionLists: rule.params.exceptionsList, + includeExpiredExceptions: shouldDuplicateExpiredExceptions, exceptionsClient, }) : []; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts new file mode 100644 index 0000000000000..aeb593ec33fff --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.test.ts @@ -0,0 +1,163 @@ +/* + * 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 { duplicateExceptions } from './duplicate_exceptions'; +import { getExceptionListClientMock } from '@kbn/lists-plugin/server/services/exception_lists/exception_list_client.mock'; +import type { List } from '@kbn/securitysolution-io-ts-list-types'; +import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import type { ExceptionListClient } from '@kbn/lists-plugin/server'; +import { getDetectionsExceptionListSchemaMock } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; + +jest.mock('uuid', () => ({ + v4: jest.fn(), +})); + +describe('duplicateExceptions', () => { + let exceptionsClient: ExceptionListClient; + + beforeAll(() => { + exceptionsClient = getExceptionListClientMock(); + exceptionsClient.duplicateExceptionListAndItems = jest.fn(); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + it('returns empty array if no exceptions to duplicate', async () => { + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(result).toEqual([]); + }); + + it('returns array referencing the same shared exception lists if no rule default exceptions included', async () => { + const sharedExceptionListReference: List = { + type: ExceptionListTypeEnum.DETECTION, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [sharedExceptionListReference], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(exceptionsClient.duplicateExceptionListAndItems).not.toHaveBeenCalled(); + expect(result).toEqual([sharedExceptionListReference]); + }); + + it('duplicates rule default and shared exceptions', async () => { + const newDefaultRuleList = { + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list_dupe', + namespace_type: 'single', + id: '123-abc', + }; + + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list', + namespace_type: 'single', + id: '5678', + }); + exceptionsClient.duplicateExceptionListAndItems = jest + .fn() + .mockResolvedValue(newDefaultRuleList); + + const sharedExceptionListReference: List = { + type: ExceptionListTypeEnum.DETECTION, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'rule_default_list', + namespace_type: 'single', + id: '5678', + }; + + const result = await duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [sharedExceptionListReference, ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }); + + expect(result).toEqual([ + sharedExceptionListReference, + { + type: newDefaultRuleList.type, + namespace_type: newDefaultRuleList.namespace_type, + id: newDefaultRuleList.id, + list_id: newDefaultRuleList.list_id, + }, + ]); + }); + + it('throws error if rule default list to duplicate not found', async () => { + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue(null); + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + await expect(() => + duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unable to duplicate rule default exceptions - unable to find their container with list_id: "my_list"]` + ); + }); + + it('throws error if list duplication returns null', async () => { + exceptionsClient.getExceptionList = jest.fn().mockResolvedValue({ + ...getDetectionsExceptionListSchemaMock(), + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }); + exceptionsClient.duplicateExceptionListAndItems = jest.fn().mockResolvedValue(null); + + const ruleDefaultListReference: List = { + type: ExceptionListTypeEnum.RULE_DEFAULT, + list_id: 'my_list', + namespace_type: 'single', + id: '1234', + }; + + await expect(() => + duplicateExceptions({ + ruleId: 'rule_123', + exceptionLists: [ruleDefaultListReference], + exceptionsClient, + includeExpiredExceptions: false, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unable to duplicate rule default exception items for rule_id: rule_123]` + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts index 496a91ba55963..82f13adc4534c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_exceptions.ts @@ -5,23 +5,42 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; + import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import type { RuleParams } from '../../../rule_schema'; +const ERROR_DUPLICATING = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.cloneExceptions.errorDuplicatingList', + { + defaultMessage: + 'Unable to duplicate rule default exceptions - unable to find their container with list_id:', + } +); + +const ERROR_DUPLICATING_ITEMS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.cloneExceptions.errorDuplicatingListItems', + { + defaultMessage: 'Unable to duplicate rule default exception items for rule_id:', + } +); + interface DuplicateExceptionsParams { ruleId: RuleParams['ruleId']; exceptionLists: RuleParams['exceptionsList']; exceptionsClient: ExceptionListClient | undefined; + includeExpiredExceptions: boolean; } export const duplicateExceptions = async ({ ruleId, exceptionLists, exceptionsClient, + includeExpiredExceptions, }: DuplicateExceptionsParams): Promise => { - if (exceptionLists == null) { + if (exceptionLists == null || !exceptionLists.length) { return []; } @@ -37,24 +56,36 @@ export const duplicateExceptions = async ({ // For rule_default list (exceptions that live only on a single rule), we need // to create a new rule_default list to assign to duplicated rule if (ruleDefaultList != null && exceptionsClient != null) { - const ruleDefaultExceptionList = await exceptionsClient.duplicateExceptionListAndItems({ + // fetch list container + const listToDuplicate = await exceptionsClient.getExceptionList({ + id: undefined, listId: ruleDefaultList.list_id, namespaceType: ruleDefaultList.namespace_type, }); - if (ruleDefaultExceptionList == null) { - throw new Error(`Unable to duplicate rule default exception items for rule_id: ${ruleId}`); - } + if (listToDuplicate == null) { + throw new Error(`${ERROR_DUPLICATING} "${ruleDefaultList.list_id}"`); + } else { + const ruleDefaultExceptionList = await exceptionsClient.duplicateExceptionListAndItems({ + list: listToDuplicate, + namespaceType: ruleDefaultList.namespace_type, + includeExpiredExceptions, + }); - return [ - ...sharedLists, - { - id: ruleDefaultExceptionList.id, - list_id: ruleDefaultExceptionList.list_id, - namespace_type: ruleDefaultExceptionList.namespace_type, - type: ruleDefaultExceptionList.type, - }, - ]; + if (ruleDefaultExceptionList == null) { + throw new Error(`${ERROR_DUPLICATING_ITEMS} ${ruleId}`); + } + + return [ + ...sharedLists, + { + id: ruleDefaultExceptionList.id, + list_id: ruleDefaultExceptionList.list_id, + namespace_type: ruleDefaultExceptionList.namespace_type, + type: ruleDefaultExceptionList.type, + }, + ]; + } } // If no rule_default list exists, we can just return diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 9a22d99e53702..dbc92e04c393f 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -27824,7 +27824,6 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.disable.successToastDescription": "Désactivation réussie de {totalRules, plural, =1 {{totalRules} règle} other {{totalRules} règles}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.errorToastDescription": "Impossible de dupliquer {rulesCount, plural, =1 {# règle} other {# règles}}.", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalBody": "Vous dupliquez {rulesCount, plural, one {# règle sélectionnée} other {# règles sélectionnées}}. Veuillez choisir comment dupliquer les exceptions existantes", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalTitle": "Dupliquer {rulesCount, plural, one {la règle} other {les règles}} avec les exceptions ?", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.with": "Dupliquer {rulesCount, plural, one {la règle et ses} other {les règles et leurs}} exceptions", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.without": "Dupliquer uniquement {rulesCount, plural, one {la règle} other {les règles}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "Duplication réussie de {totalRules, plural, =1 {{totalRules} règle} other {{totalRules} règles}}", @@ -30891,10 +30890,6 @@ "xpack.securitySolution.exceptions.exceptionListsCloseImportFlyout": "Fermer", "xpack.securitySolution.exceptions.exceptionListsFilePickerPrompt": "Sélectionner ou glisser-déposer plusieurs fichiers", "xpack.securitySolution.exceptions.exceptionListsImportButton": "Importer la liste", - "xpack.securitySolution.exceptions.exportModalCancelButton": "Annuler", - "xpack.securitySolution.exceptions.exportModalConfirmButton": "Exporter", - "xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel": "Inclure les exceptions ayant expiré", - "xpack.securitySolution.exceptions.exportModalTitle": "Exporter la liste d'exceptions", "xpack.securitySolution.exceptions.fetchError": "Erreur lors de la récupération de la liste d'exceptions", "xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle": "Erreur lors de la récupération des références d'exceptions", "xpack.securitySolution.exceptions.list.exception.item.card.delete.label": "Supprimer une exception à une règle", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 083d8d827afcf..22cfeb42e69d8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -27804,7 +27804,6 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.disable.successToastDescription": "{totalRules, plural, =1 {{totalRules}個のルール} other {{totalRules}個のルール}}が正常に無効にされました", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.errorToastDescription": "{rulesCount, plural, =1 {#個のルール} other {#個のルール}}を複製できませんでした。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalBody": "{rulesCount, plural, other {#個の選択したルール}}を複製しています。既存の例外を複製する方法を選択してください", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalTitle": "{rulesCount, plural, other {ルール}}と例外を複製しますか?", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.with": "{rulesCount, plural, other {ルール}}と例外を複製", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.without": "{rulesCount, plural, other {ルール}}のみを複製", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "{totalRules, plural, =1 {{totalRules}個のルール} other {{totalRules}個のルール}}が正常に複製されました", @@ -30870,10 +30869,6 @@ "xpack.securitySolution.exceptions.exceptionListsCloseImportFlyout": "閉じる", "xpack.securitySolution.exceptions.exceptionListsFilePickerPrompt": "複数のファイルを選択するかドラッグしてください", "xpack.securitySolution.exceptions.exceptionListsImportButton": "リストをインポート", - "xpack.securitySolution.exceptions.exportModalCancelButton": "キャンセル", - "xpack.securitySolution.exceptions.exportModalConfirmButton": "エクスポート", - "xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel": "有効期限切れの例外を含める", - "xpack.securitySolution.exceptions.exportModalTitle": "例外リストのエクスポート", "xpack.securitySolution.exceptions.fetchError": "例外リストの取得エラー", "xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle": "例外参照の取得エラー", "xpack.securitySolution.exceptions.list.exception.item.card.delete.label": "ルール例外の削除", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c4d28406b874f..67bf31893d10d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -27819,7 +27819,6 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.disable.successToastDescription": "已成功禁用 {totalRules, plural, other {{totalRules} 个规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.errorToastDescription": "无法复制 {rulesCount, plural, other {# 个规则}}。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalBody": "您正在复制 {rulesCount, plural, other {# 个选定规则}},请选择您希望如何复制现有例外", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.modalTitle": "复制存在例外的{rulesCount, plural, other {规则}}?", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.with": "复制{rulesCount, plural, other {规则}}及其例外", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.without": "仅复制{rulesCount, plural, other {规则}}", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastDescription": "已成功复制 {totalRules, plural, other {{totalRules} 个规则}}", @@ -30886,10 +30885,6 @@ "xpack.securitySolution.exceptions.exceptionListsCloseImportFlyout": "关闭", "xpack.securitySolution.exceptions.exceptionListsFilePickerPrompt": "选择或拖放多个文件", "xpack.securitySolution.exceptions.exceptionListsImportButton": "导入列表", - "xpack.securitySolution.exceptions.exportModalCancelButton": "取消", - "xpack.securitySolution.exceptions.exportModalConfirmButton": "导出", - "xpack.securitySolution.exceptions.exportModalIncludeSwitchLabel": "包括已过期例外", - "xpack.securitySolution.exceptions.exportModalTitle": "导出例外列表", "xpack.securitySolution.exceptions.fetchError": "提取例外列表时出错", "xpack.securitySolution.exceptions.fetchingReferencesErrorToastTitle": "提取例外引用时出错", "xpack.securitySolution.exceptions.list.exception.item.card.delete.label": "删除规则例外", diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts index c77ac5f142f92..e23db2120eb7e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts @@ -17,7 +17,10 @@ import { BulkActionType, BulkActionEditType, } from '@kbn/security-solution-plugin/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; +import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; +import { deleteAllExceptions } from '../../../lists_api_integration/utils'; import { binaryToString, createLegacyRuleAction, @@ -35,6 +38,7 @@ import { removeServerGeneratedProperties, waitForRuleSuccess, } from '../../utils'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -416,15 +420,225 @@ export default ({ getService }: FtrProviderContext): void => { .send({ query: '', action: BulkActionType.duplicate, - duplicate: { include_exceptions: false }, + duplicate: { include_exceptions: false, include_expired_exceptions: false }, + }) + .expect(200); + + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); + + // Check that the duplicated rule is returned with the response + expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + + // Check that the updates have been persisted + const { body: rulesResponse } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}/_find`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(rulesResponse.total).to.eql(2); + }); + + it('should duplicate rules with exceptions - expired exceptions included', async () => { + await deleteAllExceptions(supertest, log); + + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + const { body: exceptionList } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + // create an exception list item + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const ruleId = 'ruleId'; + const ruleToDuplicate = { + ...getSimpleRule(ruleId), + exceptions_list: [ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + ], + }; + const newRule = await createRule(supertest, log, ruleToDuplicate); + + // add an exception item to the rule + await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/${newRule.id}/exceptions`) + .set('kbn-xsrf', 'true') + .send({ + items: [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + type: 'simple', + expire_time: expiredDate, + }, + ], + }) + .expect(200); + + const { body } = await postBulkAction() + .send({ + query: '', + action: BulkActionType.duplicate, + duplicate: { include_exceptions: true, include_expired_exceptions: true }, + }) + .expect(200); + + const { body: foundItems } = await supertest + .get( + `${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${body.attributes.results.created[0].exceptions_list[1].list_id}` + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Item should have been duplicated, even if expired + expect(foundItems.total).to.eql(1); + + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); + + // Check that the duplicated rule is returned with the response + expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + + // Check that the exceptions are duplicated + expect(body.attributes.results.created[0].exceptions_list).to.eql([ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + { + id: body.attributes.results.created[0].exceptions_list[1].id, + list_id: body.attributes.results.created[0].exceptions_list[1].list_id, + namespace_type: 'single', + type: 'rule_default', + }, + ]); + + // Check that the updates have been persisted + const { body: rulesResponse } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}/_find`) + .set('kbn-xsrf', 'true') + .expect(200); + + expect(rulesResponse.total).to.eql(2); + }); + + it('should duplicate rules with exceptions - expired exceptions excluded', async () => { + await deleteAllExceptions(supertest, log); + + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + const { body: exceptionList } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + // create an exception list item + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const ruleId = 'ruleId'; + const ruleToDuplicate = { + ...getSimpleRule(ruleId), + exceptions_list: [ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + ], + }; + const newRule = await createRule(supertest, log, ruleToDuplicate); + + // add an exception item to the rule + await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/${newRule.id}/exceptions`) + .set('kbn-xsrf', 'true') + .send({ + items: [ + { + description: 'Exception item for rule default exception list', + entries: [ + { + field: 'some.not.nested.field', + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + name: 'Sample exception item', + type: 'simple', + expire_time: expiredDate, + }, + ], + }) + .expect(200); + + const { body } = await postBulkAction() + .send({ + query: '', + action: BulkActionType.duplicate, + duplicate: { include_exceptions: true, include_expired_exceptions: false }, }) .expect(200); + const { body: foundItems } = await supertest + .get( + `${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${body.attributes.results.created[0].exceptions_list[1].list_id}` + ) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Item should NOT have been duplicated, since it is expired + expect(foundItems.total).to.eql(0); + expect(body.attributes.summary).to.eql({ failed: 0, skipped: 0, succeeded: 1, total: 1 }); // Check that the duplicated rule is returned with the response expect(body.attributes.results.created[0].name).to.eql(`${ruleToDuplicate.name} [Duplicate]`); + // Check that the exceptions are duplicted + expect(body.attributes.results.created[0].exceptions_list).to.eql([ + { + type: exceptionList.type, + list_id: exceptionList.list_id, + id: exceptionList.id, + namespace_type: exceptionList.namespace_type, + }, + { + id: body.attributes.results.created[0].exceptions_list[1].id, + list_id: body.attributes.results.created[0].exceptions_list[1].list_id, + namespace_type: 'single', + type: 'rule_default', + }, + ]); + // Check that the updates have been persisted const { body: rulesResponse } = await supertest .get(`${DETECTION_ENGINE_RULES_URL}/_find`) @@ -462,7 +676,7 @@ export default ({ getService }: FtrProviderContext): void => { .send({ query: '', action: BulkActionType.duplicate, - duplicate: { include_exceptions: false }, + duplicate: { include_exceptions: false, include_expired_exceptions: false }, }) .expect(200); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts new file mode 100644 index 0000000000000..54e7a89042b02 --- /dev/null +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/duplicate_exception_list.ts @@ -0,0 +1,207 @@ +/* + * 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 { + ENDPOINT_LIST_URL, + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { getExceptionResponseMockWithoutAutoGeneratedValues } from '@kbn/lists-plugin/common/schemas/response/exception_list_schema.mock'; +import { getCreateExceptionListDetectionSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_schema.mock'; +import { getCreateExceptionListItemMinimalSchemaMock } from '@kbn/lists-plugin/common/schemas/request/create_exception_list_item_schema.mock'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } from '../../utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const log = getService('log'); + + describe('duplicate_exception_lists', () => { + afterEach(async () => { + await deleteAllExceptions(supertest, log); + }); + + it('should duplicate a list with no exception items', async () => { + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const bodyToCompare = removeExceptionListServerGeneratedProperties(body); + expect(bodyToCompare).to.eql({ + ...getExceptionResponseMockWithoutAutoGeneratedValues(), + type: 'detection', + list_id: body.list_id, + name: `${getCreateExceptionListDetectionSchemaMock().name} [Duplicate]`, + }); + }); + + it('should duplicate a list and its items', async () => { + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateExceptionListItemMinimalSchemaMock(), + list_id: getCreateExceptionListDetectionSchemaMock().list_id, + }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + const listBodyToCompare = removeExceptionListServerGeneratedProperties(listBody); + expect(listBodyToCompare).to.eql({ + ...getExceptionResponseMockWithoutAutoGeneratedValues(), + type: 'detection', + list_id: listBody.list_id, + name: `${getCreateExceptionListDetectionSchemaMock().name} [Duplicate]`, + }); + + expect(body.total).to.eql(1); + }); + + it('should duplicate a list with expired exception items', async () => { + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ ...getCreateExceptionListItemMinimalSchemaMock(), expire_time: expiredDate }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body.total).to.eql(1); + }); + + it('should duplicate a list and EXCLUDE expired exception items when "include_expired_exceptions" set to "false"', async () => { + const expiredDate = new Date(Date.now() - 1000000).toISOString(); + + // create an exception list + await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListDetectionSchemaMock()) + .expect(200); + + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateExceptionListItemMinimalSchemaMock(), + list_id: getCreateExceptionListDetectionSchemaMock().list_id, + expire_time: expiredDate, + }) + .expect(200); + + const { body: listBody } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=${ + getCreateExceptionListDetectionSchemaMock().list_id + }&namespace_type=single&include_expired_exceptions=false` + ) + .set('kbn-xsrf', 'true') + .expect(200); + + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=${listBody.list_id}`) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + expect(body.total).to.eql(0); + }); + + describe('error states', () => { + it('should cause a 409 if list does not exist', async () => { + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=exception_list_id&namespace_type=agnostic&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(404); + + expect(body).to.eql({ + message: 'exception list id: "exception_list_id" does not exist', + status_code: 404, + }); + }); + + it('should cause a 405 if trying to duplicate a reserved exception list type', async () => { + // create an exception list + await supertest.post(ENDPOINT_LIST_URL).set('kbn-xsrf', 'true').expect(200); + + const { body } = await supertest + .post( + `${EXCEPTION_LIST_URL}/_duplicate?list_id=endpoint_list&namespace_type=agnostic&include_expired_exceptions=true` + ) + .set('kbn-xsrf', 'true') + .expect(405); + + expect(body).to.eql({ + message: + 'unable to duplicate exception list with list_id: endpoint_list - action not allowed', + status_code: 405, + }); + }); + }); + }); +}; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts index bfce9ac8267e7..02b63c732d229 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/index.ts @@ -19,6 +19,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./update_list_items')); loadTestFile(require.resolve('./delete_lists')); loadTestFile(require.resolve('./delete_list_items')); + loadTestFile(require.resolve('./duplicate_exception_list')); loadTestFile(require.resolve('./find_lists')); loadTestFile(require.resolve('./find_list_items')); loadTestFile(require.resolve('./find_lists_by_size')); From 350bd3eb6de7512a847b4751d553f394a5c4f74d Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Fri, 21 Apr 2023 18:14:06 -0500 Subject: [PATCH 16/29] [RAM] Add query conditional action filter to Security Solution UI (#154680) ## Summary Closes #152611 Screenshot 2023-04-10 at 2 09 29 PM ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) --- .../src/actions/index.ts | 29 +++++- .../query_string_input/query_bar_top_row.tsx | 2 + .../public/search_bar/search_bar.tsx | 3 + x-pack/plugins/alerting/common/rule.ts | 12 ++- x-pack/plugins/alerting/server/alert/alert.ts | 6 +- .../server/routes/create_rule.test.ts | 7 +- .../alerting/server/routes/get_rule.test.ts | 1 + .../server/routes/lib/actions_schema.ts | 11 ++- .../server/routes/lib/rewrite_actions.test.ts | 5 +- .../server/routes/lib/rewrite_rule.test.ts | 2 +- .../server/routes/update_rule.test.ts | 4 +- .../lib/add_generated_action_values.test.ts | 17 +++- .../lib/add_generated_action_values.ts | 21 ++-- .../rules_client/lib/validate_actions.test.ts | 8 +- .../rules_client/lib/validate_actions.ts | 1 - .../rules_client/tests/bulk_edit.test.ts | 14 +-- .../server/rules_client/tests/create.test.ts | 2 +- .../task_runner/execution_handler.test.ts | 10 +- x-pack/plugins/alerting/server/types.ts | 6 +- .../create_get_summarized_alerts_fn.test.ts | 25 +++-- .../utils/create_get_summarized_alerts_fn.ts | 8 +- .../rules/rule_actions_field/index.tsx | 16 +++- .../public/application/lib/rule_api/create.ts | 2 +- .../public/application/lib/rule_api/update.ts | 2 +- .../action_alerts_filter_query.tsx | 96 +++++++++++++++++++ .../action_alerts_filter_timeframe.test.tsx | 4 +- .../action_alerts_filter_timeframe.tsx | 12 +-- .../action_form.test.tsx | 2 +- .../action_type_form.tsx | 8 +- .../alerts_search_bar/alerts_search_bar.tsx | 17 +++- .../sections/alerts_search_bar/types.ts | 8 ++ .../sections/rule_form/rule_reducer.ts | 17 +++- .../group2/tests/alerting/alerts.ts | 7 +- 33 files changed, 294 insertions(+), 91 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts index b173d67c9f005..ab6df3bdacc2a 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts @@ -29,18 +29,41 @@ export const RuleActionUuid = NonEmptyString; export type RuleActionParams = t.TypeOf; export const RuleActionParams = saved_object_attributes; -export const RuleActionAlertsFilter = t.strict({ +export const RuleActionAlertsFilter = t.partial({ query: t.union([ - t.null, + t.undefined, t.intersection([ t.strict({ kql: t.string, + filters: t.array( + t.intersection([ + t.type({ + meta: t.partial({ + alias: t.union([t.string, t.null]), + disabled: t.boolean, + negate: t.boolean, + controlledBy: t.string, + group: t.string, + index: t.string, + isMultiIndex: t.boolean, + type: t.string, + key: t.string, + params: t.any, + value: t.string, + }), + }), + t.partial({ + $state: t.type({ store: t.any }), + query: t.record(t.string, t.any), + }), + ]) + ), }), t.partial({ dsl: t.string }), ]), ]), timeframe: t.union([ - t.null, + t.undefined, t.strict({ timezone: t.string, days: t.array( diff --git a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx index 8ca32108a2555..aecd588673e09 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/unified_search/public/query_string_input/query_bar_top_row.tsx @@ -120,6 +120,7 @@ export interface QueryBarTopRowProps isScreenshotMode?: boolean; onTextLangQuerySubmit: (query?: Query | AggregateQuery) => void; onTextLangQueryChange: (query: AggregateQuery) => void; + submitOnBlur?: boolean; } export const SharingMetaFields = React.memo(function SharingMetaFields({ @@ -556,6 +557,7 @@ export const QueryBarTopRow = React.memo( size={props.suggestionsSize} isDisabled={props.isDisabled} appName={appName} + submitOnBlur={props.submitOnBlur} deps={{ unifiedSearch, data, diff --git a/src/plugins/unified_search/public/search_bar/search_bar.tsx b/src/plugins/unified_search/public/search_bar/search_bar.tsx index 0a83a45de1d03..3326d81e29109 100644 --- a/src/plugins/unified_search/public/search_bar/search_bar.tsx +++ b/src/plugins/unified_search/public/search_bar/search_bar.tsx @@ -105,6 +105,8 @@ export interface SearchBarOwnProps { * Disables all inputs and interactive elements, */ isDisabled?: boolean; + + submitOnBlur?: boolean; } export type SearchBarProps = SearchBarOwnProps & @@ -585,6 +587,7 @@ class SearchBarUI extends C isScreenshotMode={this.props.isScreenshotMode} onTextLangQuerySubmit={this.onTextLangQuerySubmit} onTextLangQueryChange={this.onTextLangQueryChange} + submitOnBlur={this.props.submitOnBlur} />
); diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 0bac3fd995a27..f58324f1d09ee 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -10,7 +10,7 @@ import type { SavedObjectAttributes, SavedObjectsResolveResponse, } from '@kbn/core/server'; -import type { KueryNode } from '@kbn/es-query'; +import type { Filter, KueryNode } from '@kbn/es-query'; import { RuleNotifyWhenType } from './rule_notify_when_type'; import { RuleSnooze } from './rule_snooze_type'; @@ -94,11 +94,12 @@ export interface AlertsFilterTimeframe extends SavedObjectAttributes { } export interface AlertsFilter extends SavedObjectAttributes { - query: null | { + query?: { kql: string; + filters: Filter[]; dsl?: string; // This fields is generated in the code by using "kql", therefore it's not optional but defined as optional to avoid modifying a lot of files in different plugins }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export type RuleActionAlertsFilterProperty = AlertsFilterTimeframe | RuleActionParam; @@ -191,10 +192,11 @@ export interface Rule { } export interface SanitizedAlertsFilter extends AlertsFilter { - query: null | { + query?: { kql: string; + filters: Filter[]; }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export type SanitizedRuleAction = Omit & { diff --git a/x-pack/plugins/alerting/server/alert/alert.ts b/x-pack/plugins/alerting/server/alert/alert.ts index 1fff89d527d5a..36877db2726c6 100644 --- a/x-pack/plugins/alerting/server/alert/alert.ts +++ b/x-pack/plugins/alerting/server/alert/alert.ts @@ -7,7 +7,7 @@ import { v4 as uuidV4 } from 'uuid'; import { get, isEmpty } from 'lodash'; -import { ALERT_INSTANCE_ID } from '@kbn/rule-data-utils'; +import { ALERT_INSTANCE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; import { CombinedSummarizedAlerts } from '../types'; import { AlertInstanceMeta, @@ -277,7 +277,9 @@ export class Alert< } return !summarizedAlerts.all.data.some( - (alert) => get(alert, ALERT_INSTANCE_ID) === this.getId() + (alert) => + get(alert, ALERT_INSTANCE_ID) === this.getId() || + get(alert, ALERT_RULE_UUID) === this.getId() ); } } diff --git a/x-pack/plugins/alerting/server/routes/create_rule.test.ts b/x-pack/plugins/alerting/server/routes/create_rule.test.ts index 4285c5a986b2b..edd1d10d06236 100644 --- a/x-pack/plugins/alerting/server/routes/create_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/create_rule.test.ts @@ -56,6 +56,7 @@ describe('createRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, timeframe: { days: [1], @@ -92,7 +93,7 @@ describe('createRuleRoute', () => { id: mockedAlert.actions[0].id, params: mockedAlert.actions[0].params, alerts_filter: { - query: { kql: mockedAlert.actions[0].alertsFilter!.query!.kql }, + query: { kql: mockedAlert.actions[0].alertsFilter!.query!.kql, filters: [] }, timeframe: mockedAlert.actions[0].alertsFilter?.timeframe!, }, }, @@ -167,6 +168,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -263,6 +265,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -360,6 +363,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { @@ -457,6 +461,7 @@ describe('createRuleRoute', () => { Object { "alertsFilter": Object { "query": Object { + "filters": Array [], "kql": "name:test", }, "timeframe": Object { diff --git a/x-pack/plugins/alerting/server/routes/get_rule.test.ts b/x-pack/plugins/alerting/server/routes/get_rule.test.ts index 17481e538ed86..a672de9cbf320 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule.test.ts @@ -49,6 +49,7 @@ describe('getRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, timeframe: { days: [1], diff --git a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts index 3e561168aee48..9d6f89e070c3a 100644 --- a/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts +++ b/x-pack/plugins/alerting/server/routes/lib/actions_schema.ts @@ -29,13 +29,20 @@ export const actionsSchema = schema.arrayOf( uuid: schema.maybe(schema.string()), alerts_filter: schema.maybe( schema.object({ - query: schema.nullable( + query: schema.maybe( schema.object({ kql: schema.string(), + filters: schema.arrayOf( + schema.object({ + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + meta: schema.recordOf(schema.string(), schema.any()), + state$: schema.maybe(schema.object({ store: schema.string() })), + }) + ), dsl: schema.maybe(schema.string()), }) ), - timeframe: schema.nullable( + timeframe: schema.maybe( schema.object({ days: schema.arrayOf( schema.oneOf([ diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts index b79e5a91ff381..61dc9282bbfa1 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_actions.test.ts @@ -27,6 +27,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], @@ -42,7 +43,7 @@ describe('rewrite Actions', () => { ).toEqual([ { alerts_filter: { - query: { dsl: '{test:1}', kql: 'test:1s' }, + query: { dsl: '{test:1}', kql: 'test:1s', filters: [] }, timeframe: { days: [1, 2, 3], hours: { end: '15:00', start: '00:00' }, @@ -77,6 +78,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], @@ -104,6 +106,7 @@ describe('rewrite Actions', () => { query: { kql: 'test:1s', dsl: '{test:1}', + filters: [], }, timeframe: { days: [1, 2, 3], diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts index d5d0ba1739355..7a348e583ac6c 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.test.ts @@ -43,7 +43,7 @@ const sampleRule: SanitizedRule & { activeSnoozes?: string[] } = notifyWhen: 'onThrottleInterval', throttle: '1m', }, - alertsFilter: { timeframe: null, query: { kql: 'test:1', dsl: '{}' } }, + alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, }, ], scheduledTaskId: 'xyz456', diff --git a/x-pack/plugins/alerting/server/routes/update_rule.test.ts b/x-pack/plugins/alerting/server/routes/update_rule.test.ts index 4dfe1c00145e8..5a4b3a19c0d7c 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.test.ts @@ -53,8 +53,8 @@ describe('updateRuleRoute', () => { query: { kql: 'name:test', dsl: '{"must": {"term": { "name": "test" }}}', + filters: [], }, - timeframe: null, }, }, ], @@ -123,9 +123,9 @@ describe('updateRuleRoute', () => { "alertsFilter": Object { "query": Object { "dsl": "{\\"must\\": {\\"term\\": { \\"name\\": \\"test\\" }}}", + "filters": Array [], "kql": "name:test", }, - "timeframe": null, }, "group": "default", "id": "2", diff --git a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts index 7ee9d323c7627..6605d9c22a72b 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.test.ts @@ -24,7 +24,15 @@ describe('addGeneratedActionValues()', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:testValue' }, + query: { + kql: 'test:testValue', + filters: [ + { + meta: { key: 'foo', params: { query: 'bar' } }, + query: { match_phrase: { foo: 'bar ' } }, + }, + ], + }, timeframe: { days: [1, 2], hours: { start: '08:00', end: '17:00' }, @@ -41,14 +49,17 @@ describe('addGeneratedActionValues()', () => { test('adds DSL', async () => { const actionWithGeneratedValues = addGeneratedActionValues([mockAction]); expect(actionWithGeneratedValues[0].alertsFilter?.query?.dsl).toBe( - '{"bool":{"should":[{"match":{"test":"testValue"}}],"minimum_should_match":1}}' + '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"test":"testValue"}}],"minimum_should_match":1}},{"match_phrase":{"foo":"bar "}}],"should":[],"must_not":[]}}' ); }); test('throws error if KQL is not valid', async () => { expect(() => addGeneratedActionValues([ - { ...mockAction, alertsFilter: { query: { kql: 'foo:bar:1' }, timeframe: null } }, + { + ...mockAction, + alertsFilter: { query: { kql: 'foo:bar:1', filters: [] } }, + }, ]) ).toThrowErrorMatchingInlineSnapshot('"Error creating DSL query: invalid KQL"'); }); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts index c3cd721698594..71cbf01ea1a4e 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/add_generated_action_values.ts @@ -6,7 +6,7 @@ */ import { v4 } from 'uuid'; -import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { buildEsQuery, Filter } from '@kbn/es-query'; import Boom from '@hapi/boom'; import { NormalizedAlertAction, NormalizedAlertActionWithGeneratedValues } from '..'; @@ -14,9 +14,11 @@ export function addGeneratedActionValues( actions: NormalizedAlertAction[] = [] ): NormalizedAlertActionWithGeneratedValues[] { return actions.map(({ uuid, alertsFilter, ...action }) => { - const generateDSL = (kql: string) => { + const generateDSL = (kql: string, filters: Filter[]) => { try { - return JSON.stringify(toElasticsearchQuery(fromKueryExpression(kql))); + return JSON.stringify( + buildEsQuery(undefined, [{ query: kql, language: 'kuery' }], filters) + ); } catch (e) { throw Boom.badRequest(`Error creating DSL query: invalid KQL`); } @@ -29,13 +31,12 @@ export function addGeneratedActionValues( ? { alertsFilter: { ...alertsFilter, - timeframe: alertsFilter.timeframe || null, - query: !alertsFilter.query - ? null - : { - kql: alertsFilter.query.kql, - dsl: generateDSL(alertsFilter.query.kql), - }, + query: alertsFilter.query + ? { + ...alertsFilter.query, + dsl: generateDSL(alertsFilter.query.kql, alertsFilter.query.filters), + } + : undefined, }, } : {}), diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts index 679f79d477c86..229c009df3eec 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.test.ts @@ -45,7 +45,7 @@ describe('validateActions', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { days: [1], hours: { start: '10:00', end: '17:00' }, timezone: 'UTC' }, }, }, @@ -200,7 +200,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { days: [1], hours: { start: '30:00', end: '17:00' }, timezone: 'UTC' }, }, }, @@ -237,7 +237,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, // @ts-ignore timeframe: { days: [1], hours: { start: '10:00', end: '17:00' } }, }, @@ -261,7 +261,7 @@ describe('validateActions', () => { { ...data.actions[0], alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, timeframe: { // @ts-ignore days: [0, 8], diff --git a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts index 357f624a815cd..b86e30556f090 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/validate_actions.ts @@ -154,7 +154,6 @@ export async function validateActions( ) { actionWithInvalidTimeframe.push(action); } - // alertsFilter time range filter's start time can't be before end time if (alertsFilter.timeframe.hours) { if ( validateHours(alertsFilter.timeframe.hours.start) || diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts index 47b38cce094ba..3598ad31fd390 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_edit.test.ts @@ -706,7 +706,8 @@ describe('bulkEdit()', () => { alertsFilter: { query: { kql: 'name:test', - dsl: '{"bool":{"should":[{"match":{"name":"test"}}],"minimum_should_match":1}}', + dsl: '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"name":"test"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}', + filters: [], }, timeframe: { days: [1], @@ -725,7 +726,7 @@ describe('bulkEdit()', () => { id: '2', params: {}, uuid: '222', - alertsFilter: { query: { kql: 'test:1', dsl: 'test' } }, + alertsFilter: { query: { kql: 'test:1', dsl: 'test', filters: [] } }, }; unsecuredSavedObjectsClient.bulkCreate.mockResolvedValue({ @@ -744,8 +745,7 @@ describe('bulkEdit()', () => { actionRef: 'action_1', uuid: '222', alertsFilter: { - query: { kql: 'test:1', dsl: 'test' }, - timeframe: null, + query: { kql: 'test:1', dsl: 'test', filters: [] }, }, }, ], @@ -802,10 +802,10 @@ describe('bulkEdit()', () => { uuid: '222', alertsFilter: { query: { - dsl: '{"bool":{"should":[{"match":{"test":"1"}}],"minimum_should_match":1}}', + dsl: '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match":{"test":"1"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}', kql: 'test:1', + filters: [], }, - timeframe: null, }, }, ], @@ -835,8 +835,8 @@ describe('bulkEdit()', () => { query: { dsl: 'test', kql: 'test:1', + filters: [], }, - timeframe: null, }, }, ], diff --git a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts index d44d69a6e64bf..aedf130e1f846 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/create.test.ts @@ -3343,7 +3343,7 @@ describe('create()', () => { throttle: null, }, alertsFilter: { - query: { kql: 'test:1' }, + query: { kql: 'test:1', filters: [] }, }, }, ], diff --git a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts index 3aba014226042..03e1ba5976a62 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.test.ts @@ -947,7 +947,7 @@ describe('Execution Handler', () => { message: 'New: {{alerts.new.count}} Ongoing: {{alerts.ongoing.count}} Recovered: {{alerts.recovered.count}}', }, - alertsFilter: { query: { kql: 'test:1', dsl: '{}' } }, + alertsFilter: { query: { kql: 'test:1', dsl: '{}', filters: [] } }, }, ], }, @@ -1332,7 +1332,7 @@ describe('Execution Handler', () => { 'New: {{alerts.new.count}} Ongoing: {{alerts.ongoing.count}} Recovered: {{alerts.recovered.count}}', }, alertsFilter: { - query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}' }, + query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}', filters: [] }, }, }, ], @@ -1351,7 +1351,7 @@ describe('Execution Handler', () => { spaceId: 'test1', excludedAlertInstanceIds: ['foo'], alertsFilter: { - query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}' }, + query: { kql: 'kibana.alert.rule.name:foo', dsl: '{}', filters: [] }, }, }); expect(actionsClient.bulkEnqueueExecution).not.toHaveBeenCalled(); @@ -1392,7 +1392,7 @@ describe('Execution Handler', () => { }, params: {}, alertsFilter: { - query: { kql: 'kibana.alert.instance.id:1', dsl: '{}' }, + query: { kql: 'kibana.alert.instance.id:1', dsl: '{}', filters: [] }, }, }, ], @@ -1412,7 +1412,7 @@ describe('Execution Handler', () => { spaceId: 'test1', excludedAlertInstanceIds: ['foo'], alertsFilter: { - query: { kql: 'kibana.alert.instance.id:1', dsl: '{}' }, + query: { kql: 'kibana.alert.instance.id:1', dsl: '{}', filters: [] }, }, }); expect(actionsClient.bulkEnqueueExecution).toHaveBeenCalledWith([ diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 21ba86dc0f25f..420025b1cd007 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -23,6 +23,7 @@ import { import type { PublicMethodsOf } from '@kbn/utility-types'; import { SharePluginStart } from '@kbn/share-plugin/server'; import { type FieldMap } from '@kbn/alerts-as-data-utils'; +import { Filter } from '@kbn/es-query'; import { RuleTypeRegistry as OrigruleTypeRegistry } from './rule_type_registry'; import { PluginSetupContract, PluginStartContract } from './plugin'; import { RulesClient } from './rules_client'; @@ -285,11 +286,12 @@ export type UntypedRuleType = RuleType< >; export interface RawAlertsFilter extends AlertsFilter { - query: null | { + query?: { kql: string; + filters: Filter[]; dsl: string; }; - timeframe: null | AlertsFilterTimeframe; + timeframe?: AlertsFilterTimeframe; } export interface RawRuleAction extends SavedObjectAttributes { diff --git a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts index 1e55fbe047ddc..3bf83993df1e8 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.test.ts @@ -2098,6 +2098,7 @@ describe('createGetSummarizedAlertsFn', () => { query: { kql: 'kibana.alert.rule.name:test', dsl: '{"bool":{"minimum_should_match":1,"should":[{"match":{"kibana.alert.rule.name":"test"}}]}}', + filters: [], }, timeframe: { days: [1, 2, 3, 4, 5], @@ -2147,11 +2148,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2159,17 +2161,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -2231,11 +2234,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2243,17 +2247,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -2315,11 +2320,12 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', days: [1, 2, 3, 4, 5], timezone: 'UTC', }, source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', }, }, }, @@ -2327,17 +2333,18 @@ describe('createGetSummarizedAlertsFn', () => { script: { script: { params: { + datetimeField: '@timestamp', end: '17:00', start: '08:00', timezone: 'UTC', }, source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { diff --git a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts index 4a7a727276ad4..d4fe127b240ee 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_get_summarized_alerts_fn.ts @@ -573,10 +573,11 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta script: { script: { source: - "params.days.contains(doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())", + 'params.days.contains(doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)).dayOfWeek.getValue())', params: { days: alertsFilter.timeframe.days, timezone: alertsFilter.timeframe.timezone, + datetimeField: TIMESTAMP, }, }, }, @@ -585,12 +586,12 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta script: { script: { source: ` - def alertsDateTime = doc['kibana.alert.start'].value.withZoneSameInstant(ZoneId.of(params.timezone)); + def alertsDateTime = doc[params.datetimeField].value.withZoneSameInstant(ZoneId.of(params.timezone)); def alertsTime = LocalTime.of(alertsDateTime.getHour(), alertsDateTime.getMinute()); def start = LocalTime.parse(params.start); def end = LocalTime.parse(params.end); - if (end.isBefore(start)){ // overnight + if (end.isBefore(start) || end.equals(start)){ // overnight def dayEnd = LocalTime.parse("23:59:59"); def dayStart = LocalTime.parse("00:00:00"); if ((alertsTime.isAfter(start) && alertsTime.isBefore(dayEnd)) || (alertsTime.isAfter(dayStart) && alertsTime.isBefore(end))) { @@ -610,6 +611,7 @@ const generateAlertsFilterDSL = (alertsFilter: AlertsFilter): QueryDslQueryConta start: alertsFilter.timeframe.hours.start, end: alertsFilter.timeframe.hours.end, timezone: alertsFilter.timeframe.timezone, + datetimeField: TIMESTAMP, }, }, }, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx index 02566a8302937..019e20b25db1a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx @@ -195,12 +195,18 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = (key: string, value: RuleActionAlertsFilterProperty, index: number) => { field.setValue((prevValue: RuleAction[]) => { const updatedActions = [...prevValue]; + const { alertsFilter, ...rest } = updatedActions[index]; + const updatedAlertsFilter = { ...alertsFilter }; + + if (value) { + updatedAlertsFilter[key] = value; + } else { + delete updatedAlertsFilter[key]; + } + updatedActions[index] = { - ...updatedActions[index], - alertsFilter: { - ...(updatedActions[index].alertsFilter ?? { query: null, timeframe: null }), - [key]: value, - }, + ...rest, + ...(!isEmpty(updatedAlertsFilter) ? { alertsFilter: updatedAlertsFilter } : {}), }; return updatedActions; }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts index ed56bcdb3ca7d..d8b8c85ad26a5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts @@ -36,7 +36,7 @@ const rewriteBodyRequest: RewriteResponseCase = ({ throttle: frequency!.throttle, summary: frequency!.summary, }, - alertsFilter, + alerts_filter: alertsFilter, })), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts index 15097eb03f156..ee2418d22262b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts @@ -26,7 +26,7 @@ const rewriteBodyRequest: RewriteResponseCase = ({ actions, ... throttle: frequency!.throttle, summary: frequency!.summary, }, - alertsFilter, + alerts_filter: alertsFilter, ...(uuid && { uuid }), })), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx new file mode 100644 index 0000000000000..3cd22cc70a429 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_query.tsx @@ -0,0 +1,96 @@ +/* + * 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 React, { useState, useCallback, useMemo, useEffect } from 'react'; +import { Filter } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; +import { EuiSwitch, EuiSpacer } from '@elastic/eui'; +import { AlertsFilter } from '@kbn/alerting-plugin/common'; +import deepEqual from 'fast-deep-equal'; +import { AlertsSearchBar } from '../alerts_search_bar'; + +interface ActionAlertsFilterQueryProps { + state?: AlertsFilter['query']; + onChange: (update?: AlertsFilter['query']) => void; +} + +export const ActionAlertsFilterQuery: React.FC = ({ + state, + onChange, +}) => { + const [query, setQuery] = useState(state ?? { kql: '', filters: [] }); + + const queryEnabled = useMemo(() => Boolean(state), [state]); + + useEffect(() => { + const nextState = queryEnabled ? query : undefined; + if (!deepEqual(state, nextState)) onChange(nextState); + }, [queryEnabled, query, state, onChange]); + + const toggleQuery = useCallback( + () => onChange(state ? undefined : query), + [state, query, onChange] + ); + const updateQuery = useCallback( + (update: Partial) => { + setQuery({ + ...query, + ...update, + }); + }, + [query, setQuery] + ); + + const onQueryChange = useCallback( + ({ query: newQuery }) => updateQuery({ kql: newQuery }), + [updateQuery] + ); + + const onFiltersUpdated = useCallback( + (filters: Filter[]) => updateQuery({ filters }), + [updateQuery] + ); + + return ( + <> + + {queryEnabled && ( + <> + + + + )} + + ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx index a4c3edfda3344..ef73b3ab305c0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.test.tsx @@ -17,7 +17,7 @@ jest.mock('@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting', () => ({ })); describe('action_alerts_filter_timeframe', () => { - async function setup(timeframe: AlertsFilterTimeframe | null) { + async function setup(timeframe?: AlertsFilterTimeframe) { const wrapper = mountWithIntl( {}} /> ); @@ -32,7 +32,7 @@ describe('action_alerts_filter_timeframe', () => { } it('renders an unchecked switch when passed a null timeframe', async () => { - const wrapper = await setup(null); + const wrapper = await setup(); const alertsFilterTimeframeToggle = wrapper.find( '[data-test-subj="alertsFilterTimeframeToggle"]' diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx index a8e276f81535f..05fcd8fba77a7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_alerts_filter_timeframe.tsx @@ -24,8 +24,8 @@ import { AlertsFilterTimeframe, IsoWeekday } from '@kbn/alerting-plugin/common'; import { I18N_WEEKDAY_OPTIONS_DDD, ISO_WEEKDAYS } from '../../../common/constants'; interface ActionAlertsFilterTimeframeProps { - state: AlertsFilterTimeframe | null; - onChange: (update: AlertsFilterTimeframe | null) => void; + state?: AlertsFilterTimeframe; + onChange: (update?: AlertsFilterTimeframe) => void; } const TIMEZONE_OPTIONS = moment.tz?.names().map((n) => ({ label: n })) ?? [{ label: 'UTC' }]; @@ -46,14 +46,14 @@ const useDefaultTimezone = () => { return kibanaTz; }; -const useTimeframe = (initialTimeframe: AlertsFilterTimeframe | null) => { +const useTimeframe = (initialTimeframe?: AlertsFilterTimeframe) => { const timezone = useDefaultTimezone(); const DEFAULT_TIMEFRAME = { days: [], timezone, hours: { start: '00:00', - end: '24:00', + end: '23:59', }, }; return useState(initialTimeframe || DEFAULT_TIMEFRAME); @@ -79,12 +79,12 @@ export const ActionAlertsFilterTimeframe: React.FC { - const nextState = timeframeEnabled ? timeframe : null; + const nextState = timeframeEnabled ? timeframe : undefined; if (!deepEqual(state, nextState)) onChange(nextState); }, [timeframeEnabled, timeframe, state, onChange]); const toggleTimeframe = useCallback( - () => onChange(state ? null : timeframe), + () => onChange(state ? undefined : timeframe), [state, timeframe, onChange] ); const updateTimeframe = useCallback( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index 18a8a577b7e0f..5be6a91698833 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -351,7 +351,7 @@ describe('action_form', () => { (initialAlert.actions[index] = { ...initialAlert.actions[index], alertsFilter: { - ...(initialAlert.actions[index].alertsFilter ?? { query: null, timeframe: null }), + ...initialAlert.actions[index].alertsFilter, [key]: value, }, }) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 38d267d014824..1743d435b722f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -61,6 +61,7 @@ import { ConnectorsSelection } from './connectors_selection'; import { ActionNotifyWhen } from './action_notify_when'; import { validateParamsForWarnings } from '../../lib/validate_params_for_warnings'; import { ActionAlertsFilterTimeframe } from './action_alerts_filter_timeframe'; +import { ActionAlertsFilterQuery } from './action_alerts_filter_query'; export type ActionTypeFormProps = { actionItem: RuleAction; @@ -405,8 +406,13 @@ export const ActionTypeForm = ({ {showActionAlertsFilter && ( <> {!hideNotifyWhen && } + setActionAlertsFilterProperty('query', query, index)} + /> + setActionAlertsFilterProperty('timeframe', timeframe, index)} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 6a170363d34a8..49b1f9905cd7f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -19,9 +19,16 @@ export function AlertsSearchBar({ appName, featureIds, query, + filters, onQueryChange, + onFiltersUpdated, rangeFrom, rangeTo, + showFilterBar = false, + showDatePicker = true, + showSubmitButton = true, + placeholder = SEARCH_BAR_PLACEHOLDER, + submitOnBlur = false, }: AlertsSearchBarProps) { const { unifiedSearch: { @@ -52,14 +59,20 @@ export function AlertsSearchBar({ ); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts index 2c94c250c168a..b7616333c199a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { Filter } from '@kbn/es-query'; import { ValidFeatureId } from '@kbn/rule-data-utils'; export type QueryLanguageType = 'lucene' | 'kuery'; @@ -15,8 +16,15 @@ export interface AlertsSearchBarProps { rangeFrom?: string; rangeTo?: string; query?: string; + filters?: Filter[]; + showFilterBar?: boolean; + showDatePicker?: boolean; + showSubmitButton?: boolean; + placeholder?: string; + submitOnBlur?: boolean; onQueryChange: (query: { dateRange: { from: string; to: string; mode?: 'absolute' | 'relative' }; query?: string; }) => void; + onFiltersUpdated?: (filters: Filter[]) => void; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts index 8ac7c38276233..54f3871928fb3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_reducer.ts @@ -13,6 +13,7 @@ import { IntervalSchedule, RuleActionAlertsFilterProperty, } from '@kbn/alerting-plugin/common'; +import { isEmpty } from 'lodash/fp'; import { Rule, RuleAction } from '../../../types'; import { DEFAULT_FREQUENCY } from '../../../common/constants'; @@ -237,12 +238,18 @@ export const ruleReducer = ( return state; } else { const oldAction = rule.actions.splice(index, 1)[0]; + const { alertsFilter, ...rest } = oldAction; + const updatedAlertsFilter = { ...alertsFilter }; + + if (value) { + updatedAlertsFilter[key] = value; + } else { + delete updatedAlertsFilter[key]; + } + const updatedAction = { - ...oldAction, - alertsFilter: { - ...(oldAction.alertsFilter ?? { timeframe: null, query: null }), - [key]: value, - }, + ...rest, + ...(!isEmpty(updatedAlertsFilter) ? { alertsFilter: updatedAlertsFilter } : {}), }; rule.actions.splice(index, 0, updatedAction); return { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts index 7b5f41f4448d2..410fee01c71f5 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/alerting/alerts.ts @@ -1244,8 +1244,7 @@ instanceStateValue: true throttle: null, summary: true, alertsFilter: { - timeframe: null, - query: { kql: 'kibana.alert.rule.name:abc' }, + query: { kql: 'kibana.alert.rule.name:abc', filters: [] }, }, }); @@ -1307,8 +1306,7 @@ instanceStateValue: true throttle: null, summary: true, alertsFilter: { - timeframe: null, - query: { kql: 'kibana.alert.instance.id:1' }, + query: { kql: 'kibana.alert.instance.id:1', filters: [] }, }, }); @@ -1383,7 +1381,6 @@ instanceStateValue: true timezone: 'UTC', hours: { start, end }, }, - query: null, }, }); From 085686fceb79cc86face1179582341d7b98ac811 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Fri, 21 Apr 2023 19:26:18 -0400 Subject: [PATCH 17/29] [Dashboard] Removes Reload on Clone / Replace Panel (#155561) Removes the page reload on clone and replace panel to make them feel snappier. --- .../public/dashboard_actions/replace_panel_flyout.tsx | 1 - .../dashboard_container/embeddable/api/panel_management.ts | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx b/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx index d5d5c439a4745..4a2ac8f41d6a6 100644 --- a/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/replace_panel_flyout.tsx @@ -81,7 +81,6 @@ export class ReplacePanelFlyout extends React.Component { } as DashboardPanelState, }, }); - container.reload(); this.showToast(name); this.props.onClose(); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts index ee57970a93cd4..cb2ce9af37bcd 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/api/panel_management.ts @@ -86,11 +86,7 @@ export async function replacePanel( }; } - await this.updateInput({ - panels, - lastReloadRequestTime: new Date().getTime(), - }); - + await this.updateInput({ panels }); return panelId; } From d4f2639377f190340d10b85eb449af51196fce04 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 21 Apr 2023 18:14:57 -0600 Subject: [PATCH 18/29] [Maps] fix maps listing page 'Cannot update a component while rendering a different component' (#155545) To view problem 1) Install sample data set 2) Open maps listing page. Notice the following console warning Screen Shot 2023-04-21 at 9 19 16 AM PR removes side effect during `MapsListViewComp` render that caused [ScreenReaderRouteAnnouncements](https://github.com/elastic/kibana/blob/0cd7973e1ff4693094e802b2cd3b6d711007bc5c/packages/core/chrome/core-chrome-browser-internal/src/ui/header/screen_reader_a11y.tsx) to update state. PR also refactors LoadListAndRender into a functional component with hooks. That change is not needed but was done to isolate the issue. Figured its worth keeping. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../routes/list_page/load_list_and_render.tsx | 80 ++++++++----------- .../routes/list_page/maps_list_view.tsx | 12 ++- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 5 files changed, 43 insertions(+), 52 deletions(-) diff --git a/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx b/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx index 26cb872006dee..f988c5b11ef98 100644 --- a/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/load_list_and_render.tsx @@ -5,12 +5,10 @@ * 2.0. */ -import React, { Component } from 'react'; -import { i18n } from '@kbn/i18n'; +import React, { useState, useEffect } from 'react'; import { Redirect } from 'react-router-dom'; import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { ScopedHistory } from '@kbn/core/public'; -import { getToasts } from '../../kibana_services'; import { MapsListView } from './maps_list_view'; import { APP_ID } from '../../../common/constants'; import { mapsClient } from '../../content_management'; @@ -20,49 +18,39 @@ interface Props { stateTransfer: EmbeddableStateTransfer; } -export class LoadListAndRender extends Component { - _isMounted: boolean = false; - state = { - mapsLoaded: false, - hasSavedMaps: null, - }; - - componentDidMount() { - this._isMounted = true; - this.props.stateTransfer.clearEditorState(APP_ID); - this._loadMapsList(); - } - - componentWillUnmount() { - this._isMounted = false; +export function LoadListAndRender(props: Props) { + const [mapsLoaded, setMapsLoaded] = useState(false); + const [hasSavedMaps, setHasSavedMaps] = useState(true); + + useEffect(() => { + props.stateTransfer.clearEditorState(APP_ID); + + let ignore = false; + mapsClient + .search({ limit: 1 }) + .then((results) => { + if (!ignore) { + setHasSavedMaps(results.hits.length > 0); + setMapsLoaded(true); + } + }) + .catch((err) => { + if (!ignore) { + setMapsLoaded(true); + setHasSavedMaps(false); + } + }); + return () => { + ignore = true; + }; + // only run on mount + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (!mapsLoaded) { + // do not render loading state to avoid UI flash when listing page is displayed + return null; } - async _loadMapsList() { - try { - const results = await mapsClient.search({ limit: 1 }); - if (this._isMounted) { - this.setState({ mapsLoaded: true, hasSavedMaps: !!results.hits.length }); - } - } catch (err) { - if (this._isMounted) { - this.setState({ mapsLoaded: true, hasSavedMaps: false }); - getToasts().addDanger({ - title: i18n.translate('xpack.maps.mapListing.errorAttemptingToLoadSavedMaps', { - defaultMessage: `Unable to load maps`, - }), - text: `${err}`, - }); - } - } - } - - render() { - const { mapsLoaded, hasSavedMaps } = this.state; - - if (mapsLoaded) { - return hasSavedMaps ? : ; - } else { - return null; - } - } + return hasSavedMaps ? : ; } diff --git a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx index 95063f728a8fc..d98444057097d 100644 --- a/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx +++ b/x-pack/plugins/maps/public/routes/list_page/maps_list_view.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, memo } from 'react'; +import React, { useCallback, memo, useEffect } from 'react'; import type { SavedObjectsFindOptionsReference, ScopedHistory } from '@kbn/core/public'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; @@ -72,8 +72,14 @@ function MapsListViewComp({ history }: Props) { const listingLimit = getUiSettings().get(SAVED_OBJECTS_LIMIT_SETTING); const initialPageSize = getUiSettings().get(SAVED_OBJECTS_PER_PAGE_SETTING); - getCoreChrome().docTitle.change(APP_NAME); - getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]); + // TLDR; render should be side effect free + // + // setBreadcrumbs fires observables which cause state changes in ScreenReaderRouteAnnouncements. + // wrap chrome updates in useEffect to avoid potentially causing state changes in other component during render phase. + useEffect(() => { + getCoreChrome().docTitle.change(APP_NAME); + getCoreChrome().setBreadcrumbs([{ text: APP_NAME }]); + }, []); const findMaps = useCallback( async ( diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index dbc92e04c393f..58305363bb55e 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -20560,7 +20560,6 @@ "xpack.maps.mapActions.removeFeatureError": "Impossible de retirer la fonctionnalité de l’index.", "xpack.maps.mapListing.entityName": "carte", "xpack.maps.mapListing.entityNamePlural": "cartes", - "xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "Impossible de charger les cartes", "xpack.maps.mapSavedObjectLabel": "Carte", "xpack.maps.mapSettingsPanel.addCustomIcon": "Ajouter une icône personnalisée", "xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "Ajuster automatiquement la carte aux limites de données", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 22cfeb42e69d8..a17220cadd2d2 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20560,7 +20560,6 @@ "xpack.maps.mapActions.removeFeatureError": "インデックスから機能を削除できません。", "xpack.maps.mapListing.entityName": "マップ", "xpack.maps.mapListing.entityNamePlural": "マップ", - "xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "マップを読み込めません", "xpack.maps.mapSavedObjectLabel": "マップ", "xpack.maps.mapSettingsPanel.addCustomIcon": "カスタムアイコンを追加", "xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "自動的にマップをデータ境界に合わせる", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 67bf31893d10d..057233f627ace 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20560,7 +20560,6 @@ "xpack.maps.mapActions.removeFeatureError": "无法从索引中移除特征。", "xpack.maps.mapListing.entityName": "地图", "xpack.maps.mapListing.entityNamePlural": "地图", - "xpack.maps.mapListing.errorAttemptingToLoadSavedMaps": "无法加载地图", "xpack.maps.mapSavedObjectLabel": "地图", "xpack.maps.mapSettingsPanel.addCustomIcon": "添加定制图标", "xpack.maps.mapSettingsPanel.autoFitToBoundsLocationLabel": "使地图自动适应数据边界", From ef64acf405ed7577426571fbb87a898c1934bc81 Mon Sep 17 00:00:00 2001 From: Garrett Spong Date: Fri, 21 Apr 2023 18:20:26 -0600 Subject: [PATCH 19/29] [RAM] Adds revision to event-log (#153716) ## Summary Follow on from https://github.com/elastic/kibana/pull/151388 & https://github.com/elastic/kibana/pull/147398, which includes the rule's current `revision` when writing to the kibana event-log. Note: Added as `kibana.alert.rule.revision` instead of as ECS field `rule.version` as the [ECS docs](https://www.elastic.co/guide/en/ecs/current/ecs-rule.html#field-rule-version) conflate `version` & `revision` and figured it was best to be explicit. If we do indeed want to use `rule.version` I'll make the change.

### 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 --- .../plugins/alerting/common/alert_summary.ts | 1 + .../lib/alert_summary_from_event_log.test.ts | 2 + .../lib/alert_summary_from_event_log.ts | 1 + .../alerting_event_logger.test.ts | 1 + .../alerting_event_logger.ts | 5 + ...eate_alert_event_log_record_object.test.ts | 6 ++ .../create_alert_event_log_record_object.ts | 3 + .../routes/get_rule_alert_summary.test.ts | 1 + .../legacy/get_alert_instance_summary.test.ts | 1 + .../rules_client/lib/recover_rule_alerts.ts | 1 + .../server/rules_client/tests/disable.test.ts | 9 ++ .../tests/get_alert_summary.test.ts | 1 + .../alerting/server/task_runner/fixtures.ts | 1 + .../transform_action_params.test.ts | 102 +++++++++--------- .../plugins/event_log/generated/mappings.json | 3 + x-pack/plugins/event_log/generated/schemas.ts | 1 + x-pack/plugins/event_log/scripts/mappings.js | 3 + .../rule_execution_log/__mocks__/index.ts | 1 + .../client_for_executors/client.ts | 5 +- .../client_for_executors/client_interface.ts | 5 + .../event_log/event_log_writer.ts | 4 + .../rule_schema/model/rule_schemas.mock.ts | 2 +- .../create_security_rule_type_wrapper.ts | 1 + .../rule_types/es_query/rule_type.test.ts | 2 +- .../index_threshold/rule_type.test.ts | 8 +- .../lib/rule_api/rule_summary.test.ts | 2 + .../rule_details/components/rule.test.tsx | 1 + .../components/rule_route.test.tsx | 1 + .../rule_details/components/test_helpers.ts | 1 + .../tests/alerting/get_alert_summary.ts | 1 + .../alerting/group1/get_alert_summary.ts | 2 + .../event_log/service_api_integration.ts | 1 + 32 files changed, 121 insertions(+), 58 deletions(-) diff --git a/x-pack/plugins/alerting/common/alert_summary.ts b/x-pack/plugins/alerting/common/alert_summary.ts index a465d6af2daed..ed6cf325e20b1 100644 --- a/x-pack/plugins/alerting/common/alert_summary.ts +++ b/x-pack/plugins/alerting/common/alert_summary.ts @@ -29,6 +29,7 @@ export interface AlertSummary { errorMessages: Array<{ date: string; message: string }>; alerts: Record; executionDuration: ExecutionDuration; + revision: number; } export interface AlertStatus { diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts index dbcb75a7816e7..45f8d84fd4a98 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.test.ts @@ -42,6 +42,7 @@ describe('alertSummaryFromEventLog', () => { "lastRun": undefined, "muteAll": false, "name": "rule-name", + "revision": 0, "ruleTypeId": "123", "status": "OK", "statusEndDate": "2020-06-18T01:00:00.000Z", @@ -88,6 +89,7 @@ describe('alertSummaryFromEventLog', () => { "lastRun": undefined, "muteAll": true, "name": "rule-name-2", + "revision": 0, "ruleTypeId": "456", "status": "OK", "statusEndDate": "2020-06-18T03:00:00.000Z", diff --git a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts index 9dc4a4fec5f8e..d316b1b5bf01c 100644 --- a/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts +++ b/x-pack/plugins/alerting/server/lib/alert_summary_from_event_log.ts @@ -40,6 +40,7 @@ export function alertSummaryFromEventLog(params: AlertSummaryFromEventLogParams) average: 0, valuesWithTimestamp: {}, }, + revision: rule.revision, }; const alerts = new Map(); diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index 62c8d9eed29fd..2b6075cd49f49 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -55,6 +55,7 @@ const context: RuleContextOpts = { spaceId: 'test-space', executionId: 'abcd-efgh-ijklmnop', taskScheduledAt: new Date('2020-01-01T00:00:00.000Z'), + ruleRevision: 0, }; const contextWithScheduleDelay = { ...context, taskScheduleDelay: 7200000 }; diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts index 60d9d21f16113..3af760488a259 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -30,6 +30,7 @@ export interface RuleContextOpts { executionId: string; taskScheduledAt: Date; ruleName?: string; + ruleRevision?: number; } type RuleContext = RuleContextOpts & { @@ -266,6 +267,7 @@ export function createAlertRecord(context: RuleContextOpts, alert: AlertOpts) { ruleName: context.ruleName, flapping: alert.flapping, maintenanceWindowIds: alert.maintenanceWindowIds, + ruleRevision: context.ruleRevision, }); } @@ -296,6 +298,7 @@ export function createActionExecuteRecord(context: RuleContextOpts, action: Acti ], ruleName: context.ruleName, alertSummary: action.alertSummary, + ruleRevision: context.ruleRevision, }); } @@ -322,6 +325,7 @@ export function createExecuteTimeoutRecord(context: RuleContextOpts) { }, ], ruleName: context.ruleName, + ruleRevision: context.ruleRevision, }); } @@ -334,6 +338,7 @@ export function initializeExecuteRecord(context: RuleContext) { spaceId: context.spaceId, executionId: context.executionId, action: EVENT_LOG_ACTIONS.execute, + ruleRevision: context.ruleRevision, task: { scheduled: context.taskScheduledAt.toISOString(), scheduleDelay: Millis2Nanos * context.taskScheduleDelay, diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index e62ec213cba0f..6b18d1aac93dd 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -36,6 +36,7 @@ describe('createAlertEventLogRecordObject', () => { ruleType, consumer: 'rule-consumer', action: 'execute-start', + ruleRevision: 0, timestamp: '1970-01-01T00:00:00.000Z', task: { scheduled: '1970-01-01T00:00:00.000Z', @@ -66,6 +67,7 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, maintenance_window_ids: MAINTENANCE_WINDOW_IDS, @@ -107,6 +109,7 @@ describe('createAlertEventLogRecordObject', () => { group: 'group 1', message: 'message text here', namespace: 'default', + ruleRevision: 0, state: { start: '1970-01-01T00:00:00.000Z', end: '1970-01-01T00:05:00.000Z', @@ -139,6 +142,7 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, maintenance_window_ids: MAINTENANCE_WINDOW_IDS, @@ -182,6 +186,7 @@ describe('createAlertEventLogRecordObject', () => { group: 'group 1', message: 'action execution start', namespace: 'default', + ruleRevision: 0, state: { start: '1970-01-01T00:00:00.000Z', end: '1970-01-01T00:05:00.000Z', @@ -224,6 +229,7 @@ describe('createAlertEventLogRecordObject', () => { execution: { uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', }, + revision: 0, rule_type_id: 'test', }, maintenance_window_ids: MAINTENANCE_WINDOW_IDS, diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index 97284f0ce9f78..251df68e5267f 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -43,6 +43,7 @@ interface CreateAlertEventLogRecordParams { recovered: number; }; maintenanceWindowIds?: string[]; + ruleRevision?: number; } export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecordParams): Event { @@ -62,6 +63,7 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor alertUuid, alertSummary, maintenanceWindowIds, + ruleRevision, } = params; const alerting = params.instanceId || group || alertSummary @@ -97,6 +99,7 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor ...(maintenanceWindowIds ? { maintenance_window_ids: maintenanceWindowIds } : {}), ...(alertUuid ? { uuid: alertUuid } : {}), rule: { + revision: ruleRevision, rule_type_id: ruleType.id, ...(consumer ? { consumer } : {}), ...(executionId diff --git a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts index a6b0e88bbf5af..f7fe1a3406e9c 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts @@ -38,6 +38,7 @@ describe('getRuleAlertSummaryRoute', () => { status: 'OK', errorMessages: [], alerts: {}, + revision: 0, executionDuration: { average: 1, valuesWithTimestamp: { diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts index 672ccee369aac..a78fcd7f86f65 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts @@ -47,6 +47,7 @@ describe('getAlertInstanceSummaryRoute', () => { average: 0, valuesWithTimestamp: {}, }, + revision: 0, }; it('gets alert instance summary', async () => { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts b/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts index bf8d10a341597..9648f23dc05d2 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/recover_rule_alerts.ts @@ -43,6 +43,7 @@ export const recoverRuleAlerts = async ( const event = createAlertEventLogRecordObject({ ruleId: id, ruleName: attributes.name, + ruleRevision: attributes.revision, ruleType: context.ruleTypeRegistry.get(attributes.alertTypeId), consumer: attributes.consumer, instanceId: alertId, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index c09b491dbbdcc..b135f9b0ee23d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -100,6 +100,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: true, + revision: 0, scheduledTaskId: '1', actions: [ { @@ -224,6 +225,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -277,6 +279,7 @@ describe('disable()', () => { }, params: { alertId: '1', + revision: 0, }, ownerId: null, }); @@ -296,6 +299,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -333,6 +337,7 @@ describe('disable()', () => { uuid: 'uuid-1', rule: { consumer: 'myApp', + revision: 0, rule_type_id: '123', }, }, @@ -379,6 +384,7 @@ describe('disable()', () => { meta: { versionApiKeyLastmodified: 'v7.10.0', }, + revision: 0, scheduledTaskId: '1', apiKey: 'MTIzOmFiYw==', apiKeyOwner: 'elastic', @@ -425,6 +431,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: '1', updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', @@ -517,6 +524,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: null, updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', @@ -565,6 +573,7 @@ describe('disable()', () => { schedule: { interval: '10s' }, alertTypeId: 'myType', enabled: false, + revision: 0, scheduledTaskId: null, updatedAt: '2019-02-12T21:01:22.479Z', updatedBy: 'elastic', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts index 212977a8381c2..dbfcc5f3fc017 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts @@ -189,6 +189,7 @@ describe('getAlertSummary()', () => { "lastRun": "2019-02-12T21:01:32.479Z", "muteAll": false, "name": "rule-name", + "revision": 0, "ruleTypeId": "123", "status": "Active", "statusEndDate": "2019-02-12T21:01:22.479Z", diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index 0b5cd235185d0..9be9064908fcb 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -409,6 +409,7 @@ export const mockAAD = { execution: { uuid: 'c35db7cc-5bf7-46ea-b43f-b251613a5b72' }, name: 'test-rule', producer: 'infrastructure', + revision: 0, rule_type_id: 'metrics.alert.threshold', uuid: '0de91960-7643-11ed-b719-bb9db8582cb6', tags: [], diff --git a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts index cd581afafa266..a8889bc620c83 100644 --- a/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/transform_action_params.test.ts @@ -182,10 +182,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"1\\" exists", - } - `); + Object { + "message": "Value \\"1\\" exists", + } + `); }); test('alertName is passed to templates', () => { @@ -212,10 +212,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"alert-name\\" exists", - } - `); + Object { + "message": "Value \\"alert-name\\" exists", + } + `); }); test('tags is passed to templates', () => { @@ -242,10 +242,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"tag-A,tag-B\\" exists", - } - `); + Object { + "message": "Value \\"tag-A,tag-B\\" exists", + } + `); }); test('undefined tags is passed to templates', () => { @@ -271,10 +271,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"\\" is undefined and renders as empty string", - } - `); + Object { + "message": "Value \\"\\" is undefined and renders as empty string", + } + `); }); test('empty tags is passed to templates', () => { @@ -301,10 +301,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"\\" is an empty array and renders as empty string", - } - `); + Object { + "message": "Value \\"\\" is an empty array and renders as empty string", + } + `); }); test('spaceId is passed to templates', () => { @@ -331,10 +331,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"spaceId-A\\" exists", - } - `); + Object { + "message": "Value \\"spaceId-A\\" exists", + } + `); }); test('alertInstanceId is passed to templates', () => { @@ -361,10 +361,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"2\\" exists", - } - `); + Object { + "message": "Value \\"2\\" exists", + } + `); }); test('alertActionGroup is passed to templates', () => { @@ -391,10 +391,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"action-group\\" exists", - } - `); + Object { + "message": "Value \\"action-group\\" exists", + } + `); }); test('alertActionGroupName is passed to templates', () => { @@ -421,10 +421,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"Action Group\\" exists", - } - `); + Object { + "message": "Value \\"Action Group\\" exists", + } + `); }); test('rule variables are passed to templates', () => { @@ -451,10 +451,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"1\\", \\"alert-name\\", \\"spaceId-A\\" and \\"tag-A,tag-B\\" exist", - } - `); + Object { + "message": "Value \\"1\\", \\"alert-name\\", \\"spaceId-A\\" and \\"tag-A,tag-B\\" exist", + } + `); }); test('rule alert variables are passed to templates', () => { @@ -482,10 +482,10 @@ describe('transformActionParams', () => { flapping: false, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"2\\", \\"action-group\\", \\"uuid-1\\" and \\"Action Group\\" exist", - } - `); + Object { + "message": "Value \\"2\\", \\"action-group\\", \\"uuid-1\\" and \\"Action Group\\" exist", + } + `); }); test('date is passed to templates', () => { @@ -613,10 +613,10 @@ describe('transformActionParams', () => { flapping: true, }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"true\\" exists", - } - `); + Object { + "message": "Value \\"true\\" exists", + } + `); }); }); @@ -665,9 +665,9 @@ describe('transformSummaryActionParams', () => { const result = transformSummaryActionParams({ ...params, actionParams }); expect(result).toMatchInlineSnapshot(` - Object { - "message": "Value \\"{\\"@timestamp\\":\\"2022-12-07T15:38:43.472Z\\",\\"event\\":{\\"kind\\":\\"signal\\",\\"action\\":\\"active\\"},\\"kibana\\":{\\"version\\":\\"8.7.0\\",\\"space_ids\\":[\\"default\\"],\\"alert\\":{\\"instance\\":{\\"id\\":\\"*\\"},\\"uuid\\":\\"2d3e8fe5-3e8b-4361-916e-9eaab0bf2084\\",\\"status\\":\\"active\\",\\"workflow_status\\":\\"open\\",\\"reason\\":\\"system.cpu is 90% in the last 1 min for all hosts. Alert when > 50%.\\",\\"time_range\\":{\\"gte\\":\\"2022-01-01T12:00:00.000Z\\"},\\"start\\":\\"2022-12-07T15:23:13.488Z\\",\\"duration\\":{\\"us\\":100000},\\"flapping\\":false,\\"rule\\":{\\"category\\":\\"Metric threshold\\",\\"consumer\\":\\"alerts\\",\\"execution\\":{\\"uuid\\":\\"c35db7cc-5bf7-46ea-b43f-b251613a5b72\\"},\\"name\\":\\"test-rule\\",\\"producer\\":\\"infrastructure\\",\\"rule_type_id\\":\\"metrics.alert.threshold\\",\\"uuid\\":\\"0de91960-7643-11ed-b719-bb9db8582cb6\\",\\"tags\\":[]}}}}\\", \\"http://ruleurl\\" and \\"{\\"foo\\":\\"bar\\",\\"foo_bar\\":true,\\"name\\":\\"test-rule\\",\\"id\\":\\"1\\"}\\" exist", - } + Object { + "message": "Value \\"{\\"@timestamp\\":\\"2022-12-07T15:38:43.472Z\\",\\"event\\":{\\"kind\\":\\"signal\\",\\"action\\":\\"active\\"},\\"kibana\\":{\\"version\\":\\"8.7.0\\",\\"space_ids\\":[\\"default\\"],\\"alert\\":{\\"instance\\":{\\"id\\":\\"*\\"},\\"uuid\\":\\"2d3e8fe5-3e8b-4361-916e-9eaab0bf2084\\",\\"status\\":\\"active\\",\\"workflow_status\\":\\"open\\",\\"reason\\":\\"system.cpu is 90% in the last 1 min for all hosts. Alert when > 50%.\\",\\"time_range\\":{\\"gte\\":\\"2022-01-01T12:00:00.000Z\\"},\\"start\\":\\"2022-12-07T15:23:13.488Z\\",\\"duration\\":{\\"us\\":100000},\\"flapping\\":false,\\"rule\\":{\\"category\\":\\"Metric threshold\\",\\"consumer\\":\\"alerts\\",\\"execution\\":{\\"uuid\\":\\"c35db7cc-5bf7-46ea-b43f-b251613a5b72\\"},\\"name\\":\\"test-rule\\",\\"producer\\":\\"infrastructure\\",\\"revision\\":0,\\"rule_type_id\\":\\"metrics.alert.threshold\\",\\"uuid\\":\\"0de91960-7643-11ed-b719-bb9db8582cb6\\",\\"tags\\":[]}}}}\\", \\"http://ruleurl\\" and \\"{\\"foo\\":\\"bar\\",\\"foo_bar\\":true,\\"name\\":\\"test-rule\\",\\"id\\":\\"1\\"}\\" exist", + } `); }); diff --git a/x-pack/plugins/event_log/generated/mappings.json b/x-pack/plugins/event_log/generated/mappings.json index f50a1c0ef3321..e802567b2ab7f 100644 --- a/x-pack/plugins/event_log/generated/mappings.json +++ b/x-pack/plugins/event_log/generated/mappings.json @@ -398,6 +398,9 @@ } } }, + "revision": { + "type": "long" + }, "rule_type_id": { "type": "keyword", "ignore_above": 1024 diff --git a/x-pack/plugins/event_log/generated/schemas.ts b/x-pack/plugins/event_log/generated/schemas.ts index 6706db9635397..8621df23020bd 100644 --- a/x-pack/plugins/event_log/generated/schemas.ts +++ b/x-pack/plugins/event_log/generated/schemas.ts @@ -178,6 +178,7 @@ export const EventSchema = schema.maybe( ), }) ), + revision: ecsStringOrNumber(), rule_type_id: ecsString(), }) ), diff --git a/x-pack/plugins/event_log/scripts/mappings.js b/x-pack/plugins/event_log/scripts/mappings.js index 7081c321cc659..768988aa3c07b 100644 --- a/x-pack/plugins/event_log/scripts/mappings.js +++ b/x-pack/plugins/event_log/scripts/mappings.js @@ -177,6 +177,9 @@ exports.EcsCustomPropertyMappings = { }, }, }, + revision: { + type: 'long', + }, rule_type_id: { type: 'keyword', ignore_above: 1024, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts index c0cb0cefa234e..c68eab1baec36 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/__mocks__/index.ts @@ -40,6 +40,7 @@ const ruleExecutionLogForExecutorsMock = { ruleId: context.ruleId ?? 'some rule id', ruleUuid: context.ruleUuid ?? 'some rule uuid', ruleName: context.ruleName ?? 'Some rule', + ruleRevision: context.ruleRevision ?? 0, ruleType: context.ruleType ?? 'some rule type', spaceId: context.spaceId ?? 'some space id', }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts index a0cbe754d6b3c..a8d61aa7dda84 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client.ts @@ -50,7 +50,7 @@ export const createClientForExecutors = ( const baseLogSuffix = baseCorrelationIds.getLogSuffix(); const baseLogMeta = baseCorrelationIds.getLogMeta(); - const { executionId, ruleId, ruleUuid, ruleName, ruleType, spaceId } = context; + const { executionId, ruleId, ruleUuid, ruleName, ruleRevision, ruleType, spaceId } = context; const client: IRuleExecutionLogForExecutors = { get context() { @@ -140,6 +140,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, @@ -202,6 +203,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, @@ -213,6 +215,7 @@ export const createClientForExecutors = ( ruleId, ruleUuid, ruleName, + ruleRevision, ruleType, spaceId, executionId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts index cb65b42d50b69..54cad1f72be07 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_executors/client_interface.ts @@ -92,6 +92,11 @@ export interface RuleExecutionContext { */ ruleName: string; + /** + * Current revision of the rule being execution (rule.revision) + */ + ruleRevision: number; + /** * Alerting Framework's rule type id of the rule being executed. */ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts index aa1fcf36aba68..ceed2f1d3a739 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_writer.ts @@ -31,6 +31,7 @@ export interface BaseArgs { ruleId: string; ruleUuid: string; ruleName: string; + ruleRevision: number; ruleType: string; spaceId: string; executionId: string; @@ -83,6 +84,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL execution: { uuid: args.executionId, }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], @@ -126,6 +128,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL status: args.newStatus, status_order: ruleExecutionStatusToNumber(args.newStatus), }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], @@ -167,6 +170,7 @@ export const createEventLogWriter = (eventLogService: IEventLogService): IEventL uuid: args.executionId, metrics: args.metrics, }, + revision: args.ruleRevision, }, }, space_ids: [args.spaceId], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts index 10a213a5d8b2f..44fa44ee01892 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.mock.ts @@ -213,10 +213,10 @@ export const getRuleConfigMock = (type: string = 'rule-type'): SanitizedRuleConf consumer: 'sample consumer', notifyWhen: null, producer: 'sample producer', + revision: 0, ruleTypeId: `${type}-id`, ruleTypeName: type, muteAll: false, - revision: 0, snoozeSchedule: [], }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index a22a95adf307b..e2650f0a142f2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -124,6 +124,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ruleId: rule.id, ruleUuid: params.ruleId, ruleName: rule.name, + ruleRevision: rule.revision, ruleType: rule.ruleTypeId, spaceId, }, diff --git a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts index 8b1679241d9c9..fded7e8ee37b9 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/es_query/rule_type.test.ts @@ -713,10 +713,10 @@ async function invokeExecutor({ tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, diff --git a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts index 92664d05ff9ab..b424fb742b5ab 100644 --- a/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/rule_types/index_threshold/rule_type.test.ts @@ -203,10 +203,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -270,10 +270,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -337,10 +337,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, @@ -403,10 +403,10 @@ describe('ruleType', () => { tags: [], consumer: '', producer: '', + revision: 0, ruleTypeId: '', ruleTypeName: '', enabled: true, - revision: 0, schedule: { interval: '1h', }, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts index 43beb66b40f9e..889f2634eacc9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/rule_summary.test.ts @@ -28,6 +28,7 @@ describe('loadRuleSummary', () => { lastRun: '2021-04-01T22:18:27.609Z', muteAll: false, name: 'test', + revision: 0, ruleTypeId: '.index-threshold', status: 'OK', statusEndDate: '2021-04-01T22:19:25.174Z', @@ -55,6 +56,7 @@ describe('loadRuleSummary', () => { last_run: '2021-04-01T22:18:27.609Z', mute_all: false, name: 'test', + revision: 0, rule_type_id: '.index-threshold', status: 'OK', status_end_date: '2021-04-01T22:19:25.174Z', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx index f3775e5de5066..821d4edf16c66 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule.test.tsx @@ -481,6 +481,7 @@ function mockRuleSummary(overloads: Partial = {}): RuleSummary { throttle: '', enabled: true, errorMessages: [], + revision: 0, statusStartDate: fake2MinutesAgo.toISOString(), statusEndDate: fakeNow.toISOString(), alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx index 117bca134f0f9..1826894ef70f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_route.test.tsx @@ -162,6 +162,7 @@ function mockRuleSummary(overloads: Partial = {}): any { throttle: null, enabled: true, errorMessages: [], + revision: 0, statusStartDate: fake2MinutesAgo.toISOString(), statusEndDate: fakeNow.toISOString(), alerts: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts index 4146ac8006325..d72e381c170ab 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/test_helpers.ts @@ -95,6 +95,7 @@ export function mockRuleSummary(overloads: Partial = {}): RuleSumma throttle: '', enabled: true, errorMessages: [], + revision: 0, statusStartDate: '2022-03-21T07:40:46-07:00', statusEndDate: '2022-03-25T07:40:46-07:00', alerts: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts index f0ba9dc451937..7ad1a54de2485 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get_alert_summary.ts @@ -78,6 +78,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo expect(stableBody).to.eql({ name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts index 6f8de29437621..9be9db54904aa 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get_alert_summary.ts @@ -68,6 +68,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo id: createdRule.id, name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', @@ -106,6 +107,7 @@ export default function createGetAlertSummaryTests({ getService }: FtrProviderCo id: createdRule.id, name: 'abc', tags: ['foo'], + revision: 0, rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts index 2bd65ef5dcd3e..dc244a51bb183 100644 --- a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts @@ -163,6 +163,7 @@ export default function ({ getService }: FtrProviderContext) { execution_gap_duration_s: 3000, }, }, + revision: 0, }, }, alerting: { From a108b8c9c6c641a68aaa3e343f6a7e823f1340a8 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Fri, 21 Apr 2023 18:46:57 -0600 Subject: [PATCH 20/29] [maps] metrics mask (#154983) Fixes https://github.com/elastic/kibana/issues/55520 PR adds ability to set mask configuration for metric fields to hide features client side that are outside the range of the mask. Screen Shot 2023-04-19 at 3 45 15 PM Screen Shot 2023-04-19 at 3 44 45 PM For vector layer, I could not use easier solution of filter expression since filter expressions do not support feature-state. Instead, implementation sets opacity paint property to 0. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/maps/common/constants.ts | 9 +- .../source_descriptor_types.ts | 5 + .../classes/fields/agg/agg_field_types.ts | 3 + .../classes/fields/agg/count_agg_field.ts | 10 +- .../classes/fields/agg/es_agg_factory.ts | 3 + .../fields/agg/top_term_percentage_field.ts | 4 + .../layers/heatmap_layer/heatmap_layer.ts | 16 +- .../blended_vector_layer.ts | 6 +- .../classes/layers/vector_layer/mask.ts | 193 ++++++++++++++++++ .../mvt_vector_layer.test.tsx | 7 + .../layers/vector_layer/vector_layer.tsx | 76 ++++++- .../sources/es_agg_source/es_agg_source.ts | 14 +- .../es_agg_source/get_agg_display_name.ts | 48 +++++ .../classes/sources/es_agg_source/index.ts | 1 + .../classes/sources/es_agg_source/types.ts | 6 + .../update_source_editor.test.tsx.snap | 4 + .../es_geo_grid_source/es_geo_grid_source.tsx | 19 ++ .../update_source_editor.test.tsx | 1 + .../update_source_editor.tsx | 4 +- .../es_geo_line_source/es_geo_line_source.tsx | 7 + .../update_source_editor.tsx | 8 +- .../es_pew_pew_source/es_pew_pew_source.tsx | 8 +- ...rce_editor.js => update_source_editor.tsx} | 35 ++-- .../es_term_source/es_term_source.test.js | 4 +- .../sources/es_term_source/es_term_source.ts | 5 +- .../components/legend/heatmap_legend.tsx | 31 ++- .../vector/components/legend/mask_legend.tsx | 95 +++++++++ .../components/legend/vector_style_legend.tsx | 67 +++++- .../properties/dynamic_color_property.tsx | 10 +- .../properties/static_color_property.ts | 10 +- .../classes/styles/vector/vector_style.tsx | 17 +- .../metrics_editor.test.tsx.snap | 2 + .../metrics_editor/mask_expression/index.ts | 8 + .../mask_expression/mask_editor.tsx | 183 +++++++++++++++++ .../mask_expression/mask_expression.tsx | 104 ++++++++++ .../metrics_editor/metric_editor.tsx | 15 ++ .../metrics_editor/metric_select.tsx | 33 +-- .../metrics_editor/metrics_editor.test.tsx | 1 + .../metrics_editor/metrics_editor.tsx | 4 + .../metrics_expression.test.tsx.snap | 2 + .../resources/metrics_expression.tsx | 1 + .../attribution_form_row.test.tsx.snap | 6 +- .../layer_settings/attribution_form_row.tsx | 11 +- .../layer_settings/attribution_popover.tsx | 3 +- .../style_settings/style_settings.tsx | 2 +- .../tooltip_control/tooltip_control.test.tsx | 3 + .../tooltip_control/tooltip_control.tsx | 9 + .../connected_components/panel_strings.ts | 9 + .../translations/translations/fr-FR.json | 12 -- .../translations/translations/ja-JP.json | 14 +- .../translations/translations/zh-CN.json | 14 +- 51 files changed, 1022 insertions(+), 140 deletions(-) create mode 100644 x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts create mode 100644 x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts rename x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/{update_source_editor.js => update_source_editor.tsx} (74%) create mode 100644 x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx create mode 100644 x-pack/plugins/maps/public/components/metrics_editor/mask_expression/index.ts create mode 100644 x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_editor.tsx create mode 100644 x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 6c8f719bf2886..8f5ad4869ebe7 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -188,10 +188,6 @@ export const GEOCENTROID_AGG_NAME = 'gridCentroid'; export const TOP_TERM_PERCENTAGE_SUFFIX = '__percentage'; export const DEFAULT_PERCENTILE = 50; -export const COUNT_PROP_LABEL = i18n.translate('xpack.maps.aggs.defaultCountLabel', { - defaultMessage: 'count', -}); - export const COUNT_PROP_NAME = 'doc_count'; export enum STYLE_TYPE { @@ -341,6 +337,11 @@ export enum WIZARD_ID { TMS_LAYER = 'tmsLayer', } +export enum MASK_OPERATOR { + ABOVE = 'ABOVE', + BELOW = 'BELOW', +} + // Maplibre does not provide any feedback when rendering is complete. // Workaround is hard-coded timeout period. export const RENDER_TIMEOUT = 1000; diff --git a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts index b862558d6a215..c76bc8f6a6e17 100644 --- a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts @@ -13,6 +13,7 @@ import { SortDirection } from '@kbn/data-plugin/common/search'; import { AGG_TYPE, GRID_RESOLUTION, + MASK_OPERATOR, RENDER_AS, SCALING_TYPES, MVT_FIELD_TYPE, @@ -49,6 +50,10 @@ export type AbstractESSourceDescriptor = AbstractSourceDescriptor & { type AbstractAggDescriptor = { type: AGG_TYPE; label?: string; + mask?: { + operator: MASK_OPERATOR; + value: number; + }; }; export type CountAggDescriptor = AbstractAggDescriptor & { diff --git a/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts b/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts index d881accb1e42f..6db0d32dbd551 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/agg_field_types.ts @@ -9,14 +9,17 @@ import { DataView } from '@kbn/data-plugin/common'; import { IField } from '../field'; import { IESAggSource } from '../../sources/es_agg_source'; import { FIELD_ORIGIN } from '../../../../common/constants'; +import { AggDescriptor } from '../../../../common/descriptor_types'; export interface IESAggField extends IField { getValueAggDsl(indexPattern: DataView): unknown | null; getBucketCount(): number; + getMask(): AggDescriptor['mask'] | undefined; } export interface CountAggFieldParams { label?: string; source: IESAggSource; origin: FIELD_ORIGIN; + mask?: AggDescriptor['mask']; } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts index 03ade37c3dbec..140f605832fca 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/count_agg_field.ts @@ -14,7 +14,7 @@ import { DataView } from '@kbn/data-plugin/common'; import { IESAggSource } from '../../sources/es_agg_source'; import { IVectorSource } from '../../sources/vector_source'; import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants'; -import { TileMetaFeature } from '../../../../common/descriptor_types'; +import { AggDescriptor, TileMetaFeature } from '../../../../common/descriptor_types'; import { ITooltipProperty, TooltipProperty } from '../../tooltips/tooltip_property'; import { ESAggTooltipProperty } from '../../tooltips/es_agg_tooltip_property'; import { IESAggField, CountAggFieldParams } from './agg_field_types'; @@ -25,11 +25,13 @@ export class CountAggField implements IESAggField { protected readonly _source: IESAggSource; private readonly _origin: FIELD_ORIGIN; protected readonly _label?: string; + protected readonly _mask?: AggDescriptor['mask']; - constructor({ label, source, origin }: CountAggFieldParams) { + constructor({ label, source, origin, mask }: CountAggFieldParams) { this._source = source; this._origin = origin; this._label = label; + this._mask = mask; } supportsFieldMetaFromEs(): boolean { @@ -131,4 +133,8 @@ export class CountAggField implements IESAggField { pluckRangeFromTileMetaFeature(metaFeature: TileMetaFeature) { return getAggRange(metaFeature, '_count'); } + + getMask() { + return this._mask; + } } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts b/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts index 7e43a2a63658c..9db4e481b9963 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/es_agg_factory.ts @@ -26,6 +26,7 @@ export function esAggFieldsFactory( label: aggDescriptor.label, source, origin, + mask: aggDescriptor.mask, }); } else if (aggDescriptor.type === AGG_TYPE.PERCENTILE) { aggField = new PercentileAggField({ @@ -40,6 +41,7 @@ export function esAggFieldsFactory( : DEFAULT_PERCENTILE, source, origin, + mask: aggDescriptor.mask, }); } else { aggField = new AggField({ @@ -51,6 +53,7 @@ export function esAggFieldsFactory( aggType: aggDescriptor.type, source, origin, + mask: aggDescriptor.mask, }); } diff --git a/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts b/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts index c6a19f448390a..568fad59e058b 100644 --- a/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts +++ b/x-pack/plugins/maps/public/classes/fields/agg/top_term_percentage_field.ts @@ -105,4 +105,8 @@ export class TopTermPercentageField implements IESAggField { pluckRangeFromTileMetaFeature(metaFeature: TileMetaFeature) { return null; } + + getMask() { + return undefined; + } } diff --git a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts index 0421fc3b087b5..69988f0e8a6cb 100644 --- a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Map as MbMap, VectorTileSource } from '@kbn/mapbox-gl'; +import type { FilterSpecification, Map as MbMap, VectorTileSource } from '@kbn/mapbox-gl'; import { AbstractLayer } from '../layer'; import { HeatmapStyle } from '../../styles/heatmap/heatmap_style'; import { LAYER_TYPE } from '../../../../common/constants'; @@ -21,6 +21,7 @@ import { DataRequestContext } from '../../../actions'; import { buildVectorRequestMeta } from '../build_vector_request_meta'; import { IMvtVectorSource } from '../../sources/vector_source'; import { getAggsMeta } from '../../util/tile_meta_feature_utils'; +import { Mask } from '../vector_layer/mask'; export class HeatmapLayer extends AbstractLayer { private readonly _style: HeatmapStyle; @@ -186,6 +187,19 @@ export class HeatmapLayer extends AbstractLayer { this.syncVisibilityWithMb(mbMap, heatmapLayerId); mbMap.setPaintProperty(heatmapLayerId, 'heatmap-opacity', this.getAlpha()); + + // heatmap can implement mask with filter expression because + // feature-state support is not needed since heatmap layers do not support joins + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + const mask = new Mask({ + esAggField: metricField, + isGeometrySourceMvt: true, + ...maskDescriptor, + }); + mbMap.setFilter(heatmapLayerId, mask.getMatchUnmaskedExpression() as FilterSpecification); + } + mbMap.setLayerZoomRange(heatmapLayerId, this.getMinZoom(), this.getMaxZoom()); } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts index ee9fdaf410abb..200c8cad24a4c 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/blended_vector_layer/blended_vector_layer.ts @@ -13,7 +13,6 @@ import { getDefaultDynamicProperties } from '../../../styles/vector/vector_style import { IDynamicStyleProperty } from '../../../styles/vector/properties/dynamic_style_property'; import { IStyleProperty } from '../../../styles/vector/properties/style_property'; import { - COUNT_PROP_LABEL, COUNT_PROP_NAME, GRID_RESOLUTION, LAYER_TYPE, @@ -67,7 +66,6 @@ function getClusterSource(documentSource: IESSource, documentStyle: IVectorStyle clusterSourceDescriptor.metrics = [ { type: AGG_TYPE.COUNT, - label: COUNT_PROP_LABEL, }, ...documentStyle.getDynamicPropertiesArray().map((dynamicProperty) => { return { @@ -267,9 +265,9 @@ export class BlendedVectorLayer extends GeoJsonVectorLayer implements IVectorLay return [clonedDescriptor]; } - getSource(): IVectorSource { + getSource = () => { return this._isClustered ? this._clusterSource : this._documentSource; - } + }; getSourceForEditing() { // Layer is based on this._documentSource diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts new file mode 100644 index 0000000000000..0b1861fd73397 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mask.ts @@ -0,0 +1,193 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { MapGeoJSONFeature } from '@kbn/mapbox-gl'; +import type { IESAggSource } from '../../sources/es_agg_source'; +import type { IESAggField } from '../../fields/agg'; +import { FIELD_ORIGIN, MASK_OPERATOR, MB_LOOKUP_FUNCTION } from '../../../../common/constants'; + +export const BELOW = i18n.translate('xpack.maps.mask.belowLabel', { + defaultMessage: 'below', +}); + +export const ABOVE = i18n.translate('xpack.maps.mask.aboveLabel', { + defaultMessage: 'above', +}); + +export const BUCKETS = i18n.translate('xpack.maps.mask.genericBucketsName', { + defaultMessage: 'buckets', +}); + +const FEATURES = i18n.translate('xpack.maps.mask.genericFeaturesName', { + defaultMessage: 'features', +}); + +const VALUE = i18n.translate('xpack.maps.mask.genericAggLabel', { + defaultMessage: 'value', +}); + +const WHEN = i18n.translate('xpack.maps.mask.when', { + defaultMessage: 'when', +}); + +const WHEN_JOIN_METRIC = i18n.translate('xpack.maps.mask.whenJoinMetric', { + defaultMessage: '{whenLabel} join metric', + values: { + whenLabel: WHEN, + }, +}); + +function getOperatorLabel(operator: MASK_OPERATOR): string { + if (operator === MASK_OPERATOR.BELOW) { + return BELOW; + } + + if (operator === MASK_OPERATOR.ABOVE) { + return ABOVE; + } + + return operator as string; +} + +export function getMaskI18nValue(operator: MASK_OPERATOR, value: number): string { + return `${getOperatorLabel(operator)} ${value}`; +} + +export function getMaskI18nLabel({ + bucketsName, + isJoin, +}: { + bucketsName?: string; + isJoin: boolean; +}): string { + return i18n.translate('xpack.maps.mask.maskLabel', { + defaultMessage: 'Hide {hideNoun}', + values: { + hideNoun: isJoin ? FEATURES : bucketsName ? bucketsName : BUCKETS, + }, + }); +} + +export function getMaskI18nDescription({ + aggLabel, + bucketsName, + isJoin, +}: { + aggLabel?: string; + bucketsName?: string; + isJoin: boolean; +}): string { + return i18n.translate('xpack.maps.mask.maskDescription', { + defaultMessage: '{maskAdverb} {aggLabel} is ', + values: { + aggLabel: aggLabel ? aggLabel : VALUE, + maskAdverb: isJoin ? WHEN_JOIN_METRIC : WHEN, + }, + }); +} + +export class Mask { + private readonly _esAggField: IESAggField; + private readonly _isGeometrySourceMvt: boolean; + private readonly _operator: MASK_OPERATOR; + private readonly _value: number; + + constructor({ + esAggField, + isGeometrySourceMvt, + operator, + value, + }: { + esAggField: IESAggField; + isGeometrySourceMvt: boolean; + operator: MASK_OPERATOR; + value: number; + }) { + this._esAggField = esAggField; + this._isGeometrySourceMvt = isGeometrySourceMvt; + this._operator = operator; + this._value = value; + } + + private _isFeatureState() { + if (this._esAggField.getOrigin() === FIELD_ORIGIN.SOURCE) { + // source fields are stored in properties + return false; + } + + if (!this._isGeometrySourceMvt) { + // For geojson sources, join fields are stored in properties + return false; + } + + // For vector tile sources, it is not possible to add join fields to properties + // so join fields are stored in feature state + return true; + } + + /* + * Returns maplibre expression that matches masked features + */ + getMatchMaskedExpression() { + const comparisionOperator = this._operator === MASK_OPERATOR.BELOW ? '<' : '>'; + const lookup = this._isFeatureState() + ? MB_LOOKUP_FUNCTION.FEATURE_STATE + : MB_LOOKUP_FUNCTION.GET; + return [comparisionOperator, [lookup, this._esAggField.getMbFieldName()], this._value]; + } + + /* + * Returns maplibre expression that matches unmasked features + */ + getMatchUnmaskedExpression() { + const comparisionOperator = this._operator === MASK_OPERATOR.BELOW ? '>=' : '<='; + const lookup = this._isFeatureState() + ? MB_LOOKUP_FUNCTION.FEATURE_STATE + : MB_LOOKUP_FUNCTION.GET; + return [comparisionOperator, [lookup, this._esAggField.getMbFieldName()], this._value]; + } + + getEsAggField() { + return this._esAggField; + } + + getFieldOriginListLabel() { + const source = this._esAggField.getSource(); + const isJoin = this._esAggField.getOrigin() === FIELD_ORIGIN.JOIN; + const maskLabel = getMaskI18nLabel({ + bucketsName: + 'getBucketsName' in (source as IESAggSource) + ? (source as IESAggSource).getBucketsName() + : undefined, + isJoin, + }); + const adverb = isJoin ? WHEN_JOIN_METRIC : WHEN; + + return `${maskLabel} ${adverb}`; + } + + getOperator() { + return this._operator; + } + + getValue() { + return this._value; + } + + isFeatureMasked(feature: MapGeoJSONFeature) { + const featureValue = this._isFeatureState() + ? feature?.state[this._esAggField.getMbFieldName()] + : feature?.properties[this._esAggField.getMbFieldName()]; + if (typeof featureValue !== 'number') { + return false; + } + return this._operator === MASK_OPERATOR.BELOW + ? featureValue < this._value + : featureValue > this._value; + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx index a976837ee2881..82bb15c19ffca 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/mvt_vector_layer/mvt_vector_layer.test.tsx @@ -25,6 +25,7 @@ import { } from '../../../../../common/descriptor_types'; import { LAYER_TYPE, SOURCE_TYPES } from '../../../../../common/constants'; import { MvtVectorLayer } from './mvt_vector_layer'; +import { ITermJoinSource } from '../../../sources/term_join_source'; const defaultConfig = { urlTemplate: 'https://example.com/{x}/{y}/{z}.pbf', @@ -176,6 +177,9 @@ describe('isLayerLoading', () => { getSourceDataRequestId: () => { return 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2'; }, + getRightJoinSource: () => { + return {} as unknown as ITermJoinSource; + }, } as unknown as InnerJoin, ], layerDescriptor: { @@ -212,6 +216,9 @@ describe('isLayerLoading', () => { getSourceDataRequestId: () => { return 'join_source_a0b0da65-5e1a-4967-9dbe-74f24391afe2'; }, + getRightJoinSource: () => { + return {} as unknown as ITermJoinSource; + }, } as unknown as InnerJoin, ], layerDescriptor: { diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index dbee11b617dec..366c9cde6eee6 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -58,12 +58,14 @@ import { ITooltipProperty } from '../../tooltips/tooltip_property'; import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; import { IESSource } from '../../sources/es_source'; import { ITermJoinSource } from '../../sources/term_join_source'; +import type { IESAggSource } from '../../sources/es_agg_source'; import { buildVectorRequestMeta } from '../build_vector_request_meta'; import { getJoinAggKey } from '../../../../common/get_agg_key'; import { syncBoundsData } from './bounds_data'; import { JoinState } from './types'; import { canSkipSourceUpdate } from '../../util/can_skip_fetch'; import { PropertiesMap } from '../../../../common/elasticsearch_util'; +import { Mask } from './mask'; const SUPPORTS_FEATURE_EDITING_REQUEST_ID = 'SUPPORTS_FEATURE_EDITING_REQUEST_ID'; @@ -106,6 +108,7 @@ export interface IVectorLayer extends ILayer { getLeftJoinFields(): Promise; addFeature(geometry: Geometry | Position[]): Promise; deleteFeature(featureId: string): Promise; + getMasks(): Mask[]; } export const noResultsIcon = ; @@ -120,6 +123,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { protected readonly _style: VectorStyle; private readonly _joins: InnerJoin[]; protected readonly _descriptor: VectorLayerDescriptor; + private readonly _masks: Mask[]; static createDescriptor( options: Partial, @@ -163,6 +167,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { customIcons, chartsPaletteServiceGetColor ); + this._masks = this._createMasks(); } async cloneDescriptor(): Promise { @@ -692,6 +697,69 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { return undefined; } + _createMasks() { + const masks: Mask[] = []; + const source = this.getSource(); + if ('getMetricFields' in (source as IESAggSource)) { + const metricFields = (source as IESAggSource).getMetricFields(); + metricFields.forEach((metricField) => { + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + masks.push( + new Mask({ + esAggField: metricField, + isGeometrySourceMvt: source.isMvt(), + ...maskDescriptor, + }) + ); + } + }); + } + + this.getValidJoins().forEach((join) => { + const rightSource = join.getRightJoinSource(); + if ('getMetricFields' in (rightSource as unknown as IESAggSource)) { + const metricFields = (rightSource as unknown as IESAggSource).getMetricFields(); + metricFields.forEach((metricField) => { + const maskDescriptor = metricField.getMask(); + if (maskDescriptor) { + masks.push( + new Mask({ + esAggField: metricField, + isGeometrySourceMvt: source.isMvt(), + ...maskDescriptor, + }) + ); + } + }); + } + }); + + return masks; + } + + getMasks() { + return this._masks; + } + + // feature-state is not supported in filter expressions + // https://github.com/mapbox/mapbox-gl-js/issues/8487 + // therefore, masking must be accomplished via setting opacity paint property (hack) + _getAlphaExpression() { + const maskCaseExpressions: unknown[] = []; + this.getMasks().forEach((mask) => { + // case expressions require 2 parts + // 1) condition expression + maskCaseExpressions.push(mask.getMatchMaskedExpression()); + // 2) output. 0 opacity styling "hides" feature + maskCaseExpressions.push(0); + }); + + return maskCaseExpressions.length + ? ['case', ...maskCaseExpressions, this.getAlpha()] + : this.getAlpha(); + } + _setMbPointsProperties( mbMap: MbMap, mvtSourceLayer?: string, @@ -759,13 +827,13 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { if (this.getCurrentStyle().arePointsSymbolizedAsCircles()) { this.getCurrentStyle().setMBPaintPropertiesForPoints({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, pointLayerId: markerLayerId, }); } else { this.getCurrentStyle().setMBSymbolPropertiesForPoints({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, symbolLayerId: markerLayerId, }); @@ -811,7 +879,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { } this.getCurrentStyle().setMBPaintProperties({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, fillLayerId, lineLayerId, @@ -865,7 +933,7 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { } this.getCurrentStyle().setMBPropertiesForLabelText({ - alpha: this.getAlpha(), + alpha: this._getAlphaExpression(), mbMap, textLayerId: labelLayerId, }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts index fa7f329beb97a..dda3026ddd4ef 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts @@ -11,11 +11,13 @@ import { DataView } from '@kbn/data-plugin/common'; import type { IESAggSource } from './types'; import { AbstractESSource } from '../es_source'; import { esAggFieldsFactory, IESAggField } from '../../fields/agg'; -import { AGG_TYPE, COUNT_PROP_LABEL, FIELD_ORIGIN } from '../../../../common/constants'; +import { AGG_TYPE, FIELD_ORIGIN } from '../../../../common/constants'; import { getSourceAggKey } from '../../../../common/get_agg_key'; import { AbstractESAggSourceDescriptor, AggDescriptor } from '../../../../common/descriptor_types'; import { IField } from '../../fields/field'; import { ITooltipProperty } from '../../tooltips/tooltip_property'; +import { getAggDisplayName } from './get_agg_display_name'; +import { BUCKETS } from '../../layers/vector_layer/mask'; export const DEFAULT_METRIC = { type: AGG_TYPE.COUNT }; @@ -46,6 +48,10 @@ export abstract class AbstractESAggSource extends AbstractESSource implements IE } } + getBucketsName() { + return BUCKETS; + } + getFieldByName(fieldName: string): IField | null { return this.getMetricFieldForName(fieldName); } @@ -83,14 +89,14 @@ export abstract class AbstractESAggSource extends AbstractESSource implements IE async getAggLabel(aggType: AGG_TYPE, fieldLabel: string): Promise { switch (aggType) { case AGG_TYPE.COUNT: - return COUNT_PROP_LABEL; + return getAggDisplayName(aggType); case AGG_TYPE.TERMS: return i18n.translate('xpack.maps.source.esAggSource.topTermLabel', { - defaultMessage: `Top {fieldLabel}`, + defaultMessage: `top {fieldLabel}`, values: { fieldLabel }, }); default: - return `${aggType} ${fieldLabel}`; + return `${getAggDisplayName(aggType)} ${fieldLabel}`; } } diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts new file mode 100644 index 0000000000000..516f6448fb629 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/get_agg_display_name.ts @@ -0,0 +1,48 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { AGG_TYPE } from '../../../../common/constants'; + +export function getAggDisplayName(aggType: AGG_TYPE): string { + switch (aggType) { + case AGG_TYPE.AVG: + return i18n.translate('xpack.maps.aggType.averageLabel', { + defaultMessage: 'average', + }); + case AGG_TYPE.COUNT: + return i18n.translate('xpack.maps.aggType.countLabel', { + defaultMessage: 'count', + }); + case AGG_TYPE.MAX: + return i18n.translate('xpack.maps.aggType.maximumLabel', { + defaultMessage: 'max', + }); + case AGG_TYPE.MIN: + return i18n.translate('xpack.maps.aggType.minimumLabel', { + defaultMessage: 'min', + }); + case AGG_TYPE.PERCENTILE: + return i18n.translate('xpack.maps.aggType.percentileLabel', { + defaultMessage: 'percentile', + }); + case AGG_TYPE.SUM: + return i18n.translate('xpack.maps.aggType.sumLabel', { + defaultMessage: 'sum', + }); + case AGG_TYPE.TERMS: + return i18n.translate('xpack.maps.aggType.topTermLabel', { + defaultMessage: 'top term', + }); + case AGG_TYPE.UNIQUE_COUNT: + return i18n.translate('xpack.maps.aggType.cardinalityTermLabel', { + defaultMessage: 'unique count', + }); + default: + return aggType; + } +} diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts index 033d937aa9391..80bc74bd4ceb4 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/index.ts @@ -7,3 +7,4 @@ export type { IESAggSource } from './types'; export { AbstractESAggSource, DEFAULT_METRIC } from './es_agg_source'; +export { getAggDisplayName } from './get_agg_display_name'; diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts index d9cb6fcd95a10..697ae8a1a606d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/types.ts @@ -13,6 +13,12 @@ import { IESAggField } from '../../fields/agg'; export interface IESAggSource extends IESSource { getAggKey(aggType: AGG_TYPE, fieldName: string): string; getAggLabel(aggType: AGG_TYPE, fieldLabel: string): Promise; + + /* + * Returns human readable name describing buckets, like "clusters" or "grids" + */ + getBucketsName(): string; + getMetricFields(): IESAggField[]; getMetricFieldForName(fieldName: string): IESAggField | null; getValueAggsDsl(indexPattern: DataView): { [key: string]: unknown }; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap index 79b8c4ffc9808..fe50d54ca5535 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/__snapshots__/update_source_editor.test.tsx.snap @@ -19,7 +19,9 @@ exports[`source editor geo_grid_source should not allow editing multiple metrics /> { async function onChange(...sourceChanges: OnSourceChangeArgs[]) { sourceEditorArgs.onChange(...sourceChanges); @@ -129,6 +147,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements IMvtVectorSo } return ( ({ })); const defaultProps = { + bucketsName: 'clusters', currentLayerType: LAYER_TYPE.GEOJSON_VECTOR, geoFieldName: 'myLocation', indexPatternId: 'foobar', diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx index 3268992c7f2b6..d69a97d09dd47 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/update_source_editor.tsx @@ -6,7 +6,6 @@ */ import React, { Fragment, Component } from 'react'; - import { v4 as uuidv4 } from 'uuid'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiPanel, EuiSpacer, EuiComboBoxOptionOption, EuiTitle } from '@elastic/eui'; @@ -25,6 +24,7 @@ import { clustersTitle, heatmapTitle } from './es_geo_grid_source'; import { isMvt } from './is_mvt'; interface Props { + bucketsName: string; currentLayerType?: string; geoFieldName: string; indexPatternId: string; @@ -148,6 +148,8 @@ export class UpdateSourceEditor extends Component { { ); } diff --git a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx similarity index 74% rename from x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js rename to x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx index 856504c51865e..c36cc3d49089a 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_pew_pew_source/update_source_editor.tsx @@ -6,17 +6,31 @@ */ import React, { Component, Fragment } from 'react'; - -import { getDataViewNotFoundMessage } from '../../../../common/i18n_getters'; -import { MetricsEditor } from '../../../components/metrics_editor'; -import { getIndexPatternService } from '../../../kibana_services'; +import type { DataViewField } from '@kbn/data-plugin/common'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { indexPatterns } from '@kbn/data-plugin/public'; +import { MetricsEditor } from '../../../components/metrics_editor'; +import { getIndexPatternService } from '../../../kibana_services'; +import type { AggDescriptor } from '../../../../common/descriptor_types'; +import type { OnSourceChangeArgs } from '../source'; + +interface Props { + bucketsName: string; + indexPatternId: string; + metrics: AggDescriptor[]; + onChange: (...args: OnSourceChangeArgs[]) => void; +} + +interface State { + fields: DataViewField[]; +} + +export class UpdateSourceEditor extends Component { + private _isMounted: boolean = false; -export class UpdateSourceEditor extends Component { state = { - fields: null, + fields: [], }; componentDidMount() { @@ -33,11 +47,6 @@ export class UpdateSourceEditor extends Component { try { indexPattern = await getIndexPatternService().get(this.props.indexPatternId); } catch (err) { - if (this._isMounted) { - this.setState({ - loadError: getDataViewNotFoundMessage(this.props.indexPatternId), - }); - } return; } @@ -50,7 +59,7 @@ export class UpdateSourceEditor extends Component { }); } - _onMetricsChange = (metrics) => { + _onMetricsChange = (metrics: AggDescriptor[]) => { this.props.onChange({ propName: 'metrics', value: metrics }); }; @@ -69,6 +78,8 @@ export class UpdateSourceEditor extends Component { { }); const metrics = source.getMetricFields(); expect(metrics[0].getName()).toEqual('__kbnjoin__count__1234'); - expect(await metrics[0].getLabel()).toEqual('Count of foobar'); + expect(await metrics[0].getLabel()).toEqual('count of foobar'); }); it('should override name and label of sum metric', async () => { @@ -51,7 +51,7 @@ describe('getMetricFields', () => { expect(metrics[0].getName()).toEqual('__kbnjoin__sum_of_myFieldGettingSummed__1234'); expect(await metrics[0].getLabel()).toEqual('my custom label'); expect(metrics[1].getName()).toEqual('__kbnjoin__count__1234'); - expect(await metrics[1].getLabel()).toEqual('Count of foobar'); + expect(await metrics[1].getLabel()).toEqual('count of foobar'); }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts index 5540454702114..4c7793e1b01cb 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.ts @@ -31,6 +31,7 @@ import { import { PropertiesMap } from '../../../../common/elasticsearch_util'; import { isValidStringConfig } from '../../util/valid_string_config'; import { ITermJoinSource } from '../term_join_source'; +import type { IESAggSource } from '../es_agg_source'; import { IField } from '../../fields/field'; import { mergeExecutionContext } from '../execution_context_utils'; @@ -52,7 +53,7 @@ export function extractPropertiesMap(rawEsData: any, countPropertyName: string): return propertiesMap; } -export class ESTermSource extends AbstractESAggSource implements ITermJoinSource { +export class ESTermSource extends AbstractESAggSource implements ITermJoinSource, IESAggSource { static type = SOURCE_TYPES.ES_TERM_SOURCE; static createDescriptor(descriptor: Partial): ESTermSourceDescriptor { @@ -115,7 +116,7 @@ export class ESTermSource extends AbstractESAggSource implements ITermJoinSource } return aggType === AGG_TYPE.COUNT ? i18n.translate('xpack.maps.source.esJoin.countLabel', { - defaultMessage: `Count of {indexPatternLabel}`, + defaultMessage: `count of {indexPatternLabel}`, values: { indexPatternLabel }, }) : super.getAggLabel(aggType, fieldLabel); diff --git a/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx b/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx index 390e48408d747..027e4dc29c58c 100644 --- a/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx +++ b/x-pack/plugins/maps/public/classes/styles/heatmap/components/legend/heatmap_legend.tsx @@ -5,13 +5,15 @@ * 2.0. */ -import React, { Component } from 'react'; +import React, { Component, ReactNode } from 'react'; import { i18n } from '@kbn/i18n'; import { ColorGradient } from './color_gradient'; import { RangedStyleLegendRow } from '../../../components/ranged_style_legend_row'; import { HEATMAP_COLOR_RAMP_LABEL } from '../heatmap_constants'; -import { IField } from '../../../../fields/field'; +import type { IField } from '../../../../fields/field'; +import type { IESAggField } from '../../../../fields/agg'; +import { MaskLegend } from '../../../vector/components/legend/mask_legend'; interface Props { colorRampName: string; @@ -47,7 +49,7 @@ export class HeatmapLegend extends Component { } render() { - return ( + const metricLegend = ( } minLabel={i18n.translate('xpack.maps.heatmapLegend.coldLabel', { @@ -61,5 +63,28 @@ export class HeatmapLegend extends Component { invert={false} /> ); + + let maskLegend: ReactNode | undefined; + if ('getMask' in (this.props.field as IESAggField)) { + const mask = (this.props.field as IESAggField).getMask(); + if (mask) { + maskLegend = ( + + ); + } + } + + return maskLegend ? ( + <> + {maskLegend} + {metricLegend} + + ) : ( + metricLegend + ); } } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx new file mode 100644 index 0000000000000..4ffb7ba5a1834 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/mask_legend.tsx @@ -0,0 +1,95 @@ +/* + * 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 React, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiText } from '@elastic/eui'; +import { FIELD_ORIGIN, MASK_OPERATOR } from '../../../../../../common/constants'; +import type { IESAggField } from '../../../../fields/agg'; +import type { IESAggSource } from '../../../../sources/es_agg_source'; +import { + getMaskI18nDescription, + getMaskI18nLabel, + getMaskI18nValue, +} from '../../../../layers/vector_layer/mask'; + +interface Props { + esAggField: IESAggField; + onlyShowLabelAndValue?: boolean; + operator: MASK_OPERATOR; + value: number; +} + +interface State { + aggLabel?: string; +} + +export class MaskLegend extends Component { + private _isMounted = false; + + state: State = {}; + + componentDidMount() { + this._isMounted = true; + this._loadAggLabel(); + } + + componentWillUnmount() { + this._isMounted = false; + } + + componentDidUpdate() { + this._loadAggLabel(); + } + + _loadAggLabel = async () => { + const aggLabel = await this.props.esAggField.getLabel(); + if (this._isMounted && aggLabel !== this.state.aggLabel) { + this.setState({ aggLabel }); + } + }; + + _getBucketsName() { + const source = this.props.esAggField.getSource(); + return 'getBucketsName' in (source as IESAggSource) + ? (source as IESAggSource).getBucketsName() + : undefined; + } + + _getPrefix() { + if (this.props.onlyShowLabelAndValue) { + return i18n.translate('xpack.maps.maskLegend.is', { + defaultMessage: '{aggLabel} is', + values: { + aggLabel: this.state.aggLabel, + }, + }); + } + + const isJoin = this.props.esAggField.getOrigin() === FIELD_ORIGIN.JOIN; + const maskLabel = getMaskI18nLabel({ + bucketsName: this._getBucketsName(), + isJoin, + }); + const maskDescription = getMaskI18nDescription({ + aggLabel: this.state.aggLabel, + isJoin, + }); + return `${maskLabel} ${maskDescription}`; + } + + render() { + return ( + + + {`${this._getPrefix()} `} + {getMaskI18nValue(this.props.operator, this.props.value)} + + + ); + } +} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx index 2d282a4b530cb..60bcd05c9f738 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/legend/vector_style_legend.tsx @@ -6,17 +6,30 @@ */ import React from 'react'; +import { EuiText } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { FIELD_ORIGIN } from '../../../../../../common/constants'; +import { Mask } from '../../../../layers/vector_layer/mask'; import { IStyleProperty } from '../../properties/style_property'; +import { MaskLegend } from './mask_legend'; interface Props { isLinesOnly: boolean; isPointsOnly: boolean; + masks: Mask[]; styles: Array>; symbolId?: string; svg?: string; } -export function VectorStyleLegend({ isLinesOnly, isPointsOnly, styles, symbolId, svg }: Props) { +export function VectorStyleLegend({ + isLinesOnly, + isPointsOnly, + masks, + styles, + symbolId, + svg, +}: Props) { const legendRows = []; for (let i = 0; i < styles.length; i++) { @@ -34,5 +47,55 @@ export function VectorStyleLegend({ isLinesOnly, isPointsOnly, styles, symbolId, ); } - return <>{legendRows}; + function renderMasksByFieldOrigin(fieldOrigin: FIELD_ORIGIN) { + const masksByFieldOrigin = masks.filter( + (mask) => mask.getEsAggField().getOrigin() === fieldOrigin + ); + if (masksByFieldOrigin.length === 0) { + return null; + } + + if (masksByFieldOrigin.length === 1) { + const mask = masksByFieldOrigin[0]; + return ( + + ); + } + + return ( + <> + + {masksByFieldOrigin[0].getFieldOriginListLabel()} + +
    + {masksByFieldOrigin.map((mask) => ( +
  • + +
  • + ))} +
+ + ); + } + + return ( + <> + {renderMasksByFieldOrigin(FIELD_ORIGIN.SOURCE)} + {renderMasksByFieldOrigin(FIELD_ORIGIN.JOIN)} + {legendRows} + + ); } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx index 6daa8cf84afaa..f1a55b571e27d 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx @@ -51,7 +51,7 @@ export class DynamicColorProperty extends DynamicStyleProperty { - syncCircleColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncCircleColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'circle-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'circle-opacity', alpha); } - syncFillColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncFillColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'fill-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'fill-opacity', alpha); } @@ -28,17 +28,17 @@ export class StaticColorProperty extends StaticStyleProperty mbMap.setPaintProperty(mbLayerId, 'icon-halo-color', this._options.color); } - syncLineColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncLineColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'line-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'line-opacity', alpha); } - syncCircleStrokeWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncCircleStrokeWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'circle-stroke-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'circle-stroke-opacity', alpha); } - syncLabelColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: number) { + syncLabelColorWithMb(mbLayerId: string, mbMap: MbMap, alpha: unknown) { mbMap.setPaintProperty(mbLayerId, 'text-color', this._options.color); mbMap.setPaintProperty(mbLayerId, 'text-opacity', alpha); } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx index 3b64d0960628c..31c06728d8112 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx @@ -130,7 +130,7 @@ export interface IVectorStyle extends IStyle { fillLayerId, lineLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; fillLayerId: string; lineLayerId: string; @@ -140,7 +140,7 @@ export interface IVectorStyle extends IStyle { mbMap, pointLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; pointLayerId: string; }) => void; @@ -149,7 +149,7 @@ export interface IVectorStyle extends IStyle { mbMap, textLayerId, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; textLayerId: string; }) => void; @@ -158,7 +158,7 @@ export interface IVectorStyle extends IStyle { symbolLayerId, alpha, }: { - alpha: number; + alpha: unknown; mbMap: MbMap; symbolLayerId: string; }) => void; @@ -730,6 +730,7 @@ export class VectorStyle implements IVectorStyle { return ( void; + onClose: () => void; +} + +interface State { + operator: MASK_OPERATOR; + value: number | string; +} + +export class MaskEditor extends Component { + constructor(props: Props) { + super(props); + this.state = { + operator: + this.props.metric.mask !== undefined + ? this.props.metric.mask.operator + : MASK_OPERATOR.BELOW, + value: this.props.metric.mask !== undefined ? this.props.metric.mask.value : '', + }; + } + + _onSet = () => { + if (this._isValueInValid()) { + return; + } + + this.props.onChange({ + ...this.props.metric, + mask: { + operator: this.state.operator, + value: this.state.value as number, + }, + }); + this.props.onClose(); + }; + + _onClear = () => { + const newMetric = { + ...this.props.metric, + }; + delete newMetric.mask; + this.props.onChange(newMetric); + this.props.onClose(); + }; + + _onOperatorChange = (e: ChangeEvent) => { + this.setState({ + operator: e.target.value as MASK_OPERATOR, + }); + }; + + _onValueChange = (evt: ChangeEvent) => { + const sanitizedValue = parseFloat(evt.target.value); + this.setState({ + value: isNaN(sanitizedValue) ? evt.target.value : sanitizedValue, + }); + }; + + _hasChanges() { + return ( + this.props.metric.mask === undefined || + this.props.metric.mask.operator !== this.state.operator || + this.props.metric.mask.value !== this.state.value + ); + } + + _isValueInValid() { + return typeof this.state.value === 'string'; + } + + _renderForm() { + return ( + + + + + + + + + + + + + ); + } + + _renderFooter() { + return ( + + + + + {panelStrings.close} + + + + + {panelStrings.clear} + + + + + {panelStrings.apply} + + + + + ); + } + + render() { + return ( + <> + {this._renderForm()} + + {this._renderFooter()} + + ); + } +} diff --git a/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx b/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx new file mode 100644 index 0000000000000..e05fdc6cdf2d5 --- /dev/null +++ b/x-pack/plugins/maps/public/components/metrics_editor/mask_expression/mask_expression.tsx @@ -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 React, { Component } from 'react'; +import { EuiExpression, EuiPopover } from '@elastic/eui'; +import { DataViewField } from '@kbn/data-views-plugin/public'; +import { AGG_TYPE } from '../../../../common/constants'; +import { AggDescriptor, FieldedAggDescriptor } from '../../../../common/descriptor_types'; +import { MaskEditor } from './mask_editor'; +import { getAggDisplayName } from '../../../classes/sources/es_agg_source'; +import { + getMaskI18nDescription, + getMaskI18nValue, +} from '../../../classes/layers/vector_layer/mask'; + +interface Props { + fields: DataViewField[]; + isJoin: boolean; + metric: AggDescriptor; + onChange: (metric: AggDescriptor) => void; +} + +interface State { + isPopoverOpen: boolean; +} + +export class MaskExpression extends Component { + state: State = { + isPopoverOpen: false, + }; + + _togglePopover = () => { + this.setState((prevState) => ({ + isPopoverOpen: !prevState.isPopoverOpen, + })); + }; + + _closePopover = () => { + this.setState({ + isPopoverOpen: false, + }); + }; + + _getMaskExpressionValue() { + return this.props.metric.mask === undefined + ? '...' + : getMaskI18nValue(this.props.metric.mask.operator, this.props.metric.mask.value); + } + + _getAggLabel() { + const aggDisplayName = getAggDisplayName(this.props.metric.type); + if (this.props.metric.type === AGG_TYPE.COUNT || this.props.metric.field === undefined) { + return aggDisplayName; + } + + const targetField = this.props.fields.find( + (field) => field.name === (this.props.metric as FieldedAggDescriptor).field + ); + const fieldDisplayName = targetField?.displayName + ? targetField?.displayName + : this.props.metric.field; + return `${aggDisplayName} ${fieldDisplayName}`; + } + + render() { + // masks only supported for numerical metrics + if (this.props.metric.type === AGG_TYPE.TERMS) { + return null; + } + + return ( + + } + isOpen={this.state.isPopoverOpen} + closePopover={this._closePopover} + panelPaddingSize="s" + anchorPosition="downCenter" + repositionOnScroll={true} + > + + + ); + } +} diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx index 26cf6d5313821..5042cb4ffa9c7 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metric_editor.tsx @@ -18,6 +18,8 @@ import { AggDescriptor } from '../../../common/descriptor_types'; import { AGG_TYPE, DEFAULT_PERCENTILE } from '../../../common/constants'; import { getTermsFields } from '../../index_pattern_util'; import { ValidatedNumberInput } from '../validated_number_input'; +import { getMaskI18nLabel } from '../../classes/layers/vector_layer/mask'; +import { MaskExpression } from './mask_expression'; function filterFieldsForAgg(fields: DataViewField[], aggType: AGG_TYPE) { if (!fields) { @@ -43,6 +45,8 @@ function filterFieldsForAgg(fields: DataViewField[], aggType: AGG_TYPE) { } interface Props { + bucketsName?: string; + isJoin: boolean; metric: AggDescriptor; fields: DataViewField[]; onChange: (metric: AggDescriptor) => void; @@ -52,7 +56,9 @@ interface Props { } export function MetricEditor({ + bucketsName, fields, + isJoin, metricsFilter, metric, onChange, @@ -64,6 +70,8 @@ export function MetricEditor({ return; } + // Intentionally not adding mask. + // Changing aggregation likely changes value range so keeping old mask does not seem relevent const descriptor = { type: metricAggregationType, label: metric.label, @@ -93,6 +101,8 @@ export function MetricEditor({ if (!fieldName || metric.type === AGG_TYPE.COUNT) { return; } + // Intentionally not adding mask. + // Changing field likely changes value range so keeping old mask does not seem relevent onChange({ label: metric.label, type: metric.type, @@ -223,6 +233,11 @@ export function MetricEditor({ {fieldSelect} {percentileSelect} {labelInput} + + + + + {removeButton} ); diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx index e8dd63fe934e5..88c9b83bc54a4 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metric_select.tsx @@ -9,54 +9,39 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiComboBox, EuiComboBoxOptionOption, EuiComboBoxProps } from '@elastic/eui'; import { AGG_TYPE } from '../../../common/constants'; +import { getAggDisplayName } from '../../classes/sources/es_agg_source'; const AGG_OPTIONS = [ { - label: i18n.translate('xpack.maps.metricSelect.averageDropDownOptionLabel', { - defaultMessage: 'Average', - }), + label: getAggDisplayName(AGG_TYPE.AVG), value: AGG_TYPE.AVG, }, { - label: i18n.translate('xpack.maps.metricSelect.countDropDownOptionLabel', { - defaultMessage: 'Count', - }), + label: getAggDisplayName(AGG_TYPE.COUNT), value: AGG_TYPE.COUNT, }, { - label: i18n.translate('xpack.maps.metricSelect.maxDropDownOptionLabel', { - defaultMessage: 'Max', - }), + label: getAggDisplayName(AGG_TYPE.MAX), value: AGG_TYPE.MAX, }, { - label: i18n.translate('xpack.maps.metricSelect.minDropDownOptionLabel', { - defaultMessage: 'Min', - }), + label: getAggDisplayName(AGG_TYPE.MIN), value: AGG_TYPE.MIN, }, { - label: i18n.translate('xpack.maps.metricSelect.percentileDropDownOptionLabel', { - defaultMessage: 'Percentile', - }), + label: getAggDisplayName(AGG_TYPE.PERCENTILE), value: AGG_TYPE.PERCENTILE, }, { - label: i18n.translate('xpack.maps.metricSelect.sumDropDownOptionLabel', { - defaultMessage: 'Sum', - }), + label: getAggDisplayName(AGG_TYPE.SUM), value: AGG_TYPE.SUM, }, { - label: i18n.translate('xpack.maps.metricSelect.termsDropDownOptionLabel', { - defaultMessage: 'Top term', - }), + label: getAggDisplayName(AGG_TYPE.TERMS), value: AGG_TYPE.TERMS, }, { - label: i18n.translate('xpack.maps.metricSelect.cardinalityDropDownOptionLabel', { - defaultMessage: 'Unique count', - }), + label: getAggDisplayName(AGG_TYPE.UNIQUE_COUNT), value: AGG_TYPE.UNIQUE_COUNT, }, ]; diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx index 66fed40936b79..5aaf1369efe81 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.test.tsx @@ -20,6 +20,7 @@ const defaultProps = { fields: [], onChange: () => {}, allowMultipleMetrics: true, + isJoin: false, }; test('should render metrics editor', () => { diff --git a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx index b38e20b40d990..a18608b9631c2 100644 --- a/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx +++ b/x-pack/plugins/maps/public/components/metrics_editor/metrics_editor.tsx @@ -22,6 +22,8 @@ export function isMetricValid(aggDescriptor: AggDescriptor) { interface Props { allowMultipleMetrics: boolean; + bucketsName?: string; + isJoin: boolean; metrics: AggDescriptor[]; fields: DataViewField[]; onChange: (metrics: AggDescriptor[]) => void; @@ -81,6 +83,8 @@ export class MetricsEditor extends Component { return (
{ metrics={this.props.metrics} onChange={this.props.onChange} allowMultipleMetrics={true} + isJoin={true} /> ); }; diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap index cb496311b3d1c..46ddc7f58eb78 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/__snapshots__/attribution_form_row.test.tsx.snap @@ -76,11 +76,7 @@ exports[`Should render edit form row when attribution not provided 1`] = ` onClick={[Function]} size="xs" > - + Clear
diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx index bdab63e1029e7..38ac195904592 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_form_row.tsx @@ -8,10 +8,10 @@ import React from 'react'; import { EuiButtonEmpty, EuiLink, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import { Attribution } from '../../../../common/descriptor_types'; import { ILayer } from '../../../classes/layers/layer'; import { AttributionPopover } from './attribution_popover'; +import { panelStrings } from '../../panel_strings'; interface Props { layer: ILayer; @@ -65,9 +65,7 @@ export function AttributionFormRow(props: Props) { defaultMessage: 'Edit attribution', } )} - popoverButtonLabel={i18n.translate('xpack.maps.attribution.editBtnLabel', { - defaultMessage: 'Edit', - })} + popoverButtonLabel={panelStrings.edit} label={layerDescriptor.attribution.label} url={layerDescriptor.attribution.url} /> @@ -83,10 +81,7 @@ export function AttributionFormRow(props: Props) { defaultMessage: 'Clear attribution', })} > - + {panelStrings.clear}
diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx index 0371b68c85a3b..530b3cce20f00 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/layer_settings/attribution_popover.tsx @@ -20,6 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { Attribution } from '../../../../common/descriptor_types'; +import { panelStrings } from '../../panel_strings'; interface Props { onChange: (attribution: Attribution) => void; @@ -128,7 +129,7 @@ export class AttributionPopover extends Component { onClick={this._onApply} size="s" > - + {panelStrings.apply} diff --git a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx index 8d399f19a765c..02b9048e93b86 100644 --- a/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx +++ b/x-pack/plugins/maps/public/connected_components/edit_layer_panel/style_settings/style_settings.tsx @@ -35,7 +35,7 @@ export function StyleSettings({ layer, updateStyleDescriptor, updateCustomIcons
diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx index edd27d2d1edb1..9074fbd8fbeaf 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.test.tsx @@ -70,6 +70,9 @@ const mockLayer = { }, } as unknown as IVectorSource; }, + getMasks: () => { + return []; + }, } as unknown as IVectorLayer; const mockMbMapHandlers: { [key: string]: (event?: MapMouseEvent) => void } = {}; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx index 1c14d11eb1f96..75e41464fd0f8 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/tooltip_control/tooltip_control.tsx @@ -184,6 +184,15 @@ export class TooltipControl extends Component { continue; } + // masking must use paint property "opacity" to hide features in order to support feature state + // therefore, there is no way to remove masked features with queryRenderedFeatures + // masked features must be removed via manual filtering + const masks = layer.getMasks(); + const maskHiddingFeature = masks.find((mask) => mask.isFeatureMasked(mbFeature)); + if (maskHiddingFeature) { + continue; + } + const featureId = layer.getFeatureId(mbFeature); if (featureId === undefined) { continue; diff --git a/x-pack/plugins/maps/public/connected_components/panel_strings.ts b/x-pack/plugins/maps/public/connected_components/panel_strings.ts index f7f7278138e1e..f4eb5e871a3fe 100644 --- a/x-pack/plugins/maps/public/connected_components/panel_strings.ts +++ b/x-pack/plugins/maps/public/connected_components/panel_strings.ts @@ -8,12 +8,21 @@ import { i18n } from '@kbn/i18n'; export const panelStrings = { + apply: i18n.translate('xpack.maps.panel.applyLabel', { + defaultMessage: 'Apply', + }), + clear: i18n.translate('xpack.maps.panel.clearLabel', { + defaultMessage: 'Clear', + }), close: i18n.translate('xpack.maps.panel.closeLabel', { defaultMessage: 'Close', }), discardChanges: i18n.translate('xpack.maps.panel.discardChangesLabel', { defaultMessage: 'Discard changes', }), + edit: i18n.translate('xpack.maps.panel.editLabel', { + defaultMessage: 'Edit', + }), keepChanges: i18n.translate('xpack.maps.panel.keepChangesLabel', { defaultMessage: 'Keep changes', }), diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 58305363bb55e..854795940bbbf 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -20315,15 +20315,11 @@ "xpack.maps.addLayerPanel.addLayer": "Ajouter un calque", "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "Changer de calque", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "Annuler", - "xpack.maps.aggs.defaultCountLabel": "compte", "xpack.maps.attribution.addBtnAriaLabel": "Ajouter une attribution", "xpack.maps.attribution.addBtnLabel": "Ajouter une attribution", - "xpack.maps.attribution.applyBtnLabel": "Appliquer", "xpack.maps.attribution.attributionFormLabel": "Attribution", "xpack.maps.attribution.clearBtnAriaLabel": "Effacer l'attribution", - "xpack.maps.attribution.clearBtnLabel": "Effacer", "xpack.maps.attribution.editBtnAriaLabel": "Modifier l'attribution", - "xpack.maps.attribution.editBtnLabel": "Modifier", "xpack.maps.attribution.labelFieldLabel": "Étiquette", "xpack.maps.attribution.urlLabel": "Lien", "xpack.maps.badge.readOnly.text": "Lecture seule", @@ -20595,15 +20591,7 @@ "xpack.maps.metricsEditor.selectFieldLabel": "Champ", "xpack.maps.metricsEditor.selectFieldPlaceholder": "Sélectionner un champ", "xpack.maps.metricsEditor.selectPercentileLabel": "Centile", - "xpack.maps.metricSelect.averageDropDownOptionLabel": "Moyenne", - "xpack.maps.metricSelect.cardinalityDropDownOptionLabel": "Compte unique", - "xpack.maps.metricSelect.countDropDownOptionLabel": "Décompte", - "xpack.maps.metricSelect.maxDropDownOptionLabel": "Max.", - "xpack.maps.metricSelect.minDropDownOptionLabel": "Min.", - "xpack.maps.metricSelect.percentileDropDownOptionLabel": "Centile", "xpack.maps.metricSelect.selectAggregationPlaceholder": "Sélectionner une agrégation", - "xpack.maps.metricSelect.sumDropDownOptionLabel": "Somme", - "xpack.maps.metricSelect.termsDropDownOptionLabel": "Premier terme", "xpack.maps.mvtSource.addFieldLabel": "Ajouter", "xpack.maps.mvtSource.fieldPlaceholderText": "Nom du champ", "xpack.maps.mvtSource.numberFieldLabel": "numéro", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a17220cadd2d2..23845c1923024 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20315,15 +20315,11 @@ "xpack.maps.addLayerPanel.addLayer": "レイヤーを追加", "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "レイヤーを変更", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "キャンセル", - "xpack.maps.aggs.defaultCountLabel": "カウント", "xpack.maps.attribution.addBtnAriaLabel": "属性を追加", "xpack.maps.attribution.addBtnLabel": "属性を追加", - "xpack.maps.attribution.applyBtnLabel": "適用", "xpack.maps.attribution.attributionFormLabel": "属性", "xpack.maps.attribution.clearBtnAriaLabel": "属性を消去", - "xpack.maps.attribution.clearBtnLabel": "クリア", "xpack.maps.attribution.editBtnAriaLabel": "属性を編集", - "xpack.maps.attribution.editBtnLabel": "編集", "xpack.maps.attribution.labelFieldLabel": "ラベル", "xpack.maps.attribution.urlLabel": "リンク", "xpack.maps.badge.readOnly.text": "読み取り専用", @@ -20595,15 +20591,7 @@ "xpack.maps.metricsEditor.selectFieldLabel": "フィールド", "xpack.maps.metricsEditor.selectFieldPlaceholder": "フィールドを選択", "xpack.maps.metricsEditor.selectPercentileLabel": "パーセンタイル", - "xpack.maps.metricSelect.averageDropDownOptionLabel": "平均", - "xpack.maps.metricSelect.cardinalityDropDownOptionLabel": "ユニークカウント", - "xpack.maps.metricSelect.countDropDownOptionLabel": "カウント", - "xpack.maps.metricSelect.maxDropDownOptionLabel": "最高", - "xpack.maps.metricSelect.minDropDownOptionLabel": "最低", - "xpack.maps.metricSelect.percentileDropDownOptionLabel": "パーセンタイル", "xpack.maps.metricSelect.selectAggregationPlaceholder": "集約を選択", - "xpack.maps.metricSelect.sumDropDownOptionLabel": "合計", - "xpack.maps.metricSelect.termsDropDownOptionLabel": "トップ用語", "xpack.maps.mvtSource.addFieldLabel": "追加", "xpack.maps.mvtSource.fieldPlaceholderText": "フィールド名", "xpack.maps.mvtSource.numberFieldLabel": "数字", @@ -23391,7 +23379,7 @@ "xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "フィルターリストを表示するパーミッションがありません", "xpack.ml.ruleEditor.scopeSection.scopeTitle": "範囲", "xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "ルールを作成", - "xpack.ml.ruleEditor.selectRuleAction.orText": "OR ", + "xpack.ml.ruleEditor.selectRuleAction.orText": "OR ", "xpack.ml.ruleEditor.typicalAppliesTypeText": "通常", "xpack.ml.sampleDataLinkLabel": "ML ジョブ", "xpack.ml.selectDataViewLabel": "データビューを選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 057233f627ace..bd291abcea65b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20315,15 +20315,11 @@ "xpack.maps.addLayerPanel.addLayer": "添加图层", "xpack.maps.addLayerPanel.changeDataSourceButtonLabel": "更改图层", "xpack.maps.addLayerPanel.footer.cancelButtonLabel": "取消", - "xpack.maps.aggs.defaultCountLabel": "计数", "xpack.maps.attribution.addBtnAriaLabel": "添加归因", "xpack.maps.attribution.addBtnLabel": "添加归因", - "xpack.maps.attribution.applyBtnLabel": "应用", "xpack.maps.attribution.attributionFormLabel": "归因", "xpack.maps.attribution.clearBtnAriaLabel": "清除归因", - "xpack.maps.attribution.clearBtnLabel": "清除", "xpack.maps.attribution.editBtnAriaLabel": "编辑归因", - "xpack.maps.attribution.editBtnLabel": "编辑", "xpack.maps.attribution.labelFieldLabel": "标签", "xpack.maps.attribution.urlLabel": "链接", "xpack.maps.badge.readOnly.text": "只读", @@ -20595,15 +20591,7 @@ "xpack.maps.metricsEditor.selectFieldLabel": "字段", "xpack.maps.metricsEditor.selectFieldPlaceholder": "选择字段", "xpack.maps.metricsEditor.selectPercentileLabel": "百分位数", - "xpack.maps.metricSelect.averageDropDownOptionLabel": "平均值", - "xpack.maps.metricSelect.cardinalityDropDownOptionLabel": "唯一计数", - "xpack.maps.metricSelect.countDropDownOptionLabel": "计数", - "xpack.maps.metricSelect.maxDropDownOptionLabel": "最大值", - "xpack.maps.metricSelect.minDropDownOptionLabel": "最小值", - "xpack.maps.metricSelect.percentileDropDownOptionLabel": "百分位数", "xpack.maps.metricSelect.selectAggregationPlaceholder": "选择聚合", - "xpack.maps.metricSelect.sumDropDownOptionLabel": "求和", - "xpack.maps.metricSelect.termsDropDownOptionLabel": "热门词", "xpack.maps.mvtSource.addFieldLabel": "添加", "xpack.maps.mvtSource.fieldPlaceholderText": "字段名称", "xpack.maps.mvtSource.numberFieldLabel": "数字", @@ -23403,7 +23391,7 @@ "xpack.ml.ruleEditor.scopeSection.noPermissionToViewFilterListsTitle": "您无权查看筛选列表", "xpack.ml.ruleEditor.scopeSection.scopeTitle": "范围", "xpack.ml.ruleEditor.selectRuleAction.createRuleLinkText": "创建规则", - "xpack.ml.ruleEditor.selectRuleAction.orText": "或 ", + "xpack.ml.ruleEditor.selectRuleAction.orText": "或 ", "xpack.ml.ruleEditor.typicalAppliesTypeText": "典型", "xpack.ml.sampleDataLinkLabel": "ML 作业", "xpack.ml.selectDataViewLabel": "选择数据视图", From 1bbaa5a62f7ef8a408a9e205e7a6848ecba8ef9c Mon Sep 17 00:00:00 2001 From: Julia Rechkunova Date: Sat, 22 Apr 2023 07:34:33 +0200 Subject: [PATCH 21/29] [TSDB][Discover] Exclude counter fields from Breakdown options (#155532) Closes https://github.com/elastic/kibana/issues/155143 ## Summary This PR excludes time series counter fields (like `bytes_counter`) from "Break down by" options in the histogram. --- .../chart/utils/field_supports_breakdown.test.ts | 15 +++++++++++++++ .../chart/utils/field_supports_breakdown.ts | 5 ++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts index b38b42cf2a249..5ec2d8a1fc638 100644 --- a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts +++ b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.test.ts @@ -31,5 +31,20 @@ describe('fieldSupportsBreakdown', () => { expect( fieldSupportsBreakdown({ aggregatable: true, scripted: false, type: 'string' } as any) ).toBe(true); + + expect( + fieldSupportsBreakdown({ aggregatable: true, scripted: false, type: 'number' } as any) + ).toBe(true); + }); + + it('should return false if field is aggregatable but it is a time series counter metric', () => { + expect( + fieldSupportsBreakdown({ + aggregatable: true, + scripted: false, + type: 'number', + timeSeriesMetric: 'counter', + } as any) + ).toBe(false); }); }); diff --git a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts index 302a5950fefcb..88ec604c1462e 100644 --- a/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts +++ b/src/plugins/unified_histogram/public/chart/utils/field_supports_breakdown.ts @@ -11,4 +11,7 @@ import { DataViewField } from '@kbn/data-views-plugin/public'; const supportedTypes = new Set(['string', 'boolean', 'number', 'ip']); export const fieldSupportsBreakdown = (field: DataViewField) => - supportedTypes.has(field.type) && field.aggregatable && !field.scripted; + supportedTypes.has(field.type) && + field.aggregatable && + !field.scripted && + field.timeSeriesMetric !== 'counter'; From d6d933a2af12764970a85fa5eb3a62eebf02e41b Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Sat, 22 Apr 2023 09:25:37 +0200 Subject: [PATCH 22/29] [ML] Moves shared code to `@kbn/ml-error-utils`. (#155372) - Moves code from `x-pack/plugins/ml/common/util/errors` that was shared via `x-pack/plugins/ml/public/shared.ts` to `@kbn/ml-error-utils`. - `data_visualizer` and `aiops` plugins now use that package instead of code duplication. --- .github/CODEOWNERS | 1 + package.json | 1 + tsconfig.base.json | 2 + x-pack/packages/ml/error_utils/README.md | 3 + .../ml/error_utils}/index.ts | 10 +- x-pack/packages/ml/error_utils/jest.config.js | 12 ++ x-pack/packages/ml/error_utils/kibana.jsonc | 5 + x-pack/packages/ml/error_utils/package.json | 6 + .../error_utils/src/process_errors.test.ts} | 3 +- .../ml/error_utils/src}/process_errors.ts | 22 +- .../ml/error_utils/src}/request_error.ts | 15 ++ x-pack/packages/ml/error_utils/src/types.ts | 196 ++++++++++++++++++ x-pack/packages/ml/error_utils/tsconfig.json | 22 ++ .../public/application/utils/error_utils.ts | 193 ----------------- .../public/hooks/use_document_count_stats.ts | 2 +- x-pack/plugins/aiops/tsconfig.json | 2 +- .../field_data_expanded_row/error_message.tsx | 4 +- .../hooks/use_overall_stats.ts | 2 +- .../requests/get_boolean_field_stats.ts | 2 +- .../requests/get_date_field_stats.ts | 2 +- .../requests/get_field_examples.ts | 2 +- .../requests/get_numeric_field_stats.ts | 2 +- .../requests/get_string_field_stats.ts | 2 +- .../utils/error_utils.ts | 184 ---------------- x-pack/plugins/data_visualizer/tsconfig.json | 2 +- x-pack/plugins/ml/common/constants/search.ts | 5 - x-pack/plugins/ml/common/index.ts | 1 - .../ml/common/types/data_frame_analytics.ts | 2 +- x-pack/plugins/ml/common/types/job_service.ts | 2 +- .../plugins/ml/common/types/job_validation.ts | 2 +- x-pack/plugins/ml/common/types/modules.ts | 2 +- x-pack/plugins/ml/common/types/results.ts | 2 +- .../plugins/ml/common/types/saved_objects.ts | 2 +- x-pack/plugins/ml/common/util/errors/types.ts | 74 ------- .../components/data_grid/common.ts | 6 +- .../import_jobs_flyout/import_jobs_flyout.tsx | 7 +- .../rule_editor/rule_editor_flyout.js | 7 +- .../scatterplot_matrix/scatterplot_matrix.tsx | 10 +- .../data_frame_analytics/common/analytics.ts | 2 +- .../common/get_index_data.ts | 2 +- .../common/use_results_view_config.ts | 3 +- .../create_analytics_advanced_editor.tsx | 4 +- .../details_step/details_step_form.tsx | 2 +- .../components/shared/fetch_explain_data.ts | 3 +- .../validation_step_wrapper.tsx | 6 +- .../hooks/use_index_data.ts | 6 +- .../exploration_query_bar.tsx | 29 ++- .../use_exploration_results.ts | 4 +- .../action_clone/clone_action_name.tsx | 2 +- .../action_delete/use_delete_action.tsx | 2 +- .../use_create_analytics_form.ts | 3 +- .../analytics_service/delete_analytics.ts | 3 +- .../explorer_query_bar/explorer_query_bar.tsx | 15 +- .../application/explorer/explorer_utils.ts | 5 +- .../job_details/job_messages_pane.tsx | 2 +- .../util/model_memory_estimator.ts | 6 +- .../quick_create_job_base.ts | 2 +- .../job_from_lens/visualization_extractor.ts | 2 +- .../components/data_view/change_data_view.tsx | 5 +- .../category_stopped_partitions.tsx | 2 +- .../categorization_view/top_categories.tsx | 2 +- .../post_save_options/post_save_options.tsx | 5 +- .../new_job/recognize/components/job_item.tsx | 2 +- .../recognize/components/kibana_objects.tsx | 2 +- .../test_models/models/inference_base.ts | 2 +- .../inference_input_form/index_input.tsx | 6 +- .../inference_input_form/text_input.tsx | 7 +- .../results_service/result_service_rx.ts | 2 +- .../toast_notification_service.ts | 6 +- .../calendars/list/delete_calendars.js | 5 +- .../forecasting_modal/forecasting_modal.js | 9 +- .../timeseries_chart_with_tooltip.tsx | 2 +- .../get_focus_data.ts | 2 +- .../job_creation/common/job_details.tsx | 13 +- .../layer/incompatible_layer.tsx | 6 +- x-pack/plugins/ml/public/shared.ts | 1 - .../models/data_frame_analytics/validation.ts | 2 +- x-pack/plugins/ml/tsconfig.json | 2 +- .../legacy_uptime/state/api/ml_anomaly.ts | 2 +- x-pack/plugins/synthetics/tsconfig.json | 1 + .../public/app/hooks/use_delete_transform.tsx | 4 +- .../source_search_bar/source_search_bar.tsx | 12 +- .../components/step_define/common/index.ts | 2 +- .../components/step_define/common/types.ts | 5 - .../step_define/hooks/use_search_bar.ts | 12 +- .../server/routes/api/error_utils.ts | 4 +- x-pack/plugins/transform/tsconfig.json | 3 +- .../translations/translations/fr-FR.json | 2 +- .../translations/translations/ja-JP.json | 2 +- .../translations/translations/zh-CN.json | 2 +- yarn.lock | 4 + 91 files changed, 449 insertions(+), 611 deletions(-) create mode 100644 x-pack/packages/ml/error_utils/README.md rename x-pack/{plugins/ml/common/util/errors => packages/ml/error_utils}/index.ts (65%) create mode 100644 x-pack/packages/ml/error_utils/jest.config.js create mode 100644 x-pack/packages/ml/error_utils/kibana.jsonc create mode 100644 x-pack/packages/ml/error_utils/package.json rename x-pack/{plugins/ml/common/util/errors/errors.test.ts => packages/ml/error_utils/src/process_errors.test.ts} (95%) rename x-pack/{plugins/ml/common/util/errors => packages/ml/error_utils/src}/process_errors.ts (86%) rename x-pack/{plugins/ml/common/util/errors => packages/ml/error_utils/src}/request_error.ts (75%) create mode 100644 x-pack/packages/ml/error_utils/src/types.ts create mode 100644 x-pack/packages/ml/error_utils/tsconfig.json delete mode 100644 x-pack/plugins/aiops/public/application/utils/error_utils.ts delete mode 100644 x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts delete mode 100644 x-pack/plugins/ml/common/util/errors/types.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9b3a1b7a7909b..63cf2327516fa 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -455,6 +455,7 @@ src/plugins/maps_ems @elastic/kibana-gis x-pack/plugins/maps @elastic/kibana-gis x-pack/packages/ml/agg_utils @elastic/ml-ui x-pack/packages/ml/date_picker @elastic/ml-ui +x-pack/packages/ml/error_utils @elastic/ml-ui x-pack/packages/ml/is_defined @elastic/ml-ui x-pack/packages/ml/is_populated_object @elastic/ml-ui x-pack/packages/ml/local_storage @elastic/ml-ui diff --git a/package.json b/package.json index b7dc6a0b647d6..56fdffa498b05 100644 --- a/package.json +++ b/package.json @@ -470,6 +470,7 @@ "@kbn/maps-plugin": "link:x-pack/plugins/maps", "@kbn/ml-agg-utils": "link:x-pack/packages/ml/agg_utils", "@kbn/ml-date-picker": "link:x-pack/packages/ml/date_picker", + "@kbn/ml-error-utils": "link:x-pack/packages/ml/error_utils", "@kbn/ml-is-defined": "link:x-pack/packages/ml/is_defined", "@kbn/ml-is-populated-object": "link:x-pack/packages/ml/is_populated_object", "@kbn/ml-local-storage": "link:x-pack/packages/ml/local_storage", diff --git a/tsconfig.base.json b/tsconfig.base.json index 60c1f23d037b8..e96e5439e9c2c 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -904,6 +904,8 @@ "@kbn/ml-agg-utils/*": ["x-pack/packages/ml/agg_utils/*"], "@kbn/ml-date-picker": ["x-pack/packages/ml/date_picker"], "@kbn/ml-date-picker/*": ["x-pack/packages/ml/date_picker/*"], + "@kbn/ml-error-utils": ["x-pack/packages/ml/error_utils"], + "@kbn/ml-error-utils/*": ["x-pack/packages/ml/error_utils/*"], "@kbn/ml-is-defined": ["x-pack/packages/ml/is_defined"], "@kbn/ml-is-defined/*": ["x-pack/packages/ml/is_defined/*"], "@kbn/ml-is-populated-object": ["x-pack/packages/ml/is_populated_object"], diff --git a/x-pack/packages/ml/error_utils/README.md b/x-pack/packages/ml/error_utils/README.md new file mode 100644 index 0000000000000..d8f2837dffbd3 --- /dev/null +++ b/x-pack/packages/ml/error_utils/README.md @@ -0,0 +1,3 @@ +# @kbn/ml-error-utils + +Empty package generated by @kbn/generate diff --git a/x-pack/plugins/ml/common/util/errors/index.ts b/x-pack/packages/ml/error_utils/index.ts similarity index 65% rename from x-pack/plugins/ml/common/util/errors/index.ts rename to x-pack/packages/ml/error_utils/index.ts index f6566c98490da..9eac8a4d1c70b 100644 --- a/x-pack/plugins/ml/common/util/errors/index.ts +++ b/x-pack/packages/ml/error_utils/index.ts @@ -5,8 +5,8 @@ * 2.0. */ -export { MLRequestFailure } from './request_error'; -export { extractErrorMessage, extractErrorProperties } from './process_errors'; +export { MLRequestFailure } from './src/request_error'; +export { extractErrorMessage, extractErrorProperties } from './src/process_errors'; export type { ErrorType, ErrorMessage, @@ -14,6 +14,8 @@ export type { EsErrorRootCause, MLErrorObject, MLHttpFetchError, + MLHttpFetchErrorBase, MLResponseError, -} from './types'; -export { isBoomError, isErrorString, isEsErrorBody, isMLResponseError } from './types'; + QueryErrorMessage, +} from './src/types'; +export { isBoomError, isErrorString, isEsErrorBody, isMLResponseError } from './src/types'; diff --git a/x-pack/packages/ml/error_utils/jest.config.js b/x-pack/packages/ml/error_utils/jest.config.js new file mode 100644 index 0000000000000..f5da401040575 --- /dev/null +++ b/x-pack/packages/ml/error_utils/jest.config.js @@ -0,0 +1,12 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/x-pack/packages/ml/error_utils'], +}; diff --git a/x-pack/packages/ml/error_utils/kibana.jsonc b/x-pack/packages/ml/error_utils/kibana.jsonc new file mode 100644 index 0000000000000..7629766aca7a7 --- /dev/null +++ b/x-pack/packages/ml/error_utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/ml-error-utils", + "owner": "@elastic/ml-ui" +} diff --git a/x-pack/packages/ml/error_utils/package.json b/x-pack/packages/ml/error_utils/package.json new file mode 100644 index 0000000000000..9f0e6c09ef578 --- /dev/null +++ b/x-pack/packages/ml/error_utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/ml-error-utils", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/plugins/ml/common/util/errors/errors.test.ts b/x-pack/packages/ml/error_utils/src/process_errors.test.ts similarity index 95% rename from x-pack/plugins/ml/common/util/errors/errors.test.ts rename to x-pack/packages/ml/error_utils/src/process_errors.test.ts index 5b1e113d06f92..5624c2f0c7477 100644 --- a/x-pack/plugins/ml/common/util/errors/errors.test.ts +++ b/x-pack/packages/ml/error_utils/src/process_errors.test.ts @@ -7,7 +7,8 @@ import Boom from '@hapi/boom'; -import { extractErrorMessage, MLHttpFetchError, EsErrorBody } from '.'; +import { extractErrorMessage } from './process_errors'; +import { type MLHttpFetchError, type EsErrorBody } from './types'; describe('ML - error message utils', () => { describe('extractErrorMessage', () => { diff --git a/x-pack/plugins/ml/common/util/errors/process_errors.ts b/x-pack/packages/ml/error_utils/src/process_errors.ts similarity index 86% rename from x-pack/plugins/ml/common/util/errors/process_errors.ts rename to x-pack/packages/ml/error_utils/src/process_errors.ts index 0da2650fa5fd6..1a50fd6ce3494 100644 --- a/x-pack/plugins/ml/common/util/errors/process_errors.ts +++ b/x-pack/packages/ml/error_utils/src/process_errors.ts @@ -16,15 +16,19 @@ import { isMLResponseError, } from './types'; +/** + * Extract properties of the error object from within the response error + * coming from Kibana, Elasticsearch, and our own ML messages. + * + * @param {ErrorType} error + * @returns {MLErrorObject} + */ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own ML messages - // some responses contain raw es errors as part of a bulk response // e.g. if some jobs fail the action in a bulk request if (isEsErrorBody(error)) { return { - message: error.error.reason, + message: error.error.reason ?? '', statusCode: error.status, fullError: error, }; @@ -79,7 +83,7 @@ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { typeof error.body.attributes.body.error.root_cause[0] === 'object' && isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; + errObj.causedBy = error.body.attributes.body.error.root_cause[0].script as string; errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; } return errObj; @@ -103,8 +107,14 @@ export const extractErrorProperties = (error: ErrorType): MLErrorObject => { }; }; +/** + * Extract only the error message within the response error + * coming from Kibana, Elasticsearch, and our own ML messages. + * + * @param {ErrorType} error + * @returns {string} + */ export const extractErrorMessage = (error: ErrorType): string => { - // extract only the error message within the response error coming from Kibana, Elasticsearch, and our own ML messages const errorObj = extractErrorProperties(error); return errorObj.message; }; diff --git a/x-pack/plugins/ml/common/util/errors/request_error.ts b/x-pack/packages/ml/error_utils/src/request_error.ts similarity index 75% rename from x-pack/plugins/ml/common/util/errors/request_error.ts rename to x-pack/packages/ml/error_utils/src/request_error.ts index 57d63e6cf54b8..a370129871323 100644 --- a/x-pack/plugins/ml/common/util/errors/request_error.ts +++ b/x-pack/packages/ml/error_utils/src/request_error.ts @@ -7,7 +7,22 @@ import { MLErrorObject, ErrorType } from './types'; +/** + * ML Request Failure + * + * @export + * @class MLRequestFailure + * @typedef {MLRequestFailure} + * @extends {Error} + */ export class MLRequestFailure extends Error { + /** + * Creates an instance of MLRequestFailure. + * + * @constructor + * @param {MLErrorObject} error + * @param {ErrorType} resp + */ constructor(error: MLErrorObject, resp: ErrorType) { super(error.message); Object.setPrototypeOf(this, new.target.prototype); diff --git a/x-pack/packages/ml/error_utils/src/types.ts b/x-pack/packages/ml/error_utils/src/types.ts new file mode 100644 index 0000000000000..b66c960b8c8c0 --- /dev/null +++ b/x-pack/packages/ml/error_utils/src/types.ts @@ -0,0 +1,196 @@ +/* + * 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 Boom from '@hapi/boom'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + +/** + * Short hand type of estypes.ErrorCause. + * @typedef {EsErrorRootCause} + */ +export type EsErrorRootCause = estypes.ErrorCause; + +/** + * Short hand type of estypes.ErrorResponseBase. + * @typedef {EsErrorBody} + */ +export type EsErrorBody = estypes.ErrorResponseBase; + +/** + * ML Response error + * @export + * @interface MLResponseError + * @typedef {MLResponseError} + */ +export interface MLResponseError { + /** + * statusCode + * @type {number} + */ + statusCode: number; + /** + * error + * @type {string} + */ + error: string; + /** + * message + * @type {string} + */ + message: string; + /** + * Optional attributes + * @type {?{ + body: EsErrorBody; + }} + */ + attributes?: { + body: EsErrorBody; + }; +} + +/** + * Interface holding error message + * @export + * @interface ErrorMessage + * @typedef {ErrorMessage} + */ +export interface ErrorMessage { + /** + * message + * @type {string} + */ + message: string; +} + +/** + * To be used for client side errors related to search query bars. + */ +export interface QueryErrorMessage extends ErrorMessage { + /** + * query + * @type {string} + */ + query: string; +} + +/** + * ML Error Object + * @export + * @interface MLErrorObject + * @typedef {MLErrorObject} + */ +export interface MLErrorObject { + /** + * Optional causedBy + * @type {?string} + */ + causedBy?: string; + /** + * message + * @type {string} + */ + message: string; + /** + * Optional statusCode + * @type {?number} + */ + statusCode?: number; + /** + * Optional fullError + * @type {?EsErrorBody} + */ + fullError?: EsErrorBody; +} + +/** + * MLHttpFetchErrorBase + * @export + * @interface MLHttpFetchErrorBase + * @typedef {MLHttpFetchErrorBase} + * @template T + * @extends {IHttpFetchError} + */ +export interface MLHttpFetchErrorBase extends IHttpFetchError { + /** + * body + * @type {T} + */ + body: T; +} + +/** + * MLHttpFetchError + * @export + * @typedef {MLHttpFetchError} + */ +export type MLHttpFetchError = MLHttpFetchErrorBase; + +/** + * Union type of error types + * @export + * @typedef {ErrorType} + */ +export type ErrorType = MLHttpFetchError | EsErrorBody | Boom.Boom | string | undefined; + +/** + * Type guard to check if error is of type EsErrorBody + * @export + * @param {unknown} error + * @returns {error is EsErrorBody} + */ +export function isEsErrorBody(error: unknown): error is EsErrorBody { + return isPopulatedObject(error, ['error']) && isPopulatedObject(error.error, ['reason']); +} + +/** + * Type guard to check if error is a string. + * @export + * @param {unknown} error + * @returns {error is string} + */ +export function isErrorString(error: unknown): error is string { + return typeof error === 'string'; +} + +/** + * Type guard to check if error is of type ErrorMessage. + * @export + * @param {unknown} error + * @returns {error is ErrorMessage} + */ +export function isErrorMessage(error: unknown): error is ErrorMessage { + return isPopulatedObject(error, ['message']) && typeof error.message === 'string'; +} + +/** + * Type guard to check if error is of type MLResponseError. + * @export + * @param {unknown} error + * @returns {error is MLResponseError} + */ +export function isMLResponseError(error: unknown): error is MLResponseError { + return ( + isPopulatedObject(error, ['body']) && + isPopulatedObject(error.body, ['message']) && + 'message' in error.body + ); +} + +/** + * Type guard to check if error is of type Boom. + * @export + * @param {unknown} error + * @returns {error is Boom.Boom} + */ +export function isBoomError(error: unknown): error is Boom.Boom { + return isPopulatedObject(error, ['isBoom']) && error.isBoom === true; +} diff --git a/x-pack/packages/ml/error_utils/tsconfig.json b/x-pack/packages/ml/error_utils/tsconfig.json new file mode 100644 index 0000000000000..de1c550b0e1ab --- /dev/null +++ b/x-pack/packages/ml/error_utils/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/ml-is-populated-object", + "@kbn/core-http-browser", + ] +} diff --git a/x-pack/plugins/aiops/public/application/utils/error_utils.ts b/x-pack/plugins/aiops/public/application/utils/error_utils.ts deleted file mode 100644 index f1f1c34dd2959..0000000000000 --- a/x-pack/plugins/aiops/public/application/utils/error_utils.ts +++ /dev/null @@ -1,193 +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. - */ - -// TODO Consolidate with duplicate error utils file in -// `x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts` - -import type { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; - -export interface WrappedError { - body: { - attributes: { - body: EsErrorBody; - }; - message: Boom.Boom; - }; - statusCode: number; -} - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface AiOpsResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface AiOpsErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface AiOpsHttpFetchError extends IHttpFetchError { - body: T; -} - -export type ErrorType = - | WrappedError - | AiOpsHttpFetchError - | EsErrorBody - | Boom.Boom - | string - | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isAiOpsResponseError(error: any): error is AiOpsResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} - -export function isWrappedError(error: any): error is WrappedError { - return error && isBoomError(error.body?.message) === true; -} - -export const extractErrorProperties = (error: ErrorType): AiOpsErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own AiOps messages - - // some responses contain raw es errors as part of a bulk response - // e.g. if some jobs fail the action in a bulk request - - if (isEsErrorBody(error)) { - return { - message: error.error.reason, - statusCode: error.status, - fullError: error, - }; - } - - if (isErrorString(error)) { - return { - message: error, - }; - } - if (isWrappedError(error)) { - return error.body.message?.output?.payload; - } - - if (isBoomError(error)) { - return { - message: error.output.payload.message, - statusCode: error.output.payload.statusCode, - }; - } - - if (error?.body === undefined && !error?.message) { - return { - message: '', - }; - } - - if (typeof error.body === 'string') { - return { - message: error.body, - }; - } - - if (isAiOpsResponseError(error)) { - if ( - typeof error.body.attributes === 'object' && - typeof error.body.attributes.body?.error?.reason === 'string' - ) { - const errObj: AiOpsErrorObject = { - message: error.body.attributes.body.error.reason, - statusCode: error.body.statusCode, - fullError: error.body.attributes.body, - }; - if ( - typeof error.body.attributes.body.error.caused_by === 'object' && - (typeof error.body.attributes.body.error.caused_by?.reason === 'string' || - typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string') - ) { - errObj.causedBy = - error.body.attributes.body.error.caused_by?.caused_by?.reason || - error.body.attributes.body.error.caused_by?.reason; - } - if ( - Array.isArray(error.body.attributes.body.error.root_cause) && - typeof error.body.attributes.body.error.root_cause[0] === 'object' && - isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) - ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; - errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; - } - return errObj; - } else { - return { - message: error.body.message, - statusCode: error.body.statusCode, - }; - } - } - - if (isErrorMessage(error)) { - return { - message: error.message, - }; - } - - // If all else fail return an empty message instead of JSON.stringify - return { - message: '', - }; -}; - -export const extractErrorMessage = (error: ErrorType): string => { - // extract only the error message within the response error coming from Kibana, Elasticsearch, and our own ML messages - const errorObj = extractErrorProperties(error); - return errorObj.message; -}; diff --git a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts index 9e4f50368a0b5..8cfaa074286d6 100644 --- a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts +++ b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts @@ -12,10 +12,10 @@ import { i18n } from '@kbn/i18n'; import type { ToastsStart } from '@kbn/core/public'; import { stringHash } from '@kbn/ml-string-hash'; import { createRandomSamplerWrapper } from '@kbn/ml-random-sampler-utils'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { RANDOM_SAMPLER_SEED } from '../../common/constants'; -import { extractErrorProperties } from '../application/utils/error_utils'; import { DocumentCountStats, getDocumentCountStatsRequest, diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index b9a6ff5408eda..89c236ba25c99 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -34,7 +34,6 @@ "@kbn/ui-theme", "@kbn/i18n-react", "@kbn/rison", - "@kbn/core-http-browser", "@kbn/aiops-components", "@kbn/aiops-utils", "@kbn/licensing-plugin", @@ -51,6 +50,7 @@ "@kbn/ml-route-utils", "@kbn/unified-field-list-plugin", "@kbn/ml-random-sampler-utils", + "@kbn/ml-error-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx index 54cbeb28cf6f3..0f3dc78f97487 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/error_message.tsx @@ -8,14 +8,14 @@ import { EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { DVErrorObject } from '../../../../../index_data_visualizer/utils/error_utils'; +import { MLErrorObject } from '@kbn/ml-error-utils'; export const ErrorMessageContent = ({ fieldName, error, }: { fieldName: string; - error: DVErrorObject; + error: MLErrorObject; }) => { return ( diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts index ced7bf1058762..3419b1674fddc 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/hooks/use_overall_stats.ts @@ -16,6 +16,7 @@ import type { IKibanaSearchResponse, ISearchOptions, } from '@kbn/data-plugin/common'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { useDataVisualizerKibana } from '../../kibana_context'; import { AggregatableFieldOverallStats, @@ -29,7 +30,6 @@ import { } from '../search_strategy/requests/overall_stats'; import type { OverallStats } from '../types/overall_stats'; import { getDefaultPageState } from '../components/index_data_visualizer_view/index_data_visualizer_view'; -import { extractErrorProperties } from '../utils/error_utils'; import { DataStatsFetchProgress, isRandomSamplingOption, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts index bf941e0952657..e394f6456d8f5 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_boolean_field_stats.ts @@ -15,6 +15,7 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; @@ -25,7 +26,6 @@ import type { FieldStatsCommonRequestParams, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getBooleanFieldsStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts index 863cd6885fe88..4bd914f3637e5 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_date_field_stats.ts @@ -16,11 +16,11 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import type { FieldStatsCommonRequestParams } from '../../../../../common/types/field_stats'; import type { Field, DateFieldStats, Aggs } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getDateFieldsStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts index df7afb16479f0..3943979d290d9 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_field_examples.ts @@ -17,6 +17,7 @@ import type { import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { buildBaseFilterCriteria } from '@kbn/ml-query-utils'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { getUniqGeoOrStrExamples } from '../../../common/util/example_utils'; import type { Field, @@ -24,7 +25,6 @@ import type { FieldStatsCommonRequestParams, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; import { MAX_EXAMPLES_DEFAULT } from './constants'; export const getFieldExamplesRequest = (params: FieldStatsCommonRequestParams, field: Field) => { diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts index b9dd55351781f..f7d1b39f15d3f 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_numeric_field_stats.ts @@ -18,6 +18,7 @@ import type { import type { ISearchStart } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { isDefined } from '@kbn/ml-is-defined'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import { MAX_PERCENT, PERCENTILE_SPACING, SAMPLER_TOP_TERMS_THRESHOLD } from './constants'; @@ -32,7 +33,6 @@ import type { FieldStatsError, } from '../../../../../common/types/field_stats'; import { processDistributionData } from '../../utils/process_distribution_data'; -import { extractErrorProperties } from '../../utils/error_utils'; import { isIKibanaSearchResponse, isNormalSamplingOption, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts index a035842fa8767..159be48b338e4 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/search_strategy/requests/get_string_field_stats.ts @@ -16,6 +16,7 @@ import type { ISearchStart, } from '@kbn/data-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { processTopValues } from './utils'; import { buildAggregationWithSamplingOption } from './build_random_sampler_agg'; import { SAMPLER_TOP_TERMS_THRESHOLD } from './constants'; @@ -26,7 +27,6 @@ import type { StringFieldStats, } from '../../../../../common/types/field_stats'; import { FieldStatsError, isIKibanaSearchResponse } from '../../../../../common/types/field_stats'; -import { extractErrorProperties } from '../../utils/error_utils'; export const getStringFieldStatsRequest = ( params: FieldStatsCommonRequestParams, diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts deleted file mode 100644 index 06a9b0b4002a6..0000000000000 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/utils/error_utils.ts +++ /dev/null @@ -1,184 +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 type { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; - -export interface WrappedError { - body: { - attributes: { - body: EsErrorBody; - }; - message: Boom.Boom; - }; - statusCode: number; -} - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface DVResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface DVErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface DVHttpFetchError extends IHttpFetchError { - body: T; -} - -export type ErrorType = - | WrappedError - | DVHttpFetchError - | EsErrorBody - | Boom.Boom - | string - | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isDVResponseError(error: any): error is DVResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} - -export function isWrappedError(error: any): error is WrappedError { - return error && isBoomError(error.body?.message) === true; -} - -export const extractErrorProperties = (error: ErrorType): DVErrorObject => { - // extract properties of the error object from within the response error - // coming from Kibana, Elasticsearch, and our own DV messages - - // some responses contain raw es errors as part of a bulk response - // e.g. if some jobs fail the action in a bulk request - - if (isEsErrorBody(error)) { - return { - message: error.error.reason, - statusCode: error.status, - fullError: error, - }; - } - - if (isErrorString(error)) { - return { - message: error, - }; - } - if (isWrappedError(error)) { - return error.body.message?.output?.payload; - } - - if (isBoomError(error)) { - return { - message: error.output.payload.message, - statusCode: error.output.payload.statusCode, - }; - } - - if (error?.body === undefined && !error?.message) { - return { - message: '', - }; - } - - if (typeof error.body === 'string') { - return { - message: error.body, - }; - } - - if (isDVResponseError(error)) { - if ( - typeof error.body.attributes === 'object' && - typeof error.body.attributes.body?.error?.reason === 'string' - ) { - const errObj: DVErrorObject = { - message: error.body.attributes.body.error.reason, - statusCode: error.body.statusCode, - fullError: error.body.attributes.body, - }; - if ( - typeof error.body.attributes.body.error.caused_by === 'object' && - (typeof error.body.attributes.body.error.caused_by?.reason === 'string' || - typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string') - ) { - errObj.causedBy = - error.body.attributes.body.error.caused_by?.caused_by?.reason || - error.body.attributes.body.error.caused_by?.reason; - } - if ( - Array.isArray(error.body.attributes.body.error.root_cause) && - typeof error.body.attributes.body.error.root_cause[0] === 'object' && - isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) - ) { - errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; - errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; - } - return errObj; - } else { - return { - message: error.body.message, - statusCode: error.body.statusCode, - }; - } - } - - if (isErrorMessage(error)) { - return { - message: error.message, - }; - } - - // If all else fail return an empty message instead of JSON.stringify - return { - message: '', - }; -}; diff --git a/x-pack/plugins/data_visualizer/tsconfig.json b/x-pack/plugins/data_visualizer/tsconfig.json index 7f38e7b9b77cb..4609d1e9497d2 100644 --- a/x-pack/plugins/data_visualizer/tsconfig.json +++ b/x-pack/plugins/data_visualizer/tsconfig.json @@ -17,7 +17,6 @@ "@kbn/cloud-chat-plugin", "@kbn/cloud-plugin", "@kbn/core-execution-context-common", - "@kbn/core-http-browser", "@kbn/core", "@kbn/custom-integrations-plugin", "@kbn/data-plugin", @@ -60,6 +59,7 @@ "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", + "@kbn/ml-error-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/ml/common/constants/search.ts b/x-pack/plugins/ml/common/constants/search.ts index 9985b502b085c..8ff9b022c274f 100644 --- a/x-pack/plugins/ml/common/constants/search.ts +++ b/x-pack/plugins/ml/common/constants/search.ts @@ -14,8 +14,3 @@ export const SEARCH_QUERY_LANGUAGE = { } as const; export type SearchQueryLanguage = typeof SEARCH_QUERY_LANGUAGE[keyof typeof SEARCH_QUERY_LANGUAGE]; - -export interface ErrorMessage { - query: string; - message: string; -} diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index 8d419f120a564..b540c4d3751f1 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -16,7 +16,6 @@ export { export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; export { composeValidators, patternValidator } from './util/validators'; export { isRuntimeMappings, isRuntimeField } from './util/runtime_field_utils'; -export { extractErrorMessage } from './util/errors'; export type { RuntimeMappings } from './types/fields'; export { getDefaultCapabilities as getDefaultMlCapabilities } from './types/capabilities'; export { DATAFEED_STATE, JOB_STATE } from './constants/states'; diff --git a/x-pack/plugins/ml/common/types/data_frame_analytics.ts b/x-pack/plugins/ml/common/types/data_frame_analytics.ts index 26bdd29ac3090..cba66124bab4b 100644 --- a/x-pack/plugins/ml/common/types/data_frame_analytics.ts +++ b/x-pack/plugins/ml/common/types/data_frame_analytics.ts @@ -9,7 +9,7 @@ import Boom from '@hapi/boom'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import { EsErrorBody } from '../util/errors'; +import { EsErrorBody } from '@kbn/ml-error-utils'; import { ANALYSIS_CONFIG_TYPE } from '../constants/data_frame_analytics'; import type { UrlConfig } from './custom_urls'; diff --git a/x-pack/plugins/ml/common/types/job_service.ts b/x-pack/plugins/ml/common/types/job_service.ts index a3e1571070ffd..cf029111e0577 100644 --- a/x-pack/plugins/ml/common/types/job_service.ts +++ b/x-pack/plugins/ml/common/types/job_service.ts @@ -5,10 +5,10 @@ * 2.0. */ +import type { ErrorType } from '@kbn/ml-error-utils'; import { Job, JobStats, IndicesOptions } from './anomaly_detection_jobs'; import { RuntimeMappings } from './fields'; import { ES_AGGREGATION } from '../constants/aggregation_types'; -import { ErrorType } from '../util/errors'; export interface MlJobsResponse { jobs: Job[]; diff --git a/x-pack/plugins/ml/common/types/job_validation.ts b/x-pack/plugins/ml/common/types/job_validation.ts index 0c1db63ff3762..226166d45c956 100644 --- a/x-pack/plugins/ml/common/types/job_validation.ts +++ b/x-pack/plugins/ml/common/types/job_validation.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; export interface DatafeedValidationResponse { valid: boolean; diff --git a/x-pack/plugins/ml/common/types/modules.ts b/x-pack/plugins/ml/common/types/modules.ts index dd9f098cabe1c..2a6cc9bbd57ce 100644 --- a/x-pack/plugins/ml/common/types/modules.ts +++ b/x-pack/plugins/ml/common/types/modules.ts @@ -6,8 +6,8 @@ */ import type { SavedObjectAttributes } from '@kbn/core/types'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { Datafeed, Job } from './anomaly_detection_jobs'; -import type { ErrorType } from '../util/errors'; export interface ModuleJob { id: string; diff --git a/x-pack/plugins/ml/common/types/results.ts b/x-pack/plugins/ml/common/types/results.ts index cf25fc6081e16..3fda97b5740b1 100644 --- a/x-pack/plugins/ml/common/types/results.ts +++ b/x-pack/plugins/ml/common/types/results.ts @@ -7,7 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { LineAnnotationDatum, RectAnnotationDatum } from '@elastic/charts'; -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { EntityField } from '../util/anomaly_utils'; import type { Datafeed, JobId, ModelSnapshot } from './anomaly_detection_jobs'; import { ES_AGGREGATION, ML_JOB_AGGREGATION } from '../constants/aggregation_types'; diff --git a/x-pack/plugins/ml/common/types/saved_objects.ts b/x-pack/plugins/ml/common/types/saved_objects.ts index ab3b97d1e614d..adaf00fd9405f 100644 --- a/x-pack/plugins/ml/common/types/saved_objects.ts +++ b/x-pack/plugins/ml/common/types/saved_objects.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ErrorType } from '../util/errors'; +import type { ErrorType } from '@kbn/ml-error-utils'; export type JobType = 'anomaly-detector' | 'data-frame-analytics'; export type TrainedModelType = 'trained-model'; diff --git a/x-pack/plugins/ml/common/util/errors/types.ts b/x-pack/plugins/ml/common/util/errors/types.ts deleted file mode 100644 index 9f4b123e3e45d..0000000000000 --- a/x-pack/plugins/ml/common/util/errors/types.ts +++ /dev/null @@ -1,74 +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 type { IHttpFetchError } from '@kbn/core-http-browser'; -import Boom from '@hapi/boom'; - -export interface EsErrorRootCause { - type: string; - reason: string; - caused_by?: EsErrorRootCause; - script?: string; -} - -export interface EsErrorBody { - error: { - root_cause?: EsErrorRootCause[]; - caused_by?: EsErrorRootCause; - type: string; - reason: string; - }; - status: number; -} - -export interface MLResponseError { - statusCode: number; - error: string; - message: string; - attributes?: { - body: EsErrorBody; - }; -} - -export interface ErrorMessage { - message: string; -} - -export interface MLErrorObject { - causedBy?: string; - message: string; - statusCode?: number; - fullError?: EsErrorBody; -} - -export interface MLHttpFetchErrorBase extends IHttpFetchError { - body: T; -} - -export type MLHttpFetchError = MLHttpFetchErrorBase; - -export type ErrorType = MLHttpFetchError | EsErrorBody | Boom.Boom | string | undefined; - -export function isEsErrorBody(error: any): error is EsErrorBody { - return error && error.error?.reason !== undefined; -} - -export function isErrorString(error: any): error is string { - return typeof error === 'string'; -} - -export function isErrorMessage(error: any): error is ErrorMessage { - return error && error.message !== undefined && typeof error.message === 'string'; -} - -export function isMLResponseError(error: any): error is MLResponseError { - return typeof error.body === 'object' && 'message' in error.body; -} - -export function isBoomError(error: any): error is Boom.Boom { - return error?.isBoom === true; -} diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index 86587112f37d7..a32aafe2b0622 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -12,16 +12,14 @@ import { useMemo } from 'react'; import { EuiDataGridCellValueElementProps, EuiDataGridStyle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import { CoreSetup } from '@kbn/core/public'; - import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { getNestedProperty } from '@kbn/ml-nested-property'; - import { isCounterTimeSeriesMetric } from '@kbn/ml-agg-utils'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { FeatureImportance, FeatureImportanceClassName, diff --git a/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx b/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx index 0e786a63b222d..94effae6da28d 100644 --- a/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/import_jobs_flyout.tsx @@ -6,9 +6,7 @@ */ import React, { FC, useState, useEffect, useCallback, useMemo } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import useDebounce from 'react-use/lib/useDebounce'; -import { i18n } from '@kbn/i18n'; import { EuiFlyout, @@ -29,6 +27,10 @@ import { EuiFieldText, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { type ErrorType, extractErrorProperties } from '@kbn/ml-error-utils'; + import type { DataFrameAnalyticsConfig } from '../../../data_frame_analytics/common'; import type { JobType } from '../../../../../common/types/saved_objects'; import { useMlApiContext, useMlKibana } from '../../../contexts/kibana'; @@ -38,7 +40,6 @@ import { toastNotificationServiceProvider } from '../../../services/toast_notifi import { JobImportService } from './jobs_import_service'; import { useValidateIds } from './validate'; import type { ImportedAdJob, JobIdObject, SkippedJobs } from './jobs_import_service'; -import { ErrorType, extractErrorProperties } from '../../../../../common/util/errors'; interface Props { isDisabled: boolean; diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js index ed5be09e03b42..668907c010108 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js @@ -11,8 +11,6 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton, @@ -31,6 +29,10 @@ import { EuiTitle, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { DetectorDescriptionList } from './components/detector_description_list'; import { ActionsSection } from './actions_section'; import { checkPermission } from '../../capabilities/check_capabilities'; @@ -54,7 +56,6 @@ import { getPartitioningFieldNames } from '../../../../common/util/job_utils'; import { withKibana } from '@kbn/kibana-react-plugin/public'; import { mlJobService } from '../../services/job_service'; import { ml } from '../../services/ml_api_service'; -import { extractErrorMessage } from '../../../../common/util/errors'; class RuleEditorFlyoutUI extends Component { static propTypes = { diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx index 9a10dc2b782c9..404c897822125 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx @@ -6,9 +6,8 @@ */ import React, { useMemo, useEffect, useState, FC, useCallback } from 'react'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import rison from '@kbn/rison'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiCallOut, EuiComboBox, @@ -23,16 +22,17 @@ import { EuiSwitch, } from '@elastic/eui'; +import rison from '@kbn/rison'; import { i18n } from '@kbn/i18n'; import { Query } from '@kbn/data-plugin/common/query'; - import { DataView } from '@kbn/data-views-plugin/public'; import { stringHash } from '@kbn/ml-string-hash'; -import { extractErrorMessage } from '../../../../common'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { isRuntimeMappings } from '../../../../common/util/runtime_field_utils'; import { RuntimeMappings } from '../../../../common/types/fields'; -import { getCombinedRuntimeMappings } from '../data_grid'; +import { getCombinedRuntimeMappings } from '../data_grid'; import { useMlApiContext, useMlKibana } from '../../contexts/kibana'; import { getProcessedFields } from '../data_grid'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index 4638d3824ad61..a1609c601cb72 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -9,9 +9,9 @@ import { useEffect } from 'react'; import { BehaviorSubject, Subscription } from 'rxjs'; import { distinctUntilChanged, filter } from 'rxjs/operators'; import { cloneDeep } from 'lodash'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ml } from '../../services/ml_api_service'; import { Dictionary } from '../../../../common/types/common'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { ClassificationEvaluateResponse, EvaluateMetrics, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts index 29d76d32d8855..afd580da21ea3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts @@ -6,7 +6,7 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { extractErrorMessage } from '../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { EsSorting, UseDataGridReturnType, getProcessedFields } from '../../components/data_grid'; import { ml } from '../../services/ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts index df430142a3193..1908ff9ad614c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts @@ -10,8 +10,7 @@ import { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; - -import { extractErrorMessage } from '../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { getDataViewIdFromName } from '../../util/index_utils'; import { ml } from '../../services/ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx index ef43f1ee0d08b..c809597ed1208 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/create_analytics_advanced_editor/create_analytics_advanced_editor.tsx @@ -10,11 +10,11 @@ import { debounce } from 'lodash'; import { EuiCallOut, EuiFieldText, EuiForm, EuiFormRow, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { useNotifications } from '../../../../../contexts/kibana'; import { ml } from '../../../../../services/ml_api_service'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form'; import { CreateStep } from '../create_step'; import { ANALYTICS_STEPS } from '../../page'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx index 265018ec23f9f..dcf5adfba5ff6 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/details_step/details_step_form.tsx @@ -9,6 +9,7 @@ import React, { FC, Fragment, useRef, useEffect, useMemo, useState } from 'react import { debounce } from 'lodash'; import { EuiFieldText, EuiFormRow, EuiLink, EuiSpacer, EuiSwitch, EuiTextArea } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { useMlKibana } from '../../../../../contexts/kibana'; import { CreateAnalyticsStepProps } from '../../../analytics_management/hooks/use_create_analytics_form'; @@ -16,7 +17,6 @@ import { JOB_ID_MAX_LENGTH } from '../../../../../../../common/constants/validat import { ContinueButton } from '../continue_button'; import { ANALYTICS_STEPS } from '../../page'; import { ml } from '../../../../../services/ml_api_service'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; const DEFAULT_RESULTS_FIELD = 'ml'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts index ebf5d2dce2706..e0be92ebe1a26 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/shared/fetch_explain_data.ts @@ -5,8 +5,9 @@ * 2.0. */ +import { extractErrorProperties } from '@kbn/ml-error-utils'; + import { ml } from '../../../../../services/ml_api_service'; -import { extractErrorProperties } from '../../../../../../../common/util/errors'; import { DfAnalyticsExplainResponse, FieldSelectionItem, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx index d5b2ba8251ad4..c1256529f357f 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/validation_step/validation_step_wrapper.tsx @@ -6,9 +6,12 @@ */ import React, { FC, useEffect, useMemo, useState } from 'react'; +import { debounce } from 'lodash'; + import { EuiForm } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; -import { debounce } from 'lodash'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { CreateAnalyticsStepProps } from '../../../analytics_management/hooks/use_create_analytics_form'; import { ValidationStep } from './validation_step'; @@ -16,7 +19,6 @@ import { ValidationStepDetails } from './validation_step_details'; import { ANALYTICS_STEPS } from '../../page'; import { useMlApiContext } from '../../../../../contexts/kibana'; import { getJobConfigFromFormState } from '../../../analytics_management/hooks/use_create_analytics_form/state'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { CalloutMessage, ValidateAnalyticsJobResponse, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts index 4f681f07ff69e..fdd338be65bb2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts @@ -9,11 +9,13 @@ import { useEffect, useMemo, useState } from 'react'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiDataGridColumn } from '@elastic/eui'; -import { CoreSetup } from '@kbn/core/public'; +import { CoreSetup } from '@kbn/core/public'; import type { DataView } from '@kbn/data-views-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { isRuntimeMappings } from '../../../../../../common/util/runtime_field_utils'; import { RuntimeMappings } from '../../../../../../common/types/fields'; import { DEFAULT_SAMPLER_SHARD_SIZE } from '../../../../../../common/constants/field_histograms'; @@ -34,7 +36,7 @@ import { getProcessedFields, getCombinedRuntimeMappings, } from '../../../../components/data_grid'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; + import { INDEX_STATUS } from '../../../common/analytics'; import { ml } from '../../../../services/ml_api_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx index 464d57ff2917a..1ac8404318385 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx @@ -6,17 +6,19 @@ */ import React, { FC, useEffect, useMemo, useState } from 'react'; +import { debounce } from 'lodash'; + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiButtonGroup, EuiCode, EuiFlexGroup, EuiFlexItem, EuiInputPopover } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { debounce } from 'lodash'; +import { i18n } from '@kbn/i18n'; import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@kbn/es-query'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { DataView } from '@kbn/data-views-plugin/common'; import type { Query } from '@kbn/es-query'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; -import { Dictionary } from '../../../../../../../common/types/common'; +import { QueryErrorMessage } from '@kbn/ml-error-utils'; +import { Dictionary } from '../../../../../../../common/types/common'; import { SEARCH_QUERY_LANGUAGE, SearchQueryLanguage, @@ -25,11 +27,6 @@ import { removeFilterFromQueryString } from '../../../../../explorer/explorer_ut import { SavedSearchQuery } from '../../../../../contexts/ml'; import { useMlKibana } from '../../../../../contexts/kibana'; -interface ErrorMessage { - query: string; - message: string; -} - export interface ExplorationQueryBarProps { indexPattern: DataView; setSearchQuery: (update: { @@ -55,7 +52,9 @@ export const ExplorationQueryBar: FC = ({ // The internal state of the input query bar updated on every key stroke. const [searchInput, setSearchInput] = useState(query); const [idToSelectedMap, setIdToSelectedMap] = useState<{ [id: string]: boolean }>({}); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); const { services } = useMlKibana(); const { @@ -119,7 +118,7 @@ export const ExplorationQueryBar: FC = ({ convertedQuery = luceneStringToDsl(query.query as string); break; default: - setErrorMessage({ + setQueryErrorMessage({ query: query.query as string, message: i18n.translate('xpack.ml.queryBar.queryLanguageNotSupported', { defaultMessage: 'Query language is not supported', @@ -133,7 +132,7 @@ export const ExplorationQueryBar: FC = ({ language: query.language, }); } catch (e) { - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [query.query]); @@ -187,7 +186,7 @@ export const ExplorationQueryBar: FC = ({ return ( setErrorMessage(undefined)} + closePopover={() => setQueryErrorMessage(undefined)} input={ @@ -249,14 +248,14 @@ export const ExplorationQueryBar: FC = ({ )} } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.ml.stepDefineForm.invalidQuery', { defaultMessage: 'Invalid Query', })} {': '} - {errorMessage?.message.split('\n')[0]} + {queryErrorMessage?.message.split('\n')[0]} ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts index b52c06905792c..30749558a23a1 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_results_table/use_exploration_results.ts @@ -10,9 +10,10 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiDataGridColumn } from '@elastic/eui'; import { CoreSetup } from '@kbn/core/public'; - import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { MlApiServices } from '../../../../../services/ml_api_service'; import { DataLoader } from '../../../../../datavisualizer/index_based/data_loader'; @@ -35,7 +36,6 @@ import { FEATURE_IMPORTANCE, TOP_CLASSES } from '../../../../common/constants'; import { DEFAULT_RESULTS_FIELD } from '../../../../../../../common/constants/data_frame_analytics'; import { sortExplorationResultsFields, ML__ID_COPY } from '../../../../common/fields'; import { isRegressionAnalysis } from '../../../../common/analytics'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; import { useTrainedModelsApiService } from '../../../../../services/ml_api_service/trained_models'; import { FeatureImportanceBaseline } from '../../../../../../../common/types/feature_importance'; import { useExplorationDataGrid } from './use_exploration_data_grid'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx index 76b85e2384dfd..0ba51a7ca64da 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_clone/clone_action_name.tsx @@ -11,6 +11,7 @@ import { cloneDeep, isEqual } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { DeepReadonly } from '../../../../../../../common/types/common'; import { DataFrameAnalyticsConfig, isOutlierAnalysis } from '../../../../common'; import { isClassificationAnalysis, isRegressionAnalysis } from '../../../../common/analytics'; @@ -19,7 +20,6 @@ import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana'; import { DEFAULT_NUM_TOP_FEATURE_IMPORTANCE_VALUES } from '../../hooks/use_create_analytics_form'; import { State } from '../../hooks/use_create_analytics_form/state'; import { DataFrameAnalyticsListRow } from '../analytics_list/common'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; interface PropDefinition { /** diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx index 4d1565c1769f3..01b6e3a3f50ad 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/action_delete/use_delete_action.tsx @@ -8,7 +8,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { useMlKibana } from '../../../../../contexts/kibana'; import { useToastNotificationService } from '../../../../../services/toast_notification_service'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index cddc4fcd092dc..4a6ed2176be25 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -10,7 +10,8 @@ import { useReducer } from 'react'; import { i18n } from '@kbn/i18n'; import { DuplicateDataViewError } from '@kbn/data-plugin/public'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { DeepReadonly } from '../../../../../../../common/types/common'; import { ml } from '../../../../../services/ml_api_service'; import { useMlContext } from '../../../../../contexts/ml'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts index 537b2016d9af3..c11490a660f10 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/delete_analytics.ts @@ -6,7 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../../../common/util/errors'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ml } from '../../../../../services/ml_api_service'; import { ToastNotificationService } from '../../../../../services/toast_notification_service'; import { refreshAnalyticsList$, REFRESH_ANALYTICS_LIST_STATE } from '../../../../common'; diff --git a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx index bbb18697dab1c..8f4cd30a4c115 100644 --- a/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/explorer/components/explorer_query_bar/explorer_query_bar.tsx @@ -12,7 +12,8 @@ import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@k import type { Query } from '@kbn/es-query'; import { QueryStringInput } from '@kbn/unified-search-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { SEARCH_QUERY_LANGUAGE, ErrorMessage } from '../../../../../common/constants/search'; +import type { QueryErrorMessage } from '@kbn/ml-error-utils'; +import { SEARCH_QUERY_LANGUAGE } from '../../../../../common/constants/search'; import { InfluencersFilterQuery } from '../../../../../common/types/es_client'; import { useAnomalyExplorerContext } from '../../anomaly_explorer_context'; import { useMlKibana } from '../../../contexts/kibana'; @@ -129,7 +130,9 @@ export const ExplorerQueryBar: FC = ({ const [searchInput, setSearchInput] = useState( getInitSearchInputState({ filterActive, queryString }) ); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); useEffect( function updateSearchInputFromFilter() { @@ -160,14 +163,14 @@ export const ExplorerQueryBar: FC = ({ } } catch (e) { console.log('Invalid query syntax in search bar', e); // eslint-disable-line no-console - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } }; return ( = ({ }} /> } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.ml.explorer.invalidKuerySyntaxErrorMessageQueryBar', { defaultMessage: 'Invalid query', })} {': '} - {errorMessage?.message.split('\n')[0]} + {queryErrorMessage?.message.split('\n')[0]} ); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts index e7ad6802c4401..f0f26a3918438 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.ts @@ -11,19 +11,20 @@ import { get, union, uniq } from 'lodash'; import moment from 'moment-timezone'; +import { lastValueFrom } from 'rxjs'; + import { ES_FIELD_TYPES } from '@kbn/field-types'; import { asyncForEach } from '@kbn/std'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; -import { lastValueFrom } from 'rxjs'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE, ANOMALIES_TABLE_DEFAULT_QUERY_SIZE, } from '../../../common/constants/search'; import { EntityField, getEntityFieldList } from '../../../common/util/anomaly_utils'; import { getDataViewIdFromName } from '../util/index_utils'; -import { extractErrorMessage } from '../../../common/util/errors'; import { ML_JOB_AGGREGATION } from '../../../common/constants/aggregation_types'; import { isSourceDataChartableForDetector, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx index 8b3ed99709cb5..86cd23b46383e 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/job_messages_pane.tsx @@ -9,10 +9,10 @@ import React, { FC, useCallback, useEffect, useState } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ml } from '../../../../services/ml_api_service'; import { JobMessages } from '../../../../components/job_messages'; import { JobMessage } from '../../../../../../common/types/audit_message'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; import { useToastNotificationService } from '../../../../services/toast_notification_service'; import { useMlApiContext } from '../../../../contexts/kibana'; import { checkPermission } from '../../../../capabilities/check_capabilities'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts index 159f1af10e69f..52d4d77106217 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/model_memory_estimator.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import { combineLatest, Observable, of, Subject, Subscription } from 'rxjs'; import { isEqual, cloneDeep } from 'lodash'; import { @@ -21,10 +20,13 @@ import { skipWhile, } from 'rxjs/operators'; import { useEffect, useMemo } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { type MLHttpFetchError, extractErrorMessage } from '@kbn/ml-error-utils'; + import { DEFAULT_MODEL_MEMORY_LIMIT } from '../../../../../../../common/constants/new_job'; import { ml } from '../../../../../services/ml_api_service'; import { JobValidator, VALIDATION_DELAY_MS } from '../../job_validator/job_validator'; -import { MLHttpFetchError, extractErrorMessage } from '../../../../../../../common/util/errors'; import { useMlKibana } from '../../../../../contexts/kibana'; import { JobCreator } from '../job_creator'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts index a22b6d9fd57a5..f01898fa905a1 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts @@ -17,9 +17,9 @@ import type { Filter, Query, DataViewBase } from '@kbn/es-query'; import { FilterStateStore } from '@kbn/es-query'; import type { Embeddable } from '@kbn/lens-plugin/public'; import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import type { ErrorType } from '@kbn/ml-error-utils'; import type { MlApiServices } from '../../../services/ml_api_service'; import { getFiltersForDSLQuery } from '../../../../../common/util/job_utils'; -import type { ErrorType } from '../../../../../common/util/errors'; import { CREATED_BY_LABEL } from '../../../../../common/constants/new_job'; import { createQueries } from '../utils/new_job_utils'; import { createDatafeedId } from '../../../../../common/util/job_utils'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts index c7f5a02e75d5b..4552123184f5c 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/visualization_extractor.ts @@ -10,8 +10,8 @@ import { layerTypes } from '@kbn/lens-plugin/public'; import { i18n } from '@kbn/i18n'; +import type { ErrorType } from '@kbn/ml-error-utils'; import { JOB_TYPE } from '../../../../../common/constants/new_job'; -import { ErrorType } from '../../../../../common/util/errors'; import { getVisTypeFactory, isCompatibleLayer, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx index 71d68b895f605..9e68d9f6f8c37 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx @@ -6,7 +6,6 @@ */ import React, { FC, useState, useEffect, useCallback, useContext } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { @@ -23,7 +22,10 @@ import { EuiModalBody, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { JobCreatorContext } from '../../../job_creator_context'; import { AdvancedJobCreator } from '../../../../../common/job_creator'; import { resetAdvancedJob } from '../../../../../common/job_creator/util/general'; @@ -31,7 +33,6 @@ import { CombinedJob, Datafeed, } from '../../../../../../../../../common/types/anomaly_detection_jobs'; -import { extractErrorMessage } from '../../../../../../../../../common/util/errors'; import type { DatafeedValidationResponse } from '../../../../../../../../../common/types/job_validation'; import { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx index 8030292bd9e59..090fcf40d3f17 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/category_stopped_partitions.tsx @@ -11,10 +11,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { from } from 'rxjs'; import { switchMap, takeWhile, tap } from 'rxjs/operators'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; import { ml } from '../../../../../../../services/ml_api_service'; -import { extractErrorProperties } from '../../../../../../../../../common/util/errors'; const NUMBER_OF_PREVIEW = 5; export const CategoryStoppedPartitions: FC = () => { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx index 4491dfab1abdd..b12b4261a0628 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx @@ -8,13 +8,13 @@ import React, { FC, useContext, useEffect, useState } from 'react'; import { EuiBasicTable, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; import { Results } from '../../../../../common/results_loader'; import { ml } from '../../../../../../../services/ml_api_service'; import { useToastNotificationService } from '../../../../../../../services/toast_notification_service'; import { NUMBER_OF_CATEGORY_EXAMPLES } from '../../../../../../../../../common/constants/categorization_job'; -import { extractErrorProperties } from '../../../../../../../../../common/util/errors'; export const TopCategories: FC = () => { const { displayErrorToast } = useToastNotificationService(); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx index 7fb483b54cf5d..75bc8772e0ac6 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/components/post_save_options/post_save_options.tsx @@ -6,12 +6,15 @@ */ import React, { FC, Fragment, useContext, useState } from 'react'; + import { EuiButton, EuiFlexItem } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { JobRunner } from '../../../../../common/job_runner'; import { useMlKibana } from '../../../../../../../contexts/kibana'; -import { extractErrorMessage } from '../../../../../../../../../common/util/errors'; import { JobCreatorContext } from '../../../job_creator_context'; import { DATAFEED_STATE } from '../../../../../../../../../common/constants/states'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx index 93b6086398358..cd6bd11096c65 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx @@ -18,11 +18,11 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ModuleJobUI } from '../page'; import { SETUP_RESULTS_WIDTH } from './module_jobs'; import { tabColor } from '../../../../../../common/util/group_color_utils'; import { JobOverride, DatafeedResponse } from '../../../../../../common/types/modules'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; interface JobItemProps { job: ModuleJobUI; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx index 079e698c3a5c0..95ac0b4043f57 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx @@ -18,8 +18,8 @@ import { EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { KibanaObjectUi } from '../page'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; export interface KibanaObjectItemProps { objectType: string; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts index 59fa138d0f29f..1e3e3ecd5dfd7 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { map } from 'rxjs/operators'; import { SupportedPytorchTasksType } from '@kbn/ml-trained-models-utils'; -import { MLHttpFetchError } from '../../../../../common/util/errors'; +import type { MLHttpFetchError } from '@kbn/ml-error-utils'; import { trainedModelsApiProvider } from '../../../services/ml_api_service/trained_models'; import { getInferenceInfoComponent } from './inference_info'; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx index 5060f0033fd6c..4dbe900283657 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx @@ -8,7 +8,7 @@ import React, { FC, useState, useMemo, useCallback, FormEventHandler } from 'react'; import useObservable from 'react-use/lib/useObservable'; -import { FormattedMessage } from '@kbn/i18n-react'; + import { EuiSpacer, EuiButton, @@ -21,8 +21,10 @@ import { EuiForm, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ErrorMessage } from '../../inference_error'; -import { extractErrorMessage } from '../../../../../../common'; import type { InferrerType } from '..'; import { useIndexInput, InferenceInputFormIndexControls } from '../index_input'; import { RUNNING_STATE } from '../inference_base'; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx index 3446bda477b4a..fc162a305c32b 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx @@ -6,13 +6,14 @@ */ import React, { FC, useState, useMemo, useCallback, FormEventHandler } from 'react'; - import useObservable from 'react-use/lib/useObservable'; -import { FormattedMessage } from '@kbn/i18n-react'; + import { EuiSpacer, EuiButton, EuiTabs, EuiTab, EuiForm } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { ErrorMessage } from '../../inference_error'; -import { extractErrorMessage } from '../../../../../../common'; import type { InferrerType } from '..'; import { OutputLoadingContent } from '../../output_loading'; import { RUNNING_STATE } from '../inference_base'; diff --git a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts index 2cfcf50c56514..c24ae573a9514 100644 --- a/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts +++ b/x-pack/plugins/ml/public/application/services/results_service/result_service_rx.ts @@ -16,6 +16,7 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { each, get } from 'lodash'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { ErrorType } from '@kbn/ml-error-utils'; import { Dictionary } from '../../../../common/types/common'; import { ML_MEDIAN_PERCENTS } from '../../../../common/util/job_utils'; import { Datafeed, JobId } from '../../../../common/types/anomaly_detection_jobs'; @@ -28,7 +29,6 @@ import { ES_AGGREGATION } from '../../../../common/constants/aggregation_types'; import { InfluencersFilterQuery } from '../../../../common/types/es_client'; import { RecordForInfluencer } from './results_service'; import { isRuntimeMappings } from '../../../../common'; -import { ErrorType } from '../../../../common/util/errors'; export interface ResultResponse { success: boolean; diff --git a/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts b/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts index 8bc7bc8c87e35..585b881010d7a 100644 --- a/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts +++ b/x-pack/plugins/ml/public/application/services/toast_notification_service/toast_notification_service.ts @@ -8,13 +8,9 @@ import { i18n } from '@kbn/i18n'; import { ToastInput, ToastOptions, ToastsStart } from '@kbn/core/public'; import { useMemo } from 'react'; +import { extractErrorProperties, type ErrorType, MLRequestFailure } from '@kbn/ml-error-utils'; import { getToastNotifications } from '../../util/dependency_cache'; import { useNotifications } from '../../contexts/kibana'; -import { - ErrorType, - extractErrorProperties, - MLRequestFailure, -} from '../../../../common/util/errors'; export type ToastNotificationService = ReturnType; diff --git a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js index e97cf8e2639fc..a767d7b65e4f3 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/list/delete_calendars.js @@ -5,10 +5,11 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { getToastNotifications } from '../../../util/dependency_cache'; import { ml } from '../../../services/ml_api_service'; -import { i18n } from '@kbn/i18n'; -import { extractErrorMessage } from '../../../../../common/util/errors'; export async function deleteCalendars(calendarsToDelete, callback) { if (calendarsToDelete === undefined || calendarsToDelete.length === 0) { diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js index 64bc1eefccd79..3342680070e8b 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasting_modal.js @@ -16,9 +16,13 @@ import React, { Component } from 'react'; import { EuiButton, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { withKibana } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { FORECAST_REQUEST_STATE, JOB_STATE } from '../../../../../common/constants/states'; import { MESSAGE_LEVEL } from '../../../../../common/constants/message_levels'; -import { extractErrorMessage } from '../../../../../common/util/errors'; import { isJobVersionGte } from '../../../../../common/util/job_utils'; import { parseInterval } from '../../../../../common/util/parse_interval'; import { Modal } from './modal'; @@ -26,9 +30,6 @@ import { PROGRESS_STATES } from './progress_states'; import { ml } from '../../../services/ml_api_service'; import { mlJobService } from '../../../services/job_service'; import { mlForecastService } from '../../../services/forecast_service'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { withKibana } from '@kbn/kibana-react-plugin/public'; export const FORECAST_DURATION_MAX_DAYS = 3650; // Max forecast duration allowed by analytics. diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx index c9ca8250bedd9..af42229d8ac79 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_with_tooltip.tsx @@ -7,11 +7,11 @@ import React, { FC, useEffect, useState, useCallback, useContext } from 'react'; import { i18n } from '@kbn/i18n'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { MlTooltipComponent } from '../../../components/chart_tooltip'; import { TimeseriesChart } from './timeseries_chart'; import { CombinedJob } from '../../../../../common/types/anomaly_detection_jobs'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../../common/constants/search'; -import { extractErrorMessage } from '../../../../../common/util/errors'; import { Annotation } from '../../../../../common/types/annotations'; import { useMlKibana, useNotifications } from '../../../contexts/kibana'; import { getBoundsRoundedToInterval } from '../../../util/time_buckets'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts index f9dd1fa94c4f0..7a7837ba71435 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/get_focus_data.ts @@ -7,9 +7,9 @@ import { forkJoin, Observable, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { ml } from '../../services/ml_api_service'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../common/constants/search'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { mlTimeSeriesSearchService } from '../timeseries_search_service'; import { mlResultsService, CriteriaField } from '../../services/results_service'; import { Job } from '../../../../common/types/anomaly_detection_jobs'; diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx index 7585ffe32118d..e2fff3bd286cf 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/common/job_details.tsx @@ -5,14 +5,10 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import React, { FC, useState, useCallback } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import useDebounce from 'react-use/lib/useDebounce'; -import type { Embeddable } from '@kbn/lens-plugin/public'; -import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiFlexGroup, EuiFlexItem, @@ -30,11 +26,16 @@ import { EuiCallOut, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { Embeddable } from '@kbn/lens-plugin/public'; +import type { MapEmbeddable } from '@kbn/maps-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; + import { QuickLensJobCreator } from '../../../application/jobs/new_job/job_from_lens'; import type { LayerResult } from '../../../application/jobs/new_job/job_from_lens'; import type { CreateState } from '../../../application/jobs/new_job/job_from_dashboard'; import { JOB_TYPE, DEFAULT_BUCKET_SPAN } from '../../../../common/constants/new_job'; -import { extractErrorMessage } from '../../../../common/util/errors'; import { basicJobValidation } from '../../../../common/util/job_utils'; import { JOB_ID_MAX_LENGTH } from '../../../../common/constants/validation'; import { invalidTimeIntervalMessage } from '../../../application/jobs/new_job/common/job_validator/util'; diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx index f012e928e6b61..5f804e209eaa3 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/incompatible_layer.tsx @@ -6,13 +6,13 @@ */ import React, { FC } from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; -import type { LayerResult } from '../../../../../application/jobs/new_job/job_from_lens'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; -import { extractErrorMessage } from '../../../../../../common/util/errors'; +import type { LayerResult } from '../../../../../application/jobs/new_job/job_from_lens'; interface Props { layer: LayerResult; diff --git a/x-pack/plugins/ml/public/shared.ts b/x-pack/plugins/ml/public/shared.ts index f247be1004718..1d19fee5c8392 100644 --- a/x-pack/plugins/ml/public/shared.ts +++ b/x-pack/plugins/ml/public/shared.ts @@ -15,7 +15,6 @@ export * from '../common/types/modules'; export * from '../common/types/audit_message'; export * from '../common/util/anomaly_utils'; -export * from '../common/util/errors'; export * from '../common/util/validators'; export * from '../common/util/date_utils'; diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts index 6d4982ecbf464..9d286738b17da 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { IScopedClusterClient } from '@kbn/core/server'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { getAnalysisType } from '../../../common/util/analytics_utils'; import { ANALYSIS_CONFIG_TYPE } from '../../../common/constants/data_frame_analytics'; import { @@ -25,7 +26,6 @@ import { isRegressionAnalysis, isClassificationAnalysis, } from '../../../common/util/analytics_utils'; -import { extractErrorMessage } from '../../../common/util/errors'; import { AnalysisConfig, DataFrameAnalyticsConfig, diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index 6179571a28515..8288c9998fc7d 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -30,7 +30,6 @@ "@kbn/charts-plugin", "@kbn/cloud-plugin", "@kbn/config-schema", - "@kbn/core-http-browser", "@kbn/dashboard-plugin", "@kbn/data-plugin", "@kbn/data-views-plugin", @@ -90,5 +89,6 @@ "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", + "@kbn/ml-error-utils", ], } diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts index 18ce74d9823bb..ae069537e1b26 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts +++ b/x-pack/plugins/synthetics/public/legacy_uptime/state/api/ml_anomaly.ts @@ -11,7 +11,7 @@ import { JobExistResult, MlCapabilitiesResponse, } from '@kbn/ml-plugin/public'; -import { extractErrorMessage } from '@kbn/ml-plugin/common'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import { apiService } from './utils'; import { AnomalyRecords, AnomalyRecordsParams } from '../actions'; import { API_URLS, ML_MODULE_ID } from '../../../../common/constants'; diff --git a/x-pack/plugins/synthetics/tsconfig.json b/x-pack/plugins/synthetics/tsconfig.json index a02e14d577b35..61b7fe8d8d19c 100644 --- a/x-pack/plugins/synthetics/tsconfig.json +++ b/x-pack/plugins/synthetics/tsconfig.json @@ -78,6 +78,7 @@ "@kbn/alerts-as-data-utils", "@kbn/exploratory-view-plugin", "@kbn/observability-shared-plugin", + "@kbn/ml-error-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx index f90faf53e87b5..25318fc9e2903 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_delete_transform.tsx @@ -8,6 +8,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { extractErrorMessage } from '@kbn/ml-error-utils'; import type { DeleteTransformStatus, DeleteTransformsRequestSchema, @@ -24,7 +25,6 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { const { http, data: { dataViews: dataViewsContract }, - ml: { extractErrorMessage }, application: { capabilities }, } = useAppDependencies(); const toastNotifications = useToastNotifications(); @@ -62,7 +62,7 @@ export const useDeleteIndexAndTargetIndex = (items: TransformListRow[]) => { ); } }, - [dataViewsContract, toastNotifications, extractErrorMessage] + [dataViewsContract, toastNotifications] ); const checkUserIndexPermission = useCallback(async () => { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx index c23d6ed475efc..e0a52978c0b4a 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/source_search_bar/source_search_bar.tsx @@ -25,8 +25,8 @@ interface SourceSearchBarProps { } export const SourceSearchBar: FC = ({ dataView, searchBar }) => { const { - actions: { searchChangeHandler, searchSubmitHandler, setErrorMessage }, - state: { errorMessage, searchInput }, + actions: { searchChangeHandler, searchSubmitHandler, setQueryErrorMessage }, + state: { queryErrorMessage, searchInput }, } = searchBar; const { @@ -44,7 +44,7 @@ export const SourceSearchBar: FC = ({ dataView, searchBar return ( setErrorMessage(undefined)} + closePopover={() => setQueryErrorMessage(undefined)} input={ = ({ dataView, searchBar }} /> } - isOpen={errorMessage?.query === searchInput.query && errorMessage?.message !== ''} + isOpen={queryErrorMessage?.query === searchInput.query && queryErrorMessage?.message !== ''} > {i18n.translate('xpack.transform.stepDefineForm.invalidKuerySyntaxErrorMessageQueryBar', { - defaultMessage: 'Invalid query: {errorMessage}', + defaultMessage: 'Invalid query: {queryErrorMessage}', values: { - errorMessage: errorMessage?.message.split('\n')[0], + queryErrorMessage: queryErrorMessage?.message.split('\n')[0], }, })} diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts index 775401decef35..157ab0051e631 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/index.ts @@ -13,4 +13,4 @@ export { getDefaultAggregationConfig } from './get_default_aggregation_config'; export { getDefaultGroupByConfig } from './get_default_group_by_config'; export { getDefaultStepDefineState } from './get_default_step_define_state'; export { getPivotDropdownOptions } from './get_pivot_dropdown_options'; -export type { ErrorMessage, Field, StepDefineExposedState } from './types'; +export type { Field, StepDefineExposedState } from './types'; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts index 682001a937381..2e66b2187a9e0 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts @@ -28,11 +28,6 @@ import { import { LatestFunctionConfig } from '../../../../../../../common/api_schemas/transforms'; import { RUNTIME_FIELD_TYPES } from '../../../../../../../common/shared_imports'; -export interface ErrorMessage { - query: string; - message: string; -} - export interface Field { name: EsFieldName; type: KBN_FIELD_TYPES | TIME_SERIES_METRIC_TYPES.COUNTER; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts index e8d56fc002981..62f8661f0ca49 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_search_bar.ts @@ -9,11 +9,11 @@ import { useState } from 'react'; import { toElasticsearchQuery, fromKueryExpression, luceneStringToDsl } from '@kbn/es-query'; import type { Query } from '@kbn/es-query'; +import type { QueryErrorMessage } from '@kbn/ml-error-utils'; import { getTransformConfigQuery } from '../../../../../common'; import { - ErrorMessage, StepDefineExposedState, QUERY_LANGUAGE_KUERY, QUERY_LANGUAGE_LUCENE, @@ -43,7 +43,9 @@ export const useSearchBar = ( const [searchQuery, setSearchQuery] = useState(defaults.searchQuery); - const [errorMessage, setErrorMessage] = useState(undefined); + const [queryErrorMessage, setQueryErrorMessage] = useState( + undefined + ); const searchChangeHandler = (query: Query) => setSearchInput(query); const searchSubmitHandler = (query: Query) => { @@ -61,7 +63,7 @@ export const useSearchBar = ( return; } } catch (e) { - setErrorMessage({ query: query.query as string, message: e.message }); + setQueryErrorMessage({ query: query.query as string, message: e.message }); } }; @@ -71,14 +73,14 @@ export const useSearchBar = ( actions: { searchChangeHandler, searchSubmitHandler, - setErrorMessage, + setQueryErrorMessage, setSearchInput, setSearchLanguage, setSearchQuery, setSearchString, }, state: { - errorMessage, + queryErrorMessage, transformConfigQuery, searchInput, searchLanguage, diff --git a/x-pack/plugins/transform/server/routes/api/error_utils.ts b/x-pack/plugins/transform/server/routes/api/error_utils.ts index 6f02b1978506f..9054ee7aa3b23 100644 --- a/x-pack/plugins/transform/server/routes/api/error_utils.ts +++ b/x-pack/plugins/transform/server/routes/api/error_utils.ts @@ -142,7 +142,7 @@ export function wrapEsError(err: any, statusCodeToMessageMap: Record Date: Sun, 23 Apr 2023 11:51:44 +0300 Subject: [PATCH 23/29] [Cloud Posture] Dashboard initial FTR (#155163) --- ...e_chart.tsx => compliance_score_chart.tsx} | 14 +- .../compliance_dashboard.tsx | 2 + .../dashboard_sections/benchmarks_section.tsx | 4 +- .../dashboard_sections/summary_section.tsx | 7 +- .../compliance_dashboard/test_subjects.ts | 7 + .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../page_objects/csp_dashboard_page.ts | 134 ++++++++++++++++++ .../page_objects/index.ts | 2 + .../pages/compliance_dashboard.ts | 64 +++++++++ .../pages/index.ts | 1 + 12 files changed, 224 insertions(+), 17 deletions(-) rename x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/{cloud_posture_score_chart.tsx => compliance_score_chart.tsx} (91%) create mode 100644 x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts create mode 100644 x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx similarity index 91% rename from x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx rename to x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx index c1b86258a46c9..c15e0ce87570f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx @@ -29,13 +29,14 @@ import { import { FormattedDate, FormattedTime } from '@kbn/i18n-react'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; +import { DASHBOARD_COMPLIANCE_SCORE_CHART } from '../test_subjects'; import { statusColors } from '../../../common/constants'; import { RULE_FAILED, RULE_PASSED } from '../../../../common/constants'; import { CompactFormattedNumber } from '../../../components/compact_formatted_number'; import type { Evaluation, PostureTrend, Stats } from '../../../../common/types'; import { useKibana } from '../../../common/hooks/use_kibana'; -interface CloudPostureScoreChartProps { +interface ComplianceScoreChartProps { compact?: boolean; trend: PostureTrend[]; data: Stats; @@ -48,7 +49,7 @@ const getPostureScorePercentage = (postureScore: number): string => `${Math.roun const PercentageInfo = ({ compact, postureScore, -}: CloudPostureScoreChartProps['data'] & { compact?: CloudPostureScoreChartProps['compact'] }) => { +}: ComplianceScoreChartProps['data'] & { compact?: ComplianceScoreChartProps['compact'] }) => { const { euiTheme } = useEuiTheme(); const percentage = getPostureScorePercentage(postureScore); @@ -59,6 +60,7 @@ const PercentageInfo = ({ paddingLeft: compact ? euiTheme.size.s : euiTheme.size.xs, marginBottom: compact ? euiTheme.size.s : 'none', }} + data-test-subj={DASHBOARD_COMPLIANCE_SCORE_CHART.COMPLIANCE_SCORE} >

{percentage}

@@ -140,12 +142,12 @@ const CounterLink = ({ ); }; -export const CloudPostureScoreChart = ({ +export const ComplianceScoreChart = ({ data, trend, onEvalCounterClick, compact, -}: CloudPostureScoreChartProps) => { +}: ComplianceScoreChartProps) => { const { euiTheme } = useEuiTheme(); return ( @@ -173,7 +175,7 @@ export const CloudPostureScoreChart = ({ color={statusColors.passed} onClick={() => onEvalCounterClick(RULE_PASSED)} tooltipContent={i18n.translate( - 'xpack.csp.cloudPostureScoreChart.counterLink.passedFindingsTooltip', + 'xpack.csp.complianceScoreChart.counterLink.passedFindingsTooltip', { defaultMessage: 'Passed findings' } )} /> @@ -184,7 +186,7 @@ export const CloudPostureScoreChart = ({ color={statusColors.failed} onClick={() => onEvalCounterClick(RULE_FAILED)} tooltipContent={i18n.translate( - 'xpack.csp.cloudPostureScoreChart.counterLink.failedFindingsTooltip', + 'xpack.csp.complianceScoreChart.counterLink.failedFindingsTooltip', { defaultMessage: 'Failed findings' } )} /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx index 6c75891fc62af..fe4f62dbbc8f5 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx @@ -31,6 +31,7 @@ import { KUBERNETES_DASHBOARD_CONTAINER, KUBERNETES_DASHBOARD_TAB, CLOUD_DASHBOARD_TAB, + CLOUD_POSTURE_DASHBOARD_PAGE_HEADER, } from './test_subjects'; import { useCspmStatsApi, useKspmStatsApi } from '../../common/api/use_stats_api'; import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; @@ -332,6 +333,7 @@ export const ComplianceDashboard = () => { return ( - @@ -151,7 +152,7 @@ export const SummarySection = ({ - => + retry.try(async () => { + log.debug('Check CSP plugin is initialized'); + const response = await supertest + .get('/internal/cloud_security_posture/status?check=init') + .expect(200); + expect(response.body).to.eql({ isPluginInitialized: true }); + log.debug('CSP plugin is initialized'); + }); + + const index = { + remove: () => es.indices.delete({ index: LATEST_FINDINGS_INDEX, ignore_unavailable: true }), + add: async (findingsMock: T[]) => { + await Promise.all( + findingsMock.map((finding) => + es.index({ + index: LATEST_FINDINGS_INDEX, + body: finding, + }) + ) + ); + }, + }; + + const dashboard = { + getDashboardPageHeader: () => testSubjects.find('cloud-posture-dashboard-page-header'), + + getDashboardTabs: async () => { + const dashboardPageHeader = await dashboard.getDashboardPageHeader(); + return await dashboardPageHeader.findByClassName('euiTabs'); + }, + + getCloudTab: async () => { + const tabs = await dashboard.getDashboardTabs(); + return await tabs.findByXpath(`//span[text()="Cloud"]`); + }, + + getKubernetesTab: async () => { + const tabs = await dashboard.getDashboardTabs(); + return await tabs.findByXpath(`//span[text()="Kubernetes"]`); + }, + + clickTab: async (tab: 'Cloud' | 'Kubernetes') => { + if (tab === 'Cloud') { + const cloudTab = await dashboard.getCloudTab(); + await cloudTab.click(); + } + if (tab === 'Kubernetes') { + const k8sTab = await dashboard.getKubernetesTab(); + await k8sTab.click(); + } + }, + + getIntegrationDashboardContainer: () => testSubjects.find('dashboard-container'), + + // Cloud Dashboard + + getCloudDashboard: async () => { + await dashboard.clickTab('Cloud'); + return await testSubjects.find('cloud-dashboard-container'); + }, + + getCloudSummarySection: async () => { + await dashboard.getCloudDashboard(); + return await testSubjects.find('dashboard-summary-section'); + }, + + getCloudComplianceScore: async () => { + await dashboard.getCloudSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + + // Kubernetes Dashboard + + getKubernetesDashboard: async () => { + await dashboard.clickTab('Kubernetes'); + return await testSubjects.find('kubernetes-dashboard-container'); + }, + + getKubernetesSummarySection: async () => { + await dashboard.getKubernetesDashboard(); + return await testSubjects.find('dashboard-summary-section'); + }, + + getKubernetesComplianceScore: async () => { + await dashboard.getKubernetesSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + + getKubernetesComplianceScore2: async () => { + // await dashboard.getKubernetesSummarySection(); + return await testSubjects.find('dashboard-summary-section-compliance-score'); + }, + }; + + const navigateToComplianceDashboardPage = async () => { + await PageObjects.common.navigateToUrl( + 'securitySolution', // Defined in Security Solution plugin + 'cloud_security_posture/dashboard', + { shouldUseHashForSubUrl: false } + ); + }; + + return { + waitForPluginInitialized, + navigateToComplianceDashboardPage, + dashboard, + index, + }; +} diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/index.ts b/x-pack/test/cloud_security_posture_functional/page_objects/index.ts index e5738873edc51..26aacd8cca997 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/index.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/index.ts @@ -7,8 +7,10 @@ import { pageObjects as xpackFunctionalPageObjects } from '../../functional/page_objects'; import { FindingsPageProvider } from './findings_page'; +import { CspDashboardPageProvider } from './csp_dashboard_page'; export const pageObjects = { ...xpackFunctionalPageObjects, findings: FindingsPageProvider, + cloudPostureDashboard: CspDashboardPageProvider, }; diff --git a/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts b/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts new file mode 100644 index 0000000000000..cb4635899f5c3 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/pages/compliance_dashboard.ts @@ -0,0 +1,64 @@ +/* + * 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 Chance from 'chance'; +import type { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const retry = getService('retry'); + const pageObjects = getPageObjects(['common', 'cloudPostureDashboard']); + const chance = new Chance(); + + const data = [ + { + '@timestamp': new Date().toISOString(), + resource: { id: chance.guid(), name: `kubelet`, sub_type: 'lower case sub type' }, + result: { evaluation: 'failed' }, + rule: { + name: 'Upper case rule name', + section: 'Upper case section', + benchmark: { + id: 'cis_k8s', + posture_type: 'kspm', + }, + }, + cluster_id: 'Upper case cluster id', + }, + ]; + + describe('Cloud Posture Dashboard Page', () => { + let cspDashboard: typeof pageObjects.cloudPostureDashboard; + let dashboard: typeof pageObjects.cloudPostureDashboard.dashboard; + + before(async () => { + cspDashboard = pageObjects.cloudPostureDashboard; + dashboard = pageObjects.cloudPostureDashboard.dashboard; + await cspDashboard.waitForPluginInitialized(); + + await cspDashboard.index.add(data); + await cspDashboard.navigateToComplianceDashboardPage(); + await retry.waitFor( + 'Cloud posture integration dashboard to be displayed', + async () => !!dashboard.getIntegrationDashboardContainer() + ); + }); + + after(async () => { + await cspDashboard.index.remove(); + }); + + describe('Kubernetes Dashboard', () => { + it('displays accurate summary compliance score', async () => { + const scoreElement = await dashboard.getKubernetesComplianceScore(); + + expect((await scoreElement.getVisibleText()) === '0%').to.be(true); + }); + }); + }); +} diff --git a/x-pack/test/cloud_security_posture_functional/pages/index.ts b/x-pack/test/cloud_security_posture_functional/pages/index.ts index 80e96b8b17ce9..7566afda0501a 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/index.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/index.ts @@ -11,5 +11,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Cloud Security Posture', function () { loadTestFile(require.resolve('./findings')); + loadTestFile(require.resolve('./compliance_dashboard')); }); } From 68719bdb07d5307d567489b2b65a3aa8d59e630c Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Sun, 23 Apr 2023 10:53:48 +0200 Subject: [PATCH 24/29] [RAM][SECURITYSOLUTION][ALERTS] - Integrate Alert summary inside of security solution rule page (#154990) ## Summary [Main ticket](https://github.com/elastic/kibana/issues/151916) This PR dependant on [these changes](https://github.com/elastic/kibana/pull/153101) These changes cover next two tickets: - [RAM][SECURITYSOLUTION][ALERTS] - Integrate per-action frequency field in security solution APIs #154532 - [RAM][SECURITYSOLUTION][ALERTS] - Integrate per-action frequency UI in security solution #154534 With this PR we will integrate per-action `frequency` field which already works within alert framework and will update security solution UI to incorporate the possibility to select "summary" vs "for each alert" type of actions. ![](https://user-images.githubusercontent.com/616158/227377473-f34a330e-81ce-42b4-af1b-e6e302c6319d.png) ## NOTES: - There will be no more "Perform no actions" option which mutes all the actions of the rule. For back compatibility, we need to show that rule is muted in the UI cc @peluja1012 @ARWNightingale - The ability to generate per-alert action will be done as part of https://github.com/elastic/kibana/issues/153611 ## Technical Notes: Here are the overview of the conversions and transformations that we are going to do as part of these changes for devs who are going to review. On rule **create**/**read**/**update**/**patch**: - We always gonna set rule level `throttle` to `undefined` from now on - If each action has `frequency` attribute set, then we just use those values - If actions do not have `frequency` attribute set (or for some reason there is a mix of actions with some of them having `frequency` attribute and some not), then we transform rule level `throttle` into `frequency` and set it for each action in the rule On rule **bulk editing**: - We always gonna set rule level `throttle` to `undefined` - If each action has `frequency` attribute set, then we just use those values - If actions do not have `frequency` attribute set, then we transform rule level `throttle` into `frequency` and set it for each action in the rule - If user passed only `throttle` attribute with empty actions (`actions = []`), this will only remove all actions from the rule This will bring breaking changes which we agreed on with the Advanced Correlation Group cc @XavierM @vitaliidm @peluja1012 ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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> Co-authored-by: Maxim Palenov --- .../index.ts | 1 + .../src/actions/index.ts | 13 +- .../src/frequency/index.ts | 37 ++++ .../src/throttle/index.ts | 2 +- .../alerting/server/routes/bulk_edit_rules.ts | 11 + .../format_legacy_actions.ts | 5 +- .../transform_legacy_actions.test.ts | 2 +- .../transform_legacy_actions.ts | 5 +- .../transform_to_alert_throttle.test.ts | 21 ++ .../transform_to_alert_throttle.ts | 20 ++ .../server/rules_client/methods/bulk_edit.ts | 15 +- .../server/rules_client/methods/create.ts | 13 -- .../server/rules_client/methods/update.ts | 14 +- .../security_solution/common/constants.ts | 11 + .../rules/bulk_actions/request_schema.test.ts | 22 -- .../api/rules/bulk_actions/request_schema.ts | 24 ++- .../rule_schema/model/rule_schemas.ts | 3 +- .../detection_engine/transform_actions.ts | 4 + .../bulk_edit_rules_actions.cy.ts | 47 +++-- .../detection_rules/custom_query_rule.cy.ts | 12 +- .../e2e/detection_rules/rule_actions.cy.ts | 2 +- .../security_solution/cypress/objects/rule.ts | 1 - .../cypress/objects/types.ts | 3 - .../cypress/screens/common/rule_actions.ts | 17 ++ .../cypress/screens/create_new_rule.ts | 3 - .../cypress/screens/rules_bulk_edit.ts | 3 - .../cypress/tasks/common/rule_actions.ts | 85 ++++++++ .../cypress/tasks/create_new_rule.ts | 2 - .../cypress/tasks/edit_rule.ts | 5 - .../cypress/tasks/rules_bulk_edit.ts | 5 - .../pages/rule_creation/helpers.test.ts | 86 -------- .../pages/rule_creation/helpers.ts | 10 +- .../components/rules_table/__mocks__/mock.ts | 1 - .../bulk_actions/forms/rule_actions_form.tsx | 48 +---- .../rules_table/bulk_actions/translations.tsx | 15 -- .../bulk_actions/use_bulk_actions.tsx | 11 - .../utils/compute_dry_run_edit_payload.ts | 2 +- .../rules/rule_actions_field/index.tsx | 31 ++- .../rules/step_rule_actions/get_schema.ts | 10 - .../rules/step_rule_actions/index.tsx | 93 ++------- .../rules/step_rule_actions/translations.tsx | 16 -- .../use_manage_case_action.tsx | 66 ------ .../detection_engine/rules/helpers.test.tsx | 30 ++- .../pages/detection_engine/rules/helpers.tsx | 3 +- .../pages/detection_engine/rules/types.ts | 1 - .../routes/__mocks__/utils.ts | 2 +- .../logic/actions/duplicate_rule.test.ts | 2 - .../logic/actions/duplicate_rule.ts | 6 +- .../action_to_rules_client_operation.ts | 25 +-- .../logic/crud/patch_rules.test.ts | 2 + .../logic/crud/update_rules.ts | 9 +- .../logic/export/get_export_all.test.ts | 4 +- .../export/get_export_by_object_ids.test.ts | 6 +- .../normalization/rule_actions.test.ts | 151 ++++++++++++++ .../normalization/rule_actions.ts | 34 ++- .../normalization/rule_converters.ts | 39 ++-- .../rule_management/utils/validate.test.ts | 2 +- .../rule_schema/model/rule_schemas.ts | 12 +- .../translations/translations/fr-FR.json | 6 - .../translations/translations/ja-JP.json | 6 - .../translations/translations/zh-CN.json | 6 - .../basic/tests/create_rules.ts | 1 - .../group1/create_rules.ts | 149 ++++++++++++- .../group1/create_rules_bulk.ts | 145 +++++++++++++ .../group1/delete_rules.ts | 1 + .../group1/delete_rules_bulk.ts | 3 + .../group1/export_rules.ts | 47 +++-- .../security_and_spaces/group1/find_rules.ts | 20 +- .../group10/legacy_actions_migrations.ts | 26 ++- .../group10/patch_rules.ts | 180 +++++++++++++++- .../group10/patch_rules_bulk.ts | 2 +- .../group10/perform_bulk_action.ts | 83 +++++--- .../security_and_spaces/group10/read_rules.ts | 20 +- .../security_and_spaces/group10/throttle.ts | 66 +++--- .../group10/update_rules.ts | 195 +++++++++++++++++- .../group10/update_rules_bulk.ts | 195 +++++++++++++++++- .../utils/get_complex_rule_output.ts | 1 - .../utils/get_rule_actions.ts | 96 +++++++++ .../utils/get_simple_rule_output.ts | 2 +- ...simple_rule_output_with_web_hook_action.ts | 3 +- .../utils/remove_uuid_from_actions.ts | 14 ++ 81 files changed, 1720 insertions(+), 672 deletions(-) create mode 100644 packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.ts create mode 100644 x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.ts delete mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx create mode 100644 x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts create mode 100644 x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/index.ts index 7a200e4f4c8f9..a1d34a9f94451 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/index.ts @@ -20,6 +20,7 @@ export * from './src/default_severity_mapping_array'; export * from './src/default_threat_array'; export * from './src/default_to_string'; export * from './src/default_uuid'; +export * from './src/frequency'; export * from './src/language'; export * from './src/machine_learning_job_id'; export * from './src/max_signals'; diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts index ab6df3bdacc2a..1ca2f7fceba40 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts @@ -9,6 +9,7 @@ import { NonEmptyString } from '@kbn/securitysolution-io-ts-types'; import * as t from 'io-ts'; import { saved_object_attributes } from '../saved_object_attributes'; +import { RuleActionFrequency } from '../frequency'; export type RuleActionGroup = t.TypeOf; export const RuleActionGroup = t.string; @@ -94,7 +95,11 @@ export const RuleAction = t.exact( action_type_id: RuleActionTypeId, params: RuleActionParams, }), - t.partial({ uuid: RuleActionUuid, alerts_filter: RuleActionAlertsFilter }), + t.partial({ + uuid: RuleActionUuid, + alerts_filter: RuleActionAlertsFilter, + frequency: RuleActionFrequency, + }), ]) ); @@ -110,7 +115,11 @@ export const RuleActionCamel = t.exact( actionTypeId: RuleActionTypeId, params: RuleActionParams, }), - t.partial({ uuid: RuleActionUuid, alertsFilter: RuleActionAlertsFilter }), + t.partial({ + uuid: RuleActionUuid, + alertsFilter: RuleActionAlertsFilter, + frequency: RuleActionFrequency, + }), ]) ); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts new file mode 100644 index 0000000000000..ba6709d1ff495 --- /dev/null +++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts @@ -0,0 +1,37 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import * as t from 'io-ts'; + +import { RuleActionThrottle } from '../throttle'; + +/** + * Action summary indicates whether we will send a summary notification about all the generate alerts or notification per individual alert + */ +export type RuleActionSummary = t.TypeOf; +export const RuleActionSummary = t.boolean; + +/** + * The condition for throttling the notification: `onActionGroupChange`, `onActiveAlert`, or `onThrottleInterval` + */ +export type RuleActionNotifyWhen = t.TypeOf; +export const RuleActionNotifyWhen = t.union([ + t.literal('onActionGroupChange'), + t.literal('onActiveAlert'), + t.literal('onThrottleInterval'), +]); + +/** + * The action frequency defines when the action runs (for example, only on rule execution or at specific time intervals). + */ +export type RuleActionFrequency = t.TypeOf; +export const RuleActionFrequency = t.type({ + summary: RuleActionSummary, + notifyWhen: RuleActionNotifyWhen, + throttle: t.union([RuleActionThrottle, t.null]), +}); diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts b/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts index fbc75ca67693e..c0fb60482a91c 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts +++ b/packages/kbn-securitysolution-io-ts-alerting-types/src/throttle/index.ts @@ -13,5 +13,5 @@ export type RuleActionThrottle = t.TypeOf; export const RuleActionThrottle = t.union([ t.literal('no_actions'), t.literal('rule'), - TimeDuration({ allowedUnits: ['h', 'd'] }), + TimeDuration({ allowedUnits: ['s', 'm', 'h', 'd'] }), ]); diff --git a/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts b/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts index fa30b0ff8d2ed..f22d7d2055b09 100644 --- a/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts +++ b/x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts @@ -19,6 +19,17 @@ const ruleActionSchema = schema.object({ id: schema.string(), params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), uuid: schema.maybe(schema.string()), + frequency: schema.maybe( + schema.object({ + summary: schema.boolean(), + throttle: schema.nullable(schema.string()), + notifyWhen: schema.oneOf([ + schema.literal('onActionGroupChange'), + schema.literal('onActiveAlert'), + schema.literal('onThrottleInterval'), + ]), + }) + ), }); const operationsSchema = schema.arrayOf( diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts index 8689113ca7907..a0aa3286f1f6d 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts @@ -14,6 +14,7 @@ import { injectReferencesIntoActions } from '../../common'; import { transformToNotifyWhen } from './transform_to_notify_when'; import { transformFromLegacyActions } from './transform_legacy_actions'; import { LegacyIRuleActionsAttributes, legacyRuleActionsSavedObjectType } from './types'; +import { transformToAlertThrottle } from './transform_to_alert_throttle'; /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function @@ -136,7 +137,9 @@ export const formatLegacyActions = async ( return { ...rule, actions: [...rule.actions, ...legacyRuleActions], - throttle: (legacyRuleActions.length ? ruleThrottle : rule.throttle) ?? 'no_actions', + throttle: transformToAlertThrottle( + (legacyRuleActions.length ? ruleThrottle : rule.throttle) ?? 'no_actions' + ), notifyWhen: transformToNotifyWhen(ruleThrottle), // muteAll property is disregarded in further rule processing in Security Solution when legacy actions are present. // So it should be safe to set it as false, so it won't be displayed to user as w/o actions see transformFromAlertThrottle method diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts index b23798294b300..cea32ce3e1913 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts @@ -67,7 +67,7 @@ describe('transformFromLegacyActions', () => { (transformToNotifyWhen as jest.Mock).mockReturnValueOnce(null); const actions = transformFromLegacyActions(legacyActionsAttr, references); - expect(actions[0].frequency?.notifyWhen).toBe('onThrottleInterval'); + expect(actions[0].frequency?.notifyWhen).toBe('onActiveAlert'); }); it('should return transformed legacy actions', () => { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts index 485a32f781695..1218e96a8dfd7 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.ts @@ -11,6 +11,7 @@ import type { SavedObjectReference } from '@kbn/core/server'; import { RawRuleAction } from '../../../types'; import { transformToNotifyWhen } from './transform_to_notify_when'; import { LegacyIRuleActionsAttributes } from './types'; +import { transformToAlertThrottle } from './transform_to_alert_throttle'; /** * @deprecated Once we are confident all rules relying on side-car actions SO's have been migrated to SO references we should remove this function @@ -50,8 +51,8 @@ export const transformFromLegacyActions = ( actionTypeId, frequency: { summary: true, - notifyWhen: transformToNotifyWhen(legacyActionsAttr.ruleThrottle) ?? 'onThrottleInterval', - throttle: legacyActionsAttr.ruleThrottle, + notifyWhen: transformToNotifyWhen(legacyActionsAttr.ruleThrottle) ?? 'onActiveAlert', + throttle: transformToAlertThrottle(legacyActionsAttr.ruleThrottle), }, }, ]; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.ts new file mode 100644 index 0000000000000..f52742009503a --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.test.ts @@ -0,0 +1,21 @@ +/* + * 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 { transformToAlertThrottle } from './transform_to_alert_throttle'; + +describe('transformToAlertThrottle', () => { + it('should return null when throttle is null OR no_actions', () => { + expect(transformToAlertThrottle(null)).toBeNull(); + expect(transformToAlertThrottle('rule')).toBeNull(); + expect(transformToAlertThrottle('no_actions')).toBeNull(); + }); + it('should return same value for other throttle values', () => { + expect(transformToAlertThrottle('1h')).toBe('1h'); + expect(transformToAlertThrottle('1m')).toBe('1m'); + expect(transformToAlertThrottle('1d')).toBe('1d'); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.ts new file mode 100644 index 0000000000000..7f6ecee900e2f --- /dev/null +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_to_alert_throttle.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Given a throttle from a "security_solution" rule this will transform it into an "alerting" "throttle" + * on their saved object. + * @params throttle The throttle from a "security_solution" rule + * @returns The "alerting" throttle + */ +export const transformToAlertThrottle = (throttle: string | null | undefined): string | null => { + if (throttle == null || throttle === 'rule' || throttle === 'no_actions') { + return null; + } else { + return throttle; + } +}; diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts index ce11a0cc017e3..640f3c3f324b3 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_edit.ts @@ -7,7 +7,7 @@ import pMap from 'p-map'; import Boom from '@hapi/boom'; -import { cloneDeep, omit } from 'lodash'; +import { cloneDeep } from 'lodash'; import { AlertConsumers } from '@kbn/rule-data-utils'; import { KueryNode, nodeBuilder } from '@kbn/es-query'; import { @@ -638,19 +638,6 @@ async function getUpdatedAttributesFromOperations( isAttributesUpdateSkipped = false; } - // TODO https://github.com/elastic/kibana/issues/148414 - // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = updatedOperation.value.find( - (action) => action?.frequency - )?.frequency; - if (rule.attributes.consumer === AlertConsumers.SIEM && firstFrequency) { - ruleActions.actions = ruleActions.actions.map((action) => omit(action, 'frequency')); - if (!attributes.notifyWhen) { - attributes.notifyWhen = firstFrequency.notifyWhen; - attributes.throttle = firstFrequency.throttle; - } - } - break; } case 'snoozeSchedule': { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/create.ts b/x-pack/plugins/alerting/server/rules_client/methods/create.ts index 90d6a4c49f90a..fd83a7b9b92e1 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/create.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/create.ts @@ -6,8 +6,6 @@ */ import Semver from 'semver'; import Boom from '@hapi/boom'; -import { omit } from 'lodash'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import { SavedObjectsUtils } from '@kbn/core/server'; import { withSpan } from '@kbn/apm-utils'; import { parseDuration } from '../../../common/parse_duration'; @@ -111,17 +109,6 @@ export async function create( throw Boom.badRequest(`Error creating rule: could not create API key - ${error.message}`); } - // TODO https://github.com/elastic/kibana/issues/148414 - // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = data.actions.find((action) => action?.frequency)?.frequency; - if (data.consumer === AlertConsumers.SIEM && firstFrequency) { - data.actions = data.actions.map((action) => omit(action, 'frequency')); - if (!data.notifyWhen) { - data.notifyWhen = firstFrequency.notifyWhen; - data.throttle = firstFrequency.throttle; - } - } - await withSpan({ name: 'validateActions', type: 'rules' }, () => validateActions(context, ruleType, data, allowMissingConnectorSecrets) ); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/update.ts b/x-pack/plugins/alerting/server/rules_client/methods/update.ts index 7c1fbedff37ce..f68b41067ddae 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/update.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/update.ts @@ -6,9 +6,8 @@ */ import Boom from '@hapi/boom'; -import { isEqual, omit } from 'lodash'; +import { isEqual } from 'lodash'; import { SavedObject } from '@kbn/core/server'; -import { AlertConsumers } from '@kbn/rule-data-utils'; import type { ShouldIncrementRevision } from './bulk_edit'; import { PartialRule, @@ -186,17 +185,6 @@ async function updateAlert( const ruleType = context.ruleTypeRegistry.get(attributes.alertTypeId); - // TODO https://github.com/elastic/kibana/issues/148414 - // If any action-level frequencies get pushed into a SIEM rule, strip their frequencies - const firstFrequency = data.actions.find((action) => action?.frequency)?.frequency; - if (attributes.consumer === AlertConsumers.SIEM && firstFrequency) { - data.actions = data.actions.map((action) => omit(action, 'frequency')); - if (!attributes.notifyWhen) { - attributes.notifyWhen = firstFrequency.notifyWhen; - attributes.throttle = firstFrequency.throttle; - } - } - // Validate const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate.params); await validateActions(context, ruleType, data, allowMissingConnectorSecrets); diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index aba27d9b617d5..2551bc34f7fa4 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; + /** * as const * @@ -377,9 +379,18 @@ export const ML_GROUP_ID = 'security' as const; export const LEGACY_ML_GROUP_ID = 'siem' as const; export const ML_GROUP_IDS = [ML_GROUP_ID, LEGACY_ML_GROUP_ID] as const; +/** + * Rule Actions + */ export const NOTIFICATION_THROTTLE_NO_ACTIONS = 'no_actions' as const; export const NOTIFICATION_THROTTLE_RULE = 'rule' as const; +export const NOTIFICATION_DEFAULT_FREQUENCY = { + notifyWhen: RuleNotifyWhen.ACTIVE, + throttle: null, + summary: true, +}; + export const showAllOthersBucket: string[] = [ 'destination.ip', 'event.action', diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts index 4b95327810fa9..712312c50140d 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.test.ts @@ -515,28 +515,6 @@ describe('Perform bulk action request schema', () => { expect(message.schema).toEqual({}); }); - test('invalid request: missing throttle in payload', () => { - const payload = { - query: 'name: test', - action: BulkActionType.edit, - [BulkActionType.edit]: [ - { - type: BulkActionEditType.add_rule_actions, - value: { - actions: [], - }, - }, - ], - }; - - const message = retrieveValidationMessage(payload); - - expect(getPaths(left(message.errors))).toEqual( - expect.arrayContaining(['Invalid value "undefined" supplied to "edit,value,throttle"']) - ); - expect(message.schema).toEqual({}); - }); - test('invalid request: missing actions in payload', () => { const payload = { query: 'name: test', diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts index 7b2838ab1d657..e0a392885bad9 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/api/rules/bulk_actions/request_schema.ts @@ -9,6 +9,7 @@ import * as t from 'io-ts'; import { NonEmptyArray, TimeDuration } from '@kbn/securitysolution-io-ts-types'; import { + RuleActionFrequency, RuleActionGroup, RuleActionId, RuleActionParams, @@ -96,11 +97,14 @@ const BulkActionEditPayloadTimeline = t.type({ */ type NormalizedRuleAction = t.TypeOf; const NormalizedRuleAction = t.exact( - t.type({ - group: RuleActionGroup, - id: RuleActionId, - params: RuleActionParams, - }) + t.intersection([ + t.type({ + group: RuleActionGroup, + id: RuleActionId, + params: RuleActionParams, + }), + t.partial({ frequency: RuleActionFrequency }), + ]) ); export type BulkActionEditPayloadRuleActions = t.TypeOf; @@ -109,10 +113,12 @@ export const BulkActionEditPayloadRuleActions = t.type({ t.literal(BulkActionEditType.add_rule_actions), t.literal(BulkActionEditType.set_rule_actions), ]), - value: t.type({ - throttle: ThrottleForBulkActions, - actions: t.array(NormalizedRuleAction), - }), + value: t.intersection([ + t.partial({ throttle: ThrottleForBulkActions }), + t.type({ + actions: t.array(NormalizedRuleAction), + }), + ]), }); type BulkActionEditPayloadSchedule = t.TypeOf; diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts index e0b427cdcefbc..ff4bb72a5eb65 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_schema/model/rule_schemas.ts @@ -119,6 +119,8 @@ export const baseSchema = buildRuleSchemas({ output_index: AlertsIndex, namespace: AlertsIndexNamespace, meta: RuleMetadata, + // Throttle + throttle: RuleActionThrottle, }, defaultable: { // Main attributes @@ -134,7 +136,6 @@ export const baseSchema = buildRuleSchemas({ to: RuleIntervalTo, // Rule actions actions: RuleActionArray, - throttle: RuleActionThrottle, // Rule exceptions exceptions_list: ExceptionListArray, // Misc attributes diff --git a/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts b/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts index 1b74bcd320aad..3808837dc0df2 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/transform_actions.ts @@ -16,6 +16,7 @@ export const transformRuleToAlertAction = ({ action_type_id: actionTypeId, params, uuid, + frequency, alerts_filter: alertsFilter, }: RuleAlertAction): RuleAction => ({ group, @@ -24,6 +25,7 @@ export const transformRuleToAlertAction = ({ actionTypeId, ...(alertsFilter && { alertsFilter }), ...(uuid && { uuid }), + ...(frequency && { frequency }), }); export const transformAlertToRuleAction = ({ @@ -32,6 +34,7 @@ export const transformAlertToRuleAction = ({ actionTypeId, params, uuid, + frequency, alertsFilter, }: RuleAction): RuleAlertAction => ({ group, @@ -40,6 +43,7 @@ export const transformAlertToRuleAction = ({ action_type_id: actionTypeId, ...(alertsFilter && { alerts_filter: alertsFilter }), ...(uuid && { uuid }), + ...(frequency && { frequency }), }); export const transformRuleToAlertResponseAction = ({ diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts index fceecae2b1d5b..624dec7f1c955 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/bulk_edit_rules_actions.cy.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; import { ROLES } from '../../../common/test'; import { @@ -15,11 +16,18 @@ import { import { actionFormSelector } from '../../screens/common/rule_actions'; import { cleanKibana, deleteAlertsAndRules, deleteConnectors } from '../../tasks/common'; +import type { RuleActionCustomFrequency } from '../../tasks/common/rule_actions'; import { addSlackRuleAction, assertSlackRuleAction, addEmailConnectorAndRuleAction, assertEmailRuleAction, + assertSelectedCustomFrequencyOption, + assertSelectedPerRuleRunFrequencyOption, + assertSelectedSummaryOfAlertsOption, + pickCustomFrequencyOption, + pickPerRuleRunFrequencyOption, + pickSummaryOfAlertsOption, } from '../../tasks/common/rule_actions'; import { waitForRulesTableToBeLoaded, @@ -32,10 +40,8 @@ import { submitBulkEditForm, checkOverwriteRuleActionsCheckbox, openBulkEditRuleActionsForm, - pickActionFrequency, openBulkActionsMenu, } from '../../tasks/rules_bulk_edit'; -import { assertSelectedActionFrequency } from '../../tasks/edit_rule'; import { login, visitWithoutDateRange } from '../../tasks/login'; import { esArchiverResetKibana } from '../../tasks/es_archiver'; @@ -75,7 +81,7 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { esArchiverResetKibana(); createSlackConnector().then(({ body }) => { - const actions = [ + const actions: RuleActionArray = [ { id: body.id, action_type_id: '.slack', @@ -83,6 +89,11 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { params: { message: expectedExistingSlackMessage, }, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, }, ]; @@ -120,7 +131,10 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { }); it('Add a rule action to rules (existing connector)', () => { - const expectedActionFrequency = 'Daily'; + const expectedActionFrequency: RuleActionCustomFrequency = { + throttle: 1, + throttleUnit: 'd', + }; loadPrebuiltDetectionRulesFromHeaderBtn(); @@ -131,8 +145,9 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // ensure rule actions info callout displayed on the form cy.get(RULES_BULK_EDIT_ACTIONS_INFO).should('be.visible'); - pickActionFrequency(expectedActionFrequency); addSlackRuleAction(expectedSlackMessage); + pickSummaryOfAlertsOption(); + pickCustomFrequencyOption(expectedActionFrequency); submitBulkEditForm(); waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfRulesToBeEdited }); @@ -140,7 +155,8 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedCustomFrequencyOption(expectedActionFrequency, 1); assertSlackRuleAction(expectedExistingSlackMessage, 0); assertSlackRuleAction(expectedSlackMessage, 1); // ensure there is no third action @@ -148,16 +164,15 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { }); it('Overwrite rule actions in rules', () => { - const expectedActionFrequency = 'On each rule execution'; - loadPrebuiltDetectionRulesFromHeaderBtn(); // select both custom and prebuilt rules selectNumberOfRules(expectedNumberOfRulesToBeEdited); openBulkEditRuleActionsForm(); - pickActionFrequency(expectedActionFrequency); addSlackRuleAction(expectedSlackMessage); + pickSummaryOfAlertsOption(); + pickPerRuleRunFrequencyOption(); // check overwrite box, ensure warning is displayed checkOverwriteRuleActionsCheckbox(); @@ -171,22 +186,27 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedPerRuleRunFrequencyOption(); assertSlackRuleAction(expectedSlackMessage); // ensure existing action was overwritten cy.get(actionFormSelector(1)).should('not.exist'); }); it('Add a rule action to rules (new connector)', () => { - const expectedActionFrequency = 'Hourly'; + const expectedActionFrequency: RuleActionCustomFrequency = { + throttle: 2, + throttleUnit: 'h', + }; const expectedEmail = 'test@example.com'; const expectedSubject = 'Subject'; selectNumberOfRules(expectedNumberOfCustomRulesToBeEdited); openBulkEditRuleActionsForm(); - pickActionFrequency(expectedActionFrequency); addEmailConnectorAndRuleAction(expectedEmail, expectedSubject); + pickSummaryOfAlertsOption(); + pickCustomFrequencyOption(expectedActionFrequency); submitBulkEditForm(); waitForBulkEditActionToFinish({ updatedCount: expectedNumberOfCustomRulesToBeEdited }); @@ -194,7 +214,8 @@ describe.skip('Detection rules, bulk edit of rule actions', () => { // check if rule has been updated goToEditRuleActionsSettingsOf(ruleNameToAssert); - assertSelectedActionFrequency(expectedActionFrequency); + assertSelectedSummaryOfAlertsOption(); + assertSelectedCustomFrequencyOption(expectedActionFrequency, 1); assertEmailRuleAction(expectedEmail, expectedSubject); }); }); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts index 8e2ae1b85cce7..4965072e7038d 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/custom_query_rule.cy.ts @@ -19,10 +19,13 @@ import { RULE_SWITCH, SEVERITY, } from '../../screens/alerts_detection_rules'; +import { + ACTIONS_NOTIFY_WHEN_BUTTON, + ACTIONS_SUMMARY_BUTTON, +} from '../../screens/common/rule_actions'; import { ABOUT_CONTINUE_BTN, ABOUT_EDIT_BUTTON, - ACTIONS_THROTTLE_INPUT, CUSTOM_QUERY_INPUT, DEFINE_CONTINUE_BUTTON, DEFINE_EDIT_BUTTON, @@ -401,12 +404,11 @@ describe('Custom query rules', () => { goToActionsStepTab(); - cy.get(ACTIONS_THROTTLE_INPUT).invoke('val').should('eql', 'no_actions'); - - cy.get(ACTIONS_THROTTLE_INPUT).select('Weekly'); - addEmailConnectorAndRuleAction('test@example.com', 'Subject'); + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts'); + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run'); + goToAboutStepTab(); cy.get(TAGS_CLEAR_BUTTON).click({ force: true }); fillAboutRule(getEditedRule()); diff --git a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts index 5ed5ef8be059a..ab458e12dca2d 100644 --- a/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts +++ b/x-pack/plugins/security_solution/cypress/e2e/detection_rules/rule_actions.cy.ts @@ -43,7 +43,7 @@ describe('Rule actions during detection rule creation', () => { }); const rule = getSimpleCustomQueryRule(); - const actions = { throttle: 'rule', connectors: [indexConnector] }; + const actions = { connectors: [indexConnector] }; const index = actions.connectors[0].index; const initialNumberOfDocuments = 0; const expectedJson = JSON.parse(actions.connectors[0].document); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index ac630d1ee0fd9..af44aa14aa668 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -578,7 +578,6 @@ export const expectedExportedRule = (ruleResponse: Cypress.Response = Partial>; export interface Actions { - throttle: RuleActionThrottle; connectors: Connectors[]; } diff --git a/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts b/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts index 2fe606fc6bf64..9a1702b96d63c 100644 --- a/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts +++ b/x-pack/plugins/security_solution/cypress/screens/common/rule_actions.ts @@ -41,3 +41,20 @@ export const INDEX_SELECTOR = "[data-test-subj='.index-siem-ActionTypeSelectOpti export const actionFormSelector = (position: number) => `[data-test-subj="alertActionAccordion-${position}"]`; + +export const ACTIONS_SUMMARY_BUTTON = '[data-test-subj="summaryOrPerRuleSelect"]'; + +export const ACTIONS_NOTIFY_WHEN_BUTTON = '[data-test-subj="notifyWhenSelect"]'; + +export const ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON = '[data-test-subj="onActiveAlert"]'; + +export const ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON = '[data-test-subj="onThrottleInterval"]'; + +export const ACTIONS_THROTTLE_INPUT = '[data-test-subj="throttleInput"]'; + +export const ACTIONS_THROTTLE_UNIT_INPUT = '[data-test-subj="throttleUnitInput"]'; + +export const ACTIONS_SUMMARY_ALERT_BUTTON = '[data-test-subj="actionNotifyWhen-option-summary"]'; + +export const ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON = + '[data-test-subj="actionNotifyWhen-option-for_each"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index a0cccb508ed66..b248cf06e1a0d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -13,9 +13,6 @@ export const ABOUT_EDIT_TAB = '[data-test-subj="edit-rule-about-tab"]'; export const ACTIONS_EDIT_TAB = '[data-test-subj="edit-rule-actions-tab"]'; -export const ACTIONS_THROTTLE_INPUT = - '[data-test-subj="stepRuleActions"] [data-test-subj="select"]'; - export const ADD_FALSE_POSITIVE_BTN = '[data-test-subj="detectionEngineStepAboutRuleFalsePositives"] .euiButtonEmpty__text'; diff --git a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts index 9546b5da8ad8d..7a36c7fd7c74e 100644 --- a/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/screens/rules_bulk_edit.ts @@ -66,9 +66,6 @@ export const UPDATE_SCHEDULE_LOOKBACK_INPUT = export const UPDATE_SCHEDULE_TIME_UNIT_SELECT = '[data-test-subj="timeType"]'; -export const RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT = - '[data-test-subj="bulkEditRulesRuleActionThrottle"] [data-test-subj="select"]'; - export const RULES_BULK_EDIT_ACTIONS_INFO = '[data-test-subj="bulkEditRulesRuleActionInfo"]'; export const RULES_BULK_EDIT_ACTIONS_WARNING = '[data-test-subj="bulkEditRulesRuleActionsWarning"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts b/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts index 2c289eea0f736..a9a45780567ae 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common/rule_actions.ts @@ -22,6 +22,15 @@ import { EMAIL_CONNECTOR_PASSWORD_INPUT, FORM_VALIDATION_ERROR, JSON_EDITOR, + ACTIONS_SUMMARY_BUTTON, + ACTIONS_NOTIFY_WHEN_BUTTON, + ACTIONS_THROTTLE_INPUT, + ACTIONS_THROTTLE_UNIT_INPUT, + ACTIONS_SUMMARY_ALERT_BUTTON, + ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON, + ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON, + actionFormSelector, + ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON, } from '../../screens/common/rule_actions'; import { COMBO_BOX_INPUT, COMBO_BOX_SELECTION } from '../../screens/common/controls'; import type { EmailConnector, IndexConnector } from '../../objects/connector'; @@ -84,3 +93,79 @@ export const fillIndexConnectorForm = (connector: IndexConnector = getIndexConne parseSpecialCharSequences: false, }); }; + +export interface RuleActionCustomFrequency { + throttle?: number; + throttleUnit?: 's' | 'm' | 'h' | 'd'; +} + +export const pickSummaryOfAlertsOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).click(); + }); + cy.get(ACTIONS_SUMMARY_ALERT_BUTTON).click(); +}; +export const pickForEachAlertOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).click(); + }); + cy.get(ACTIONS_SUMMARY_FOR_EACH_ALERT_BUTTON).click(); +}; + +export const pickCustomFrequencyOption = ( + { throttle = 1, throttleUnit = 'h' }: RuleActionCustomFrequency, + index = 0 +) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).click(); + }); + cy.get(ACTIONS_NOTIFY_CUSTOM_FREQUENCY_BUTTON).click(); + form.within(() => { + cy.get(ACTIONS_THROTTLE_INPUT).type(`{selectAll}${throttle}`); + cy.get(ACTIONS_THROTTLE_UNIT_INPUT).select(throttleUnit); + }); +}; + +export const pickPerRuleRunFrequencyOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).click(); + }); + cy.get(ACTIONS_NOTIFY_PER_RULE_RUN_BUTTON).click(); +}; + +export const assertSelectedSummaryOfAlertsOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'Summary of alerts'); + }); +}; + +export const assertSelectedForEachAlertOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_SUMMARY_BUTTON).should('have.text', 'For each alert'); + }); +}; + +export const assertSelectedCustomFrequencyOption = ( + { throttle = 1, throttleUnit = 'h' }: RuleActionCustomFrequency, + index = 0 +) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Custom frequency'); + cy.get(ACTIONS_THROTTLE_INPUT).should('have.value', throttle); + cy.get(ACTIONS_THROTTLE_UNIT_INPUT).should('have.value', throttleUnit); + }); +}; + +export const assertSelectedPerRuleRunFrequencyOption = (index = 0) => { + const form = cy.get(actionFormSelector(index)); + form.within(() => { + cy.get(ACTIONS_NOTIFY_WHEN_BUTTON).should('have.text', 'Per rule run'); + }); +}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index b8274ed33c120..1f3f051c6f474 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -99,7 +99,6 @@ import { NEW_TERMS_HISTORY_SIZE, NEW_TERMS_HISTORY_TIME_TYPE, NEW_TERMS_INPUT_AREA, - ACTIONS_THROTTLE_INPUT, CONTINUE_BUTTON, CREATE_WITHOUT_ENABLING_BTN, RULE_INDICES, @@ -407,7 +406,6 @@ export const fillFrom = (from: RuleIntervalFrom = ruleFields.ruleIntervalFrom) = }; export const fillRuleAction = (actions: Actions) => { - cy.get(ACTIONS_THROTTLE_INPUT).select(actions.throttle); actions.connectors.forEach((connector) => { switch (connector.type) { case 'index': diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts index a016691328ffd..42d5619c28a67 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -6,7 +6,6 @@ */ import { BACK_TO_RULE_DETAILS, EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; -import { ACTIONS_THROTTLE_INPUT } from '../screens/create_new_rule'; export const saveEditedRule = () => { cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true }); @@ -17,7 +16,3 @@ export const goBackToRuleDetails = () => { cy.get(BACK_TO_RULE_DETAILS).should('exist').click(); cy.get(BACK_TO_RULE_DETAILS).should('not.exist'); }; - -export const assertSelectedActionFrequency = (frequency: string) => { - cy.get(ACTIONS_THROTTLE_INPUT).find('option:selected').should('have.text', frequency); -}; diff --git a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts index b2203d1b1202a..b4bf088bafd6a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rules_bulk_edit.ts @@ -39,7 +39,6 @@ import { UPDATE_SCHEDULE_LOOKBACK_INPUT, RULES_BULK_EDIT_SCHEDULES_WARNING, RULES_BULK_EDIT_OVERWRITE_ACTIONS_CHECKBOX, - RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT, } from '../screens/rules_bulk_edit'; import { SCHEDULE_DETAILS } from '../screens/rule_details'; @@ -292,7 +291,3 @@ export const assertRuleScheduleValues = ({ interval, lookback }: RuleSchedule) = cy.get('dd').eq(1).should('contain.text', lookback); }); }; - -export const pickActionFrequency = (frequency: string) => { - cy.get(RULES_BULK_EDIT_ACTIONS_THROTTLE_INPUT).select(frequency); -}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts index af6a82af1a9d5..8b7a9c62b1541 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.test.ts @@ -797,91 +797,6 @@ describe('helpers', () => { meta: { kibana_siem_app_url: 'http://localhost:5601/app/siem', }, - throttle: 'no_actions', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for no_actions', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: 'no_actions', - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: 'no_actions', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for rule', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: 'rule', - actions: [ - { - group: 'default', - id: 'id', - actionTypeId: 'actionTypeId', - params: {}, - }, - ], - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [ - { - group: mockStepData.actions[0].group, - id: mockStepData.actions[0].id, - action_type_id: mockStepData.actions[0].actionTypeId, - params: mockStepData.actions[0].params, - }, - ], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: 'rule', - }; - - expect(result).toEqual(expected); - }); - - test('returns proper throttle value for interval', () => { - const mockStepData: ActionsStepRule = { - ...mockData, - throttle: '1d', - actions: [ - { - group: 'default', - id: 'id', - actionTypeId: 'actionTypeId', - params: {}, - }, - ], - }; - const result = formatActionsStepData(mockStepData); - const expected: ActionsStepRuleJson = { - actions: [ - { - group: mockStepData.actions[0].group, - id: mockStepData.actions[0].id, - action_type_id: mockStepData.actions[0].actionTypeId, - params: mockStepData.actions[0].params, - }, - ], - enabled: false, - meta: { - kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, - }, - throttle: mockStepData.throttle, }; expect(result).toEqual(expected); @@ -913,7 +828,6 @@ describe('helpers', () => { meta: { kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, }, - throttle: 'no_actions', }; expect(result).toEqual(expected); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts index 5442727561ce1..e61f55dbce4ec 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/helpers.ts @@ -25,7 +25,6 @@ import type { Type, } from '@kbn/securitysolution-io-ts-alerting-types'; import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; -import { NOTIFICATION_THROTTLE_NO_ACTIONS } from '../../../../../common/constants'; import { assertUnreachable } from '../../../../../common/utility_types'; import { transformAlertToRuleAction, @@ -563,19 +562,12 @@ export const formatAboutStepData = ( }; export const formatActionsStepData = (actionsStepData: ActionsStepRule): ActionsStepRuleJson => { - const { - actions = [], - responseActions, - enabled, - kibanaSiemAppUrl, - throttle = NOTIFICATION_THROTTLE_NO_ACTIONS, - } = actionsStepData; + const { actions = [], responseActions, enabled, kibanaSiemAppUrl } = actionsStepData; return { actions: actions.map(transformAlertToRuleAction), response_actions: responseActions?.map(transformAlertToRuleResponseAction), enabled, - throttle: actions.length ? throttle : NOTIFICATION_THROTTLE_NO_ACTIONS, meta: { kibana_siem_app_url: kibanaSiemAppUrl, }, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts index 40707a4307f27..487052fcbf2ef 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/__mocks__/mock.ts @@ -200,7 +200,6 @@ export const mockActionsStepRule = (enabled = false): ActionsStepRule => ({ actions: [], kibanaSiemAppUrl: 'http://localhost:5601/app/siem', enabled, - throttle: 'no_actions', }); export const mockDefineStepRule = (): DefineStepRule => ({ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx index 8b791ee2aece1..ff41017ac1771 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/rule_actions_form.tsx @@ -23,21 +23,13 @@ import { Field, } from '../../../../../../shared_imports'; import { BulkActionEditType } from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import type { - BulkActionEditPayload, - ThrottleForBulkActions, -} from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; -import { NOTIFICATION_THROTTLE_RULE } from '../../../../../../../common/constants'; +import type { BulkActionEditPayload } from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema'; import { BulkEditFormWrapper } from './bulk_edit_form_wrapper'; import { bulkAddRuleActions as i18n } from '../translations'; import { useKibana } from '../../../../../../common/lib/kibana'; -import { - ThrottleSelectField, - THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, -} from '../../../../../../detections/components/rules/throttle_select_field'; import { getAllActionMessageParams } from '../../../../../../detections/pages/detection_engine/rules/helpers'; import { RuleActionsField } from '../../../../../../detections/components/rules/rule_actions_field'; @@ -45,19 +37,16 @@ import { debouncedValidateRuleActionsField } from '../../../../../../detections/ const CommonUseField = getUseField({ component: Field }); +type BulkActionsRuleAction = RuleAction & Required>; + export interface RuleActionsFormData { - throttle: ThrottleForBulkActions; - actions: RuleAction[]; + actions: BulkActionsRuleAction[]; overwrite: boolean; } const getFormSchema = ( actionTypeRegistry: ActionTypeRegistryContract ): FormSchema => ({ - throttle: { - label: i18n.THROTTLE_LABEL, - helpText: i18n.THROTTLE_HELP_TEXT, - }, actions: { validations: [ { @@ -75,7 +64,6 @@ const getFormSchema = ( }); const defaultFormData: RuleActionsFormData = { - throttle: NOTIFICATION_THROTTLE_RULE, actions: [], overwrite: false, }; @@ -108,7 +96,7 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction return; } - const { actions = [], throttle: throttleToSubmit, overwrite: overwriteValue } = data; + const { actions = [], overwrite: overwriteValue } = data; const editAction = overwriteValue ? BulkActionEditType.set_rule_actions : BulkActionEditType.add_rule_actions; @@ -117,23 +105,10 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction type: editAction, value: { actions: actions.map(({ actionTypeId, ...action }) => action), - throttle: throttleToSubmit, }, }); }, [form, onConfirm]); - const throttleFieldComponentProps = useMemo( - () => ({ - idAria: 'bulkEditRulesRuleActionThrottle', - 'data-test-subj': 'bulkEditRulesRuleActionThrottle', - hasNoInitialSelection: false, - euiFieldProps: { - options: THROTTLE_OPTIONS_FOR_BULK_RULE_ACTIONS, - }, - }), - [] - ); - const messageVariables = useMemo(() => getAllActionMessageParams(), []); return ( @@ -156,24 +131,11 @@ const RuleActionsFormComponent = ({ rulesCount, onClose, onConfirm }: RuleAction } >
    -
  • - -
  • {i18n.RULE_VARIABLES_DETAIL}
- - - omit(a, 'frequency')); - } - startTransaction({ name: BULK_RULE_ACTIONS.EDIT }); const hideWarningToast = () => { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts index c8f49ebe4a6c6..f25b1331d766a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/utils/compute_dry_run_edit_payload.ts @@ -50,7 +50,7 @@ export function computeDryRunEditPayload(editAction: BulkActionEditType): BulkAc return [ { type: editAction, - value: { throttle: '1h', actions: [] }, + value: { actions: [] }, }, ]; case BulkActionEditType.set_schedule: diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx index 019e20b25db1a..084be38391a45 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_actions_field/index.tsx @@ -16,7 +16,6 @@ import type { ActionVariables, NotifyWhenSelectOptions, } from '@kbn/triggers-actions-ui-plugin/public'; -import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; import type { RuleAction, RuleActionAlertsFilterProperty, @@ -24,6 +23,7 @@ import type { } from '@kbn/alerting-plugin/common'; import { SecurityConnectorFeatureId } from '@kbn/actions-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; +import { NOTIFICATION_DEFAULT_FREQUENCY } from '../../../../../common/constants'; import type { FieldHook } from '../../../../shared_imports'; import { useFormContext } from '../../../../shared_imports'; import { useKibana } from '../../../../common/lib/kibana'; @@ -33,12 +33,6 @@ import { FORM_ON_ACTIVE_ALERT_OPTION, } from './translations'; -const DEFAULT_FREQUENCY = { - notifyWhen: RuleNotifyWhen.ACTIVE, - throttle: null, - summary: true, -}; - const NOTIFY_WHEN_OPTIONS: NotifyWhenSelectOptions[] = [ { isSummaryOption: true, @@ -214,6 +208,23 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = [field] ); + const setActionFrequency = useCallback( + (key: string, value: RuleActionParam, index: number) => { + field.setValue((prevValue: RuleAction[]) => { + const updatedActions = [...prevValue]; + updatedActions[index] = { + ...updatedActions[index], + frequency: { + ...(updatedActions[index].frequency ?? NOTIFICATION_DEFAULT_FREQUENCY), + [key]: value, + }, + }; + return updatedActions; + }); + }, + [field] + ); + const actionForm = useMemo( () => getActionForm({ @@ -223,22 +234,22 @@ export const RuleActionsField: React.FC = ({ field, messageVariables }) = setActionIdByIndex, setActions: setAlertActionsProperty, setActionParamsProperty, - setActionFrequencyProperty: () => {}, + setActionFrequencyProperty: setActionFrequency, setActionAlertsFilterProperty, featureId: SecurityConnectorFeatureId, defaultActionMessage: DEFAULT_ACTION_MESSAGE, defaultSummaryMessage: DEFAULT_ACTION_MESSAGE, hideActionHeader: true, - hideNotifyWhen: true, hasSummary: true, notifyWhenSelectOptions: NOTIFY_WHEN_OPTIONS, - defaultRuleFrequency: DEFAULT_FREQUENCY, + defaultRuleFrequency: NOTIFICATION_DEFAULT_FREQUENCY, showActionAlertsFilter: true, }), [ actions, getActionForm, messageVariables, + setActionFrequency, setActionIdByIndex, setActionParamsProperty, setAlertActionsProperty, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts index 858578f8a5d38..f16cac0eb923a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/get_schema.ts @@ -5,8 +5,6 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; - import type { ActionTypeRegistryContract } from '@kbn/triggers-actions-ui-plugin/public'; import { debouncedValidateRuleActionsField } from '../../../containers/detection_engine/rules/validate_rule_actions_field'; @@ -30,12 +28,4 @@ export const getSchema = ({ responseActions: {}, enabled: {}, kibanaSiemAppUrl: {}, - throttle: { - label: i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel', - { - defaultMessage: 'Actions frequency', - } - ), - }, }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index 58dd95bcac0dd..86bbb7604add2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -15,7 +15,6 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { findIndex } from 'lodash/fp'; import type { FC } from 'react'; import React, { memo, useCallback, useEffect, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -29,20 +28,13 @@ import { ResponseActionsForm } from '../../../../detection_engine/rule_response_ import type { RuleStepProps, ActionsStepRule } from '../../../pages/detection_engine/rules/types'; import { RuleStep } from '../../../pages/detection_engine/rules/types'; import { StepRuleDescription } from '../description_step'; -import { Form, UseField, useForm, useFormData } from '../../../../shared_imports'; +import { Form, UseField, useForm } from '../../../../shared_imports'; import { StepContentWrapper } from '../step_content_wrapper'; -import { - ThrottleSelectField, - THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, - DEFAULT_THROTTLE_OPTION, -} from '../throttle_select_field'; import { RuleActionsField } from '../rule_actions_field'; import { useKibana } from '../../../../common/lib/kibana'; import { getSchema } from './get_schema'; import * as I18n from './translations'; import { APP_UI_ID } from '../../../../../common/constants'; -import { useManageCaseAction } from './use_manage_case_action'; -import { THROTTLE_FIELD_HELP_TEXT, THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY } from './translations'; interface StepRuleActionsProps extends RuleStepProps { defaultValues?: ActionsStepRule | null; @@ -55,23 +47,10 @@ export const stepActionsDefaultValue: ActionsStepRule = { actions: [], responseActions: [], kibanaSiemAppUrl: '', - throttle: DEFAULT_THROTTLE_OPTION.value, }; const GhostFormField = () => <>; -const getThrottleOptions = (throttle?: string | null) => { - // Add support for throttle options set by the API - if ( - throttle && - findIndex(['value', throttle], THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING) < 0 - ) { - return [...THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING, { value: throttle, text: throttle }]; - } - - return THROTTLE_OPTIONS_FOR_RULE_CREATION_AND_EDITING; -}; - const DisplayActionsHeader = () => { return ( <> @@ -99,7 +78,6 @@ const StepRuleActionsComponent: FC = ({ actionMessageParams, ruleType, }) => { - const [isLoadingCaseAction] = useManageCaseAction(); const { services: { application, @@ -127,11 +105,6 @@ const StepRuleActionsComponent: FC = ({ schema, }); const { getFields, getFormData, submit } = form; - const [{ throttle: formThrottle }] = useFormData({ - form, - watch: ['throttle'], - }); - const throttle = formThrottle || initialState.throttle; const handleSubmit = useCallback( (enabled: boolean) => { @@ -163,44 +136,20 @@ const StepRuleActionsComponent: FC = ({ }; }, [getData, setForm]); - const throttleOptions = useMemo(() => { - return getThrottleOptions(throttle); - }, [throttle]); - - const throttleFieldComponentProps = useMemo( - () => ({ - idAria: 'detectionEngineStepRuleActionsThrottle', - isDisabled: isLoading, - isLoading: isLoadingCaseAction, - dataTestSubj: 'detectionEngineStepRuleActionsThrottle', - hasNoInitialSelection: false, - helpText: isQueryRule(ruleType) - ? THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY - : THROTTLE_FIELD_HELP_TEXT, - euiFieldProps: { - options: throttleOptions, - }, - }), - [isLoading, isLoadingCaseAction, ruleType, throttleOptions] - ); - const displayActionsOptions = useMemo( - () => - throttle !== stepActionsDefaultValue.throttle ? ( - <> - - - - ) : ( - - ), - [throttle, actionMessageParams] + () => ( + <> + + + + ), + [actionMessageParams] ); const displayResponseActionsOptions = useMemo(() => { if (isQueryRule(ruleType)) { @@ -217,11 +166,6 @@ const StepRuleActionsComponent: FC = ({ return application.capabilities.actions.show ? ( <> - {displayActionsOptions} {responseActionsEnabled && displayResponseActionsOptions} @@ -231,14 +175,6 @@ const StepRuleActionsComponent: FC = ({ ) : ( <> {I18n.NO_ACTIONS_READ_PERMISSIONS} - - - - ); }, [ @@ -246,7 +182,6 @@ const StepRuleActionsComponent: FC = ({ displayActionsOptions, displayResponseActionsOptions, responseActionsEnabled, - throttleFieldComponentProps, ]); if (isReadOnlyView) { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx index 15937883fb409..d467c3af05f8f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx @@ -28,19 +28,3 @@ export const NO_ACTIONS_READ_PERMISSIONS = i18n.translate( 'Cannot create rule actions. You do not have "Read" permissions for the "Actions" plugin.', } ); - -export const THROTTLE_FIELD_HELP_TEXT = i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText', - { - defaultMessage: - 'Select when automated actions should be performed if a rule evaluates as true.', - } -); - -export const THROTTLE_FIELD_HELP_TEXT_WHEN_QUERY = i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery', - { - defaultMessage: - 'Select when automated actions should be performed if a rule evaluates as true. This frequency does not apply to Response Actions.', - } -); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx deleted file mode 100644 index 57dd5670dba80..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/use_manage_case_action.tsx +++ /dev/null @@ -1,66 +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 { useEffect, useRef, useState } from 'react'; -import { getAllConnectorsUrl, getCreateConnectorUrl } from '@kbn/cases-plugin/common'; -import { convertArrayToCamelCase, KibanaServices } from '../../../../common/lib/kibana'; - -interface CaseAction { - connectorTypeId: string; - id: string; - isPreconfigured: boolean; - name: string; - referencedByCount: number; -} - -const CASE_ACTION_NAME = 'Cases'; - -export const useManageCaseAction = () => { - const hasInit = useRef(true); - const [loading, setLoading] = useState(true); - const [hasError, setHasError] = useState(false); - - useEffect(() => { - const abortCtrl = new AbortController(); - const fetchActions = async () => { - try { - const actions = convertArrayToCamelCase( - await KibanaServices.get().http.fetch(getAllConnectorsUrl(), { - method: 'GET', - signal: abortCtrl.signal, - }) - ) as CaseAction[]; - - if (!actions.some((a) => a.connectorTypeId === '.case' && a.name === CASE_ACTION_NAME)) { - await KibanaServices.get().http.post(getCreateConnectorUrl(), { - method: 'POST', - body: JSON.stringify({ - connector_type_id: '.case', - config: {}, - name: CASE_ACTION_NAME, - secrets: {}, - }), - signal: abortCtrl.signal, - }); - } - setLoading(false); - } catch { - setLoading(false); - setHasError(true); - } - }; - if (hasInit.current) { - hasInit.current = false; - fetchActions(); - } - - return () => { - abortCtrl.abort(); - }; - }, []); - return [loading, hasError]; -}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index 22ba4e03dbf38..58d6e4c7c8470 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -35,6 +35,7 @@ import type { ActionsStepRule, } from './types'; import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; +import type { RuleAlertAction } from '../../../../../common/detection_engine/types'; describe('rule helpers', () => { moment.suppressDeprecationWarnings = true; @@ -146,7 +147,6 @@ describe('rule helpers', () => { const scheduleRuleStepData = { from: '0s', interval: '5m' }; const ruleActionsStepData = { enabled: true, - throttle: 'no_actions', actions: [], responseActions: undefined, }; @@ -410,16 +410,22 @@ describe('rule helpers', () => { describe('getActionsStepsData', () => { test('returns expected ActionsStepRule rule object', () => { + const actions: RuleAlertAction[] = [ + { + id: 'id', + group: 'group', + params: {}, + action_type_id: 'action_type_id', + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + ]; const mockedRule = { ...mockRule('test-id'), - actions: [ - { - id: 'id', - group: 'group', - params: {}, - action_type_id: 'action_type_id', - }, - ], + actions, }; const result: ActionsStepRule = getActionsStepsData(mockedRule); const expected = { @@ -429,11 +435,15 @@ describe('rule helpers', () => { group: 'group', params: {}, actionTypeId: 'action_type_id', + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, }, ], responseActions: undefined, enabled: mockedRule.enabled, - throttle: 'no_actions', }; expect(result).toEqual(expected); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index a290ac92f6a66..d0cfdc8b707f3 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -76,12 +76,11 @@ export const getActionsStepsData = ( response_actions?: ResponseAction[]; } ): ActionsStepRule => { - const { enabled, throttle, meta, actions = [], response_actions: responseActions } = rule; + const { enabled, meta, actions = [], response_actions: responseActions } = rule; return { actions: actions?.map(transformRuleToAlertAction), responseActions: responseActions?.map(transformRuleToAlertResponseAction), - throttle, kibanaSiemAppUrl: meta?.kibana_siem_app_url, enabled, }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index edbb3b4ecbf97..2d2c7580ed051 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -194,7 +194,6 @@ export interface ActionsStepRule { responseActions?: RuleResponseAction[]; enabled: boolean; kibanaSiemAppUrl?: string; - throttle?: string | null; } export interface DefineStepRuleJson { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts index 7df109deb6f3b..848a0933da542 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/utils.ts @@ -65,7 +65,7 @@ export const getOutputRuleAlertForRest = (): RuleResponse => ({ severity_mapping: [], updated_by: 'elastic', tags: [], - throttle: 'no_actions', + throttle: undefined, threat: getThreatMock(), exceptions_list: getListArrayMock(), filters: [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts index b441d071c21c1..08a53c007dc06 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.test.ts @@ -109,8 +109,6 @@ describe('duplicateRule', () => { consumer: rule.consumer, schedule: rule.schedule, actions: rule.actions, - throttle: null, // TODO: fix? - notifyWhen: null, // TODO: fix? enabled: false, // covered in a separate test }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts index 4a99085123b41..315517504def4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/actions/duplicate_rule.ts @@ -11,6 +11,7 @@ import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import type { SanitizedRule } from '@kbn/alerting-plugin/common'; import { SERVER_APP_ID } from '../../../../../../common/constants'; import type { InternalRuleCreate, RuleParams } from '../../../rule_schema'; +import { transformToActionFrequency } from '../../normalization/rule_actions'; const DUPLICATE_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.cloneRule.duplicateTitle', @@ -33,6 +34,7 @@ export const duplicateRule = async ({ rule }: DuplicateRuleParams): Promise - ({ - field: 'throttle', - operation: 'set', - value: transformToAlertThrottle(throttle), - } as const); - -const getNotifyWhenOperation = (throttle: string) => - ({ - field: 'notifyWhen', - operation: 'set', - value: transformToNotifyWhen(throttle), - } as const); +import { transformToActionFrequency } from '../../normalization/rule_actions'; /** * converts bulk edit action to format of rulesClient.bulkEdit operation @@ -70,10 +55,8 @@ export const bulkEditActionToRulesClientOperation = ( { field: 'actions', operation: 'add', - value: action.value.actions, + value: transformToActionFrequency(action.value.actions, action.value.throttle), }, - getThrottleOperation(action.value.throttle), - getNotifyWhenOperation(action.value.throttle), ]; case BulkActionEditType.set_rule_actions: @@ -81,10 +64,8 @@ export const bulkEditActionToRulesClientOperation = ( { field: 'actions', operation: 'set', - value: action.value.actions, + value: transformToActionFrequency(action.value.actions, action.value.throttle), }, - getThrottleOperation(action.value.throttle), - getNotifyWhenOperation(action.value.throttle), ]; // schedule actions diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts index cfb051273255a..6e4d573a880e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts @@ -123,6 +123,7 @@ describe('patchRules', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', }, group: 'default', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }), @@ -158,6 +159,7 @@ describe('patchRules', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', }, group: 'default', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts index 1996cb1652ab6..070a81692f3d5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts @@ -12,7 +12,7 @@ import type { RuleUpdateProps } from '../../../../../../common/detection_engine/ import { transformRuleToAlertAction } from '../../../../../../common/detection_engine/transform_actions'; import type { InternalRuleUpdate, RuleParams, RuleAlertType } from '../../../rule_schema'; -import { transformToAlertThrottle, transformToNotifyWhen } from '../../normalization/rule_actions'; +import { transformToActionFrequency } from '../../normalization/rule_actions'; import { typeSpecificSnakeToCamel } from '../../normalization/rule_converters'; export interface UpdateRulesOptions { @@ -30,6 +30,9 @@ export const updateRules = async ({ return null; } + const alertActions = ruleUpdate.actions?.map(transformRuleToAlertAction) ?? []; + const actions = transformToActionFrequency(alertActions, ruleUpdate.throttle); + const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); const enabled = ruleUpdate.enabled ?? true; const newInternalRule: InternalRuleUpdate = { @@ -70,9 +73,7 @@ export const updateRules = async ({ ...typeSpecificParams, }, schedule: { interval: ruleUpdate.interval ?? '5m' }, - actions: ruleUpdate.actions != null ? ruleUpdate.actions.map(transformRuleToAlertAction) : [], - throttle: transformToAlertThrottle(ruleUpdate.throttle), - notifyWhen: transformToNotifyWhen(ruleUpdate.throttle), + actions, }; const update = await rulesClient.update({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts index 038dfbc9152d6..c86387f26f50a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_all.test.ts @@ -131,7 +131,6 @@ describe('getExportAll', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), @@ -275,6 +274,7 @@ describe('getExportAll', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.slack', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], building_block_type: 'default', @@ -313,7 +313,6 @@ describe('getExportAll', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'rule', note: '# Investigative notes', version: 1, revision: 0, @@ -416,6 +415,7 @@ describe('getExportAll', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.email', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts index de76ef3b23d8b..35151f1b4b1d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/export/get_export_by_object_ids.test.ts @@ -128,7 +128,6 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', note: '# Investigative notes', version: 1, exceptions_list: getListArrayMock(), @@ -284,6 +283,7 @@ describe('get_export_by_object_ids', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.slack', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], building_block_type: 'default', @@ -322,7 +322,6 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'rule', note: '# Investigative notes', version: 1, revision: 0, @@ -426,6 +425,7 @@ describe('get_export_by_object_ids', () => { message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: '.email', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }) @@ -508,7 +508,7 @@ describe('get_export_by_object_ids', () => { to: 'now', type: 'query', threat: getThreatMock(), - throttle: 'no_actions', + throttle: undefined, note: '# Investigative notes', version: 1, revision: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts index d0d5edad970f0..33fcf0bd29c20 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.test.ts @@ -5,7 +5,9 @@ * 2.0. */ +import type { SanitizedRuleAction } from '@kbn/alerting-plugin/common'; import { + NOTIFICATION_DEFAULT_FREQUENCY, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE, } from '../../../../../common/constants'; @@ -14,6 +16,7 @@ import type { RuleAlertType } from '../../rule_schema'; import { transformFromAlertThrottle, + transformToActionFrequency, transformToAlertThrottle, transformToNotifyWhen, } from './rule_actions'; @@ -151,4 +154,152 @@ describe('Rule actions normalization', () => { ).toEqual(NOTIFICATION_THROTTLE_RULE); }); }); + + describe('transformToActionFrequency', () => { + describe('actions without frequencies', () => { + const actionsWithoutFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + }, + ]; + + test.each([undefined, null, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE])( + `it sets each action's frequency attribute to default value when 'throttle' is '%s'`, + (throttle) => { + expect(transformToActionFrequency(actionsWithoutFrequencies, throttle)).toEqual( + actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })) + ); + } + ); + + test.each(['47s', '10m', '3h', '101d'])( + `it correctly transforms 'throttle = %s' and sets it as a frequency of each action`, + (throttle) => { + expect(transformToActionFrequency(actionsWithoutFrequencies, throttle)).toEqual( + actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })) + ); + } + ); + }); + + describe('actions with frequencies', () => { + const actionsWithFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + frequency: { + summary: false, + throttle: '1s', + notifyWhen: 'onThrottleInterval', + }, + }, + ]; + + test.each([ + undefined, + null, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '1h', + '1d', + ])(`it does not change actions frequency attributes when 'throttle' is '%s'`, (throttle) => { + expect(transformToActionFrequency(actionsWithFrequencies, throttle)).toEqual( + actionsWithFrequencies + ); + }); + }); + + describe('some actions with frequencies', () => { + const someActionsWithFrequencies: SanitizedRuleAction[] = [ + { + group: 'group', + id: 'id-123', + actionTypeId: 'id-456', + params: {}, + frequency: { + summary: true, + throttle: null, + notifyWhen: 'onActiveAlert', + }, + }, + { + group: 'group', + id: 'id-789', + actionTypeId: 'id-012', + params: {}, + frequency: { + summary: false, + throttle: '1s', + notifyWhen: 'onThrottleInterval', + }, + }, + { + group: 'group', + id: 'id-345', + actionTypeId: 'id-678', + params: {}, + }, + ]; + + test.each([undefined, null, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE])( + `it overrides each action's frequency attribute to default value when 'throttle' is '%s'`, + (throttle) => { + expect(transformToActionFrequency(someActionsWithFrequencies, throttle)).toEqual( + someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })) + ); + } + ); + + test.each(['47s', '10m', '3h', '101d'])( + `it correctly transforms 'throttle = %s' and overrides frequency attribute of each action`, + (throttle) => { + expect(transformToActionFrequency(someActionsWithFrequencies, throttle)).toEqual( + someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })) + ); + } + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts index 51dbe9d80f986..d0e909cc5f329 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_actions.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; +import type { RuleActionFrequency, RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; import { NOTIFICATION_THROTTLE_NO_ACTIONS, @@ -14,6 +14,38 @@ import { import type { RuleAlertType } from '../../rule_schema'; +export const transformToFrequency = (throttle: string | null | undefined): RuleActionFrequency => { + return { + summary: true, + notifyWhen: transformToNotifyWhen(throttle) ?? 'onActiveAlert', + throttle: transformToAlertThrottle(throttle), + }; +}; + +interface ActionWithFrequency { + frequency?: RuleActionFrequency; +} + +/** + * The action level `frequency` attribute should always take precedence over the rule level `throttle` + * Frequency's default value is `{ summary: true, throttle: null, notifyWhen: 'onActiveAlert' }` + * + * The transformation follows the next rules: + * - Both rule level `throttle` and all actions have `frequency` are set: we will ignore rule level `throttle` + * - Rule level `throttle` set and actions don't have `frequency` set: we will transform rule level `throttle` in action level `frequency` + * - All actions have `frequency` set: do nothing + * - Neither of them is set: we will set action level `frequency` to default value + * - Rule level `throttle` and some of the actions have `frequency` set: we will transform rule level `throttle` and set it to actions without the frequency attribute + * - Only some actions have `frequency` set and there is no rule level `throttle`: we will set default `frequency` to actions without frequency attribute + */ +export const transformToActionFrequency = ( + actions: T[], + throttle: string | null | undefined +): T[] => { + const defaultFrequency = transformToFrequency(throttle); + return actions.map((action) => ({ ...action, frequency: action.frequency ?? defaultFrequency })); +}; + /** * Given a throttle from a "security_solution" rule this will transform it into an "alerting" notifyWhen * on their saved object. diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts index 7296e90bf73e7..66d873c2c0935 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts @@ -39,10 +39,10 @@ import { } from '../../../../../common/detection_engine/rule_schema'; import { + transformAlertToRuleAction, transformAlertToRuleResponseAction, transformRuleToAlertAction, transformRuleToAlertResponseAction, - transformAlertToRuleAction, } from '../../../../../common/detection_engine/transform_actions'; import { @@ -73,11 +73,7 @@ import type { NewTermsRuleParams, NewTermsSpecificRuleParams, } from '../../rule_schema'; -import { - transformFromAlertThrottle, - transformToAlertThrottle, - transformToNotifyWhen, -} from './rule_actions'; +import { transformFromAlertThrottle, transformToActionFrequency } from './rule_actions'; import { convertAlertSuppressionToCamel, convertAlertSuppressionToSnake } from '../utils/utils'; import { createRuleExecutionSummary } from '../../rule_monitoring'; @@ -399,6 +395,11 @@ export const convertPatchAPIToInternalSchema = ( ): InternalRuleUpdate => { const typeSpecificParams = patchTypeSpecificSnakeToCamel(nextParams, existingRule.params); const existingParams = existingRule.params; + + const alertActions = nextParams.actions?.map(transformRuleToAlertAction) ?? existingRule.actions; + const throttle = nextParams.throttle ?? transformFromAlertThrottle(existingRule); + const actions = transformToActionFrequency(alertActions, throttle); + return { name: nextParams.name ?? existingRule.name, tags: nextParams.tags ?? existingRule.tags, @@ -438,15 +439,7 @@ export const convertPatchAPIToInternalSchema = ( ...typeSpecificParams, }, schedule: { interval: nextParams.interval ?? existingRule.schedule.interval }, - actions: nextParams.actions - ? nextParams.actions.map(transformRuleToAlertAction) - : existingRule.actions, - throttle: nextParams.throttle - ? transformToAlertThrottle(nextParams.throttle) - : existingRule.throttle ?? null, - notifyWhen: nextParams.throttle - ? transformToNotifyWhen(nextParams.throttle) - : existingRule.notifyWhen ?? null, + actions, }; }; @@ -462,6 +455,10 @@ export const convertCreateAPIToInternalSchema = ( ): InternalRuleCreate => { const typeSpecificParams = typeSpecificSnakeToCamel(input); const newRuleId = input.rule_id ?? uuidv4(); + + const alertActions = input.actions?.map(transformRuleToAlertAction) ?? []; + const actions = transformToActionFrequency(alertActions, input.throttle); + return { name: input.name, tags: input.tags ?? [], @@ -502,9 +499,7 @@ export const convertCreateAPIToInternalSchema = ( }, schedule: { interval: input.interval ?? '5m' }, enabled: input.enabled ?? defaultEnabled, - actions: input.actions?.map(transformRuleToAlertAction) ?? [], - throttle: transformToAlertThrottle(input.throttle), - notifyWhen: transformToNotifyWhen(input.throttle), + actions, }; }; @@ -651,6 +646,10 @@ export const internalRuleToAPIResponse = ( const isResolvedRule = (obj: unknown): obj is ResolvedSanitizedRule => (obj as ResolvedSanitizedRule).outcome != null; + const alertActions = rule.actions.map(transformAlertToRuleAction); + const throttle = transformFromAlertThrottle(rule); + const actions = transformToActionFrequency(alertActions, throttle); + return { // saved object properties outcome: isResolvedRule(rule) ? rule.outcome : undefined, @@ -672,8 +671,8 @@ export const internalRuleToAPIResponse = ( // Type specific security solution rule params ...typeSpecificCamelToSnake(rule.params), // Actions - throttle: transformFromAlertThrottle(rule), - actions: rule.actions.map(transformAlertToRuleAction), + throttle: undefined, + actions, // Execution summary execution_summary: executionSummary ?? undefined, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index bae75363ff49a..373842fe31860 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -43,7 +43,7 @@ export const ruleOutput = (): RuleResponse => ({ tags: [], to: 'now', type: 'query', - throttle: 'no_actions', + throttle: undefined, threat: getThreatMock(), version: 1, revision: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts index e204aeb7bc50f..0fbddc2a2236c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts @@ -14,6 +14,7 @@ import { RiskScore, RiskScoreMapping, RuleActionArrayCamel, + RuleActionNotifyWhen, RuleActionThrottle, RuleIntervalFrom, RuleIntervalTo, @@ -259,13 +260,6 @@ export interface CompleteRule { ruleConfig: SanitizedRuleConfig; } -export const notifyWhen = t.union([ - t.literal('onActionGroupChange'), - t.literal('onActiveAlert'), - t.literal('onThrottleInterval'), - t.null, -]); - export const allRuleTypes = t.union([ t.literal(SIGNALS_ID), t.literal(EQL_RULE_TYPE_ID), @@ -291,7 +285,7 @@ const internalRuleCreateRequired = t.type({ }); const internalRuleCreateOptional = t.partial({ throttle: t.union([RuleActionThrottle, t.null]), - notifyWhen, + notifyWhen: t.union([RuleActionNotifyWhen, t.null]), }); export const internalRuleCreate = t.intersection([ internalRuleCreateOptional, @@ -310,7 +304,7 @@ const internalRuleUpdateRequired = t.type({ }); const internalRuleUpdateOptional = t.partial({ throttle: t.union([RuleActionThrottle, t.null]), - notifyWhen, + notifyWhen: t.union([RuleActionNotifyWhen, t.null]), }); export const internalRuleUpdate = t.intersection([ internalRuleUpdateOptional, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index d3c4ae3938077..290368a66b4ed 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -28999,9 +28999,6 @@ "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription": "Sélectionner des index de menaces", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError": "Au minimum un modèle d'indexation est requis.", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "Tous les résultats", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText": "Sélectionnez le moment auquel les actions automatiques doivent être effectuées si une règle est évaluée comme vraie.", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery": "Sélectionnez le moment auquel les actions automatiques doivent être effectuées si une règle est évaluée comme vraie. Cette fréquence ne s'applique pas aux actions de réponse.", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel": "Fréquence des actions", "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "Impossible de créer des actions de règle. Vous ne disposez pas des autorisations \"Lire\" pour le plug-in \"Actions\".", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "Créer et activer la règle", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle": "Créer la règle sans l’activer", @@ -29959,12 +29956,9 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip": " Si vous dupliquez les exceptions, la liste des exceptions partagée sera dupliquée par référence et l'exception de la règle par défaut sera copiée et créée comme une nouvelle exception", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastTitle": "Règles dupliquées", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicateTitle": "Dupliquer", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.actionFrequencyDetail": "La fréquence des actions que vous sélectionnez ci-dessous est appliquée à toutes les actions (nouvelles et existantes) pour toutes les règles sélectionnées.", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle": "Ajouter des actions sur les règles", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel": "Écraser toutes les actions sur les règles sélectionnées", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail": "Les variables de règle peuvent affecter uniquement certaines règles sélectionnées, en fonction des types de règle (par exemple, \\u007b\\u007bcontext.rule.threshold\\u007d\\u007d affichera uniquement les valeurs des règles de seuil).", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText": "Sélectionnez le moment auquel les actions automatiques doivent être effectuées si une règle est évaluée comme vraie.", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel": "Fréquence des actions", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.warningCalloutMessage.buttonLabel": "Enregistrer", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle": "Appliquer le modèle de chronologie", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue": "Aucun", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 060f5364f947f..c1c8d3f14fe4e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -28978,9 +28978,6 @@ "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription": "脅威インデックスを選択", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError": "インデックスパターンが最低1つ必要です。", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "すべての結果", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。この頻度は対応アクションには適用されません。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel": "アクション頻度", "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "ルールアクションを作成できません。「Actions」プラグインの「読み取り」アクセス権がありません。", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "ルールを作成して有効にする", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle": "有効にせずにルールを作成", @@ -29938,12 +29935,9 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip": " 例外を複製する場合は、参照によって共有例外リストが複製されます。それから、デフォルトルール例外がコピーされ、新しい例外が作成されます", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastTitle": "ルールが複製されました", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicateTitle": "複製", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.actionFrequencyDetail": "以下で選択したアクション頻度は、すべての選択したルールのすべてのアクション(新規と既存のアクション)に適用されます。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle": "ルールアクションを追加", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel": "すべての選択したルールアクションを上書き", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail": "ルールタイプによっては、ルール変数が選択舌一部のルールにのみ影響する場合があります(例:\\u007b\\u007bcontext.rule.threshold\\u007d\\u007dはしきい値ルールの値のみを表示します)。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText": "ルールが true であると評価された場合に自動アクションを実行するタイミングを選択します。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel": "アクション頻度", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.warningCalloutMessage.buttonLabel": "保存", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle": "タイムラインテンプレートを適用", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue": "なし", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e577ba4753e8f..40989bc638cc5 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -28994,9 +28994,6 @@ "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription": "选择威胁索引", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError": "至少需要一种索引模式。", "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.thresholdField.thresholdFieldPlaceholderText": "所有结果", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpText": "选择在规则评估为 true 时应执行自动操作的时间。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleHelpTextWhenQuery": "选择在规则评估为 true 时应执行自动操作的时间。此频率不适用于响应操作。", - "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.fieldThrottleLabel": "操作频率", "xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges": "无法创建规则操作。您对“操作”插件没有“读”权限。", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithEnablingTitle": "创建并启用规则", "xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.completeWithoutEnablingTitle": "创建规则但不启用", @@ -29954,12 +29951,9 @@ "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.exceptionsConfirmation.tooltip": " 如果您复制例外,则会通过引用复制共享例外列表,然后复制默认规则例外,并将其创建为新例外", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicate.successToastTitle": "规则已复制", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.duplicateTitle": "复制", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.actionFrequencyDetail": "您在下面选择的操作频率将应用于所有选定规则的所有操作(新操作和现有操作)。", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.formTitle": "添加规则操作", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.overwriteCheckboxLabel": "覆盖所有选定规则操作", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.ruleVariablesDetail": "基于规则类型,规则变量可能仅影响您选择的某些规则(例如,\\u007b\\u007bcontext.rule.threshold\\u007d\\u007d 将仅显示阈值规则的值)。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleHelpText": "选择在规则评估为 true 时应执行自动操作的时间。", - "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.throttleLabel": "操作频率", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.addRuleActions.warningCalloutMessage.buttonLabel": "保存", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.formTitle": "应用时间线模板", "xpack.securitySolution.detectionEngine.rules.allRules.bulkActions.edit.applyTimelineTemplate.templateSelectorDefaultValue": "无", diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts index 62fa4d3786db6..2c6ae644d911c 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules.ts @@ -99,7 +99,6 @@ export default ({ getService }: FtrProviderContext) => { to: 'now', type: 'query', threat: [], - throttle: 'no_actions', exceptions_list: [], version: 1, revision: 0, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts index 4346087684fd4..e6adaa069b497 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules.ts @@ -7,7 +7,12 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; import { RuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { ROLES } from '@kbn/security-solution-plugin/common/test'; @@ -32,8 +37,15 @@ import { waitForSignalsToBePresent, getThresholdRuleForSignalTesting, waitForRulePartialFailure, + createRule, } from '../../utils'; import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -200,7 +212,6 @@ export default ({ getService }: FtrProviderContext) => { to: 'now', type: 'query', threat: [], - throttle: 'no_actions', exceptions_list: [], version: 1, }; @@ -566,5 +577,139 @@ export default ({ getService }: FtrProviderContext) => { expect(rule?.execution_summary?.last_execution.status).to.eql('partial failure'); }); }); + + describe('per-action frequencies', () => { + const createSingleRule = async (rule: RuleCreateProps) => { + const createdRule = await createRule(supertest, log, rule); + createdRule.actions = removeUUIDFromActions(createdRule.actions); + return createdRule; + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = actionsWithFrequencies; + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRule(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await createSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutput(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + const rule = removeServerGeneratedProperties(createdRule); + expect(rule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts index 6d0e79975bfd6..63d7e18367dc6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts @@ -10,7 +10,11 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_BULK_CREATE, DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, } from '@kbn/security-solution-plugin/common/constants'; +import { RuleCreateProps } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -27,6 +31,12 @@ import { removeServerGeneratedPropertiesIncludingRuleId, waitForRuleSuccess, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { @@ -276,6 +286,141 @@ export default ({ getService }: FtrProviderContext): void => { }, ]); }); + + describe('per-action frequencies', () => { + const bulkCreateSingleRule = async (rule: RuleCreateProps) => { + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_BULK_CREATE) + .set('kbn-xsrf', 'true') + .send([rule]) + .expect(200); + + const createdRule = body[0]; + createdRule.actions = removeUUIDFromActions(createdRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(createdRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(createdRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithoutFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = actionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = actionsWithFrequencies; + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(createdRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + const simpleRule = getSimpleRuleWithoutRuleId(); + simpleRule.throttle = throttle; + simpleRule.actions = someActionsWithFrequencies; + + const createdRule = await bulkCreateSingleRule(simpleRule); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(createdRule).to.eql(expectedRule); + }); + }); + }); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts index a394bef16cd22..e029c13aff8e5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules.ts @@ -176,6 +176,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts index fa83ceb9a19b1..8e0ad07a782c3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/delete_rules_bulk.ts @@ -309,6 +309,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -357,6 +358,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); expect(body[1].actions).to.eql([ @@ -368,6 +370,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts index c9cb430e144ba..72fb3631ac0c3 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/export_rules.ts @@ -8,6 +8,7 @@ import expect from 'expect'; import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { RuleResponse } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { binaryToString, @@ -208,10 +209,17 @@ export default ({ getService }: FtrProviderContext): void => { const outputRule1: ReturnType = { ...getSimpleRuleOutput('rule-1'), actions: [ - { ...action1, uuid: firstRule.actions[0].uuid }, - { ...action2, uuid: firstRule.actions[1].uuid }, + { + ...action1, + uuid: firstRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + ...action2, + uuid: firstRule.actions[1].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, ], - throttle: 'rule', }; expect(firstRule).toEqual(outputRule1); }); @@ -258,13 +266,23 @@ export default ({ getService }: FtrProviderContext): void => { const outputRule1: ReturnType = { ...getSimpleRuleOutput('rule-2'), - actions: [{ ...action, uuid: firstRule.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: firstRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; const outputRule2: ReturnType = { ...getSimpleRuleOutput('rule-1'), - actions: [{ ...action, uuid: secondRule.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: secondRule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; expect(firstRule).toEqual(outputRule1); expect(secondRule).toEqual(outputRule2); @@ -437,9 +455,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const firstRule = removeServerGeneratedProperties(firstRuleParsed); @@ -514,6 +532,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -523,9 +542,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const firstRule = removeServerGeneratedProperties(firstRuleParsed); @@ -631,6 +650,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -640,9 +660,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const outputRule2: ReturnType = { @@ -656,6 +676,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { group: 'default', @@ -665,9 +686,9 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; const firstRuleParsed = JSON.parse(body.toString().split(/\n/)[0]); const secondRuleParsed = JSON.parse(body.toString().split(/\n/)[1]); @@ -682,7 +703,8 @@ export default ({ getService }: FtrProviderContext): void => { }); }; -function expectToMatchRuleSchema(obj: unknown): void { +function expectToMatchRuleSchema(obj: RuleResponse): void { + expect(obj.throttle).toBeUndefined(); expect(obj).toEqual({ id: expect.any(String), rule_id: expect.any(String), @@ -718,7 +740,6 @@ function expectToMatchRuleSchema(obj: unknown): void { language: expect.any(String), index: expect.arrayContaining([]), query: expect.any(String), - throttle: expect.any(String), actions: expect.arrayContaining([]), }); } diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts index 7cd260697be72..348400580edd2 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/find_rules.ts @@ -126,8 +126,13 @@ export default ({ getService }: FtrProviderContext): void => { const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: body.data[0].actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: body.data[0].actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; body.data = [removeServerGeneratedProperties(body.data[0])]; @@ -171,8 +176,13 @@ export default ({ getService }: FtrProviderContext): void => { const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: body.data[0].actions[0].uuid }], - throttle: '1h', // <-- throttle makes this a scheduled action + actions: [ + { + ...action, + uuid: body.data[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }, + ], }; body.data = [removeServerGeneratedProperties(body.data[0])]; @@ -239,9 +249,9 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: hookAction.actionTypeId, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; body.data = [removeServerGeneratedProperties(body.data[0])]; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts index 6ba7871b1dbf5..9fd2542664f3b 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts @@ -75,8 +75,8 @@ export default ({ getService }: FtrProviderContext) => { expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); expect(ruleSO?.alert.actions).to.eql([]); - expect(ruleSO?.alert.throttle).to.eql('no_actions'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql(null); }); it('migrates legacy actions for rule with action run on every run', async () => { @@ -122,6 +122,7 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, { actionRef: 'action_1', @@ -133,10 +134,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[1].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('rule'); - expect(ruleSO?.alert.notifyWhen).to.eql('onActiveAlert'); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -197,6 +199,7 @@ export default ({ getService }: FtrProviderContext) => { actionRef: 'action_0', group: 'default', uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, { actionTypeId: '.slack', @@ -206,10 +209,11 @@ export default ({ getService }: FtrProviderContext) => { actionRef: 'action_1', group: 'default', uuid: ruleSO?.alert.actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('1h'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -269,10 +273,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('1d'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', @@ -327,10 +332,11 @@ export default ({ getService }: FtrProviderContext) => { to: ['test@test.com'], }, uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '7d', notifyWhen: 'onThrottleInterval' }, }, ]); - expect(ruleSO?.alert.throttle).to.eql('7d'); - expect(ruleSO?.alert.notifyWhen).to.eql('onThrottleInterval'); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); expect(ruleSO?.references).to.eql([ { id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts index 98b64726bbaca..ae434a50df98f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules.ts @@ -7,7 +7,13 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -24,7 +30,14 @@ import { getSimpleMlRule, createLegacyRuleAction, getLegacyActionSO, + getSimpleRuleWithoutRuleId, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -377,9 +390,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; - outputRule.throttle = '1h'; outputRule.revision = 1; expect(bodyToCompare).to.eql(outputRule); @@ -414,5 +427,168 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + + describe('patch per-action frequencies', () => { + const patchSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + const { body: patchedRule } = await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: ruleId, throttle, actions }) + .expect(200); + + patchedRule.actions = removeUUIDFromActions(patchedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(patchedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // patch a simple rule's `throttle` and `actions` + const patchedRule = await patchSingleRule( + createdRule.rule_id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(patchedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts index 1a26b17bca42e..2d80d17f9100e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/patch_rules_bulk.ts @@ -207,9 +207,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; - outputRule.throttle = '1h'; outputRule.revision = 1; expect(bodyToCompare).to.eql(outputRule); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts index e23db2120eb7e..185d820351459 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/perform_bulk_action.ts @@ -9,7 +9,6 @@ import expect from '@kbn/expect'; import { DETECTION_ENGINE_RULES_BULK_ACTION, DETECTION_ENGINE_RULES_URL, - NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE, } from '@kbn/security-solution-plugin/common/constants'; import type { RuleResponse } from '@kbn/security-solution-plugin/common/detection_engine/rule_schema'; @@ -172,7 +171,6 @@ export default ({ getService }: FtrProviderContext): void => { const rule = removeServerGeneratedProperties(JSON.parse(ruleJson)); expect(rule).to.eql({ ...getSimpleRuleOutput(), - throttle: 'rule', actions: [ { action_type_id: '.webhook', @@ -182,6 +180,7 @@ export default ({ getService }: FtrProviderContext): void => { body: '{"test":"a default action"}', }, uuid: rule.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ], }); @@ -335,6 +334,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: ruleBody.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); // we want to ensure rule is executing successfully, to prevent any AAD issues related to partial update of rule SO @@ -407,6 +407,7 @@ export default ({ getService }: FtrProviderContext): void => { message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: ruleBody.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -705,6 +706,7 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, ...(uuid ? { uuid } : {}), + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -1334,6 +1336,7 @@ export default ({ getService }: FtrProviderContext): void => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: setTagsRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); }); @@ -1618,6 +1621,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1675,6 +1679,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1786,6 +1791,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1838,6 +1844,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1892,12 +1899,17 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); const expectedRuleActions = [ - { ...defaultRuleAction, uuid: body.attributes.results.updated[0].actions[0].uuid }, + { + ...defaultRuleAction, + uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, { ...webHookActionMock, id: webHookConnector.id, action_type_id: '.webhook', uuid: body.attributes.results.updated[0].actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -1960,12 +1972,17 @@ export default ({ getService }: FtrProviderContext): void => { .expect(200); const expectedRuleActions = [ - { ...defaultRuleAction, uuid: body.attributes.results.updated[0].actions[0].uuid }, + { + ...defaultRuleAction, + uuid: body.attributes.results.updated[0].actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, { ...slackConnectorMockProps, id: slackConnector.id, action_type_id: '.slack', uuid: body.attributes.results.updated[0].actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]; @@ -2014,20 +2031,22 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].actions).to.eql([ - { ...defaultRuleAction, uuid: createdRule.actions[0].uuid }, - ]); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: readRule } = await fetchRule(ruleId).expect(200); expect(readRule.actions).to.eql([ - { ...defaultRuleAction, uuid: createdRule.actions[0].uuid }, + { + ...defaultRuleAction, + uuid: createdRule.actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, ]); }); - it('should change throttle if actions list in payload is empty', async () => { + it('should not change throttle if actions list in payload is empty', async () => { // create a new connector const webHookConnector = await createWebHookConnector(); @@ -2063,13 +2082,14 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].throttle).to.be('1h'); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: readRule } = await fetchRule(ruleId).expect(200); - expect(readRule.throttle).to.eql('1h'); + expect(readRule.throttle).to.eql(undefined); + expect(readRule.actions).to.eql(createdRule.actions); }); }); @@ -2117,6 +2137,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: editedRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); // version of prebuilt rule should not change @@ -2131,6 +2152,7 @@ export default ({ getService }: FtrProviderContext): void => { id: webHookConnector.id, action_type_id: '.webhook', uuid: readRule.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ]); expect(prebuiltRule.version).to.be(readRule.version); @@ -2207,7 +2229,7 @@ export default ({ getService }: FtrProviderContext): void => { }, ]; casesForEmptyActions.forEach(({ payloadThrottle }) => { - it(`throttle is set to NOTIFICATION_THROTTLE_NO_ACTIONS, if payload throttle="${payloadThrottle}" and actions list is empty`, async () => { + it(`should not update throttle, if payload throttle="${payloadThrottle}" and actions list is empty`, async () => { const ruleId = 'ruleId'; const createdRule = await createRule(supertest, log, { ...getSimpleRule(ruleId), @@ -2230,34 +2252,32 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - // Check that the updated rule is returned with the response - expect(body.attributes.results.updated[0].throttle).to.eql( - NOTIFICATION_THROTTLE_NO_ACTIONS - ); + // Check that the rule is skipped and was not updated + expect(body.attributes.results.skipped[0].id).to.eql(createdRule.id); // Check that the updates have been persisted const { body: rule } = await fetchRule(ruleId).expect(200); - expect(rule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(rule.throttle).to.eql(undefined); }); }); const casesForNonEmptyActions = [ { payloadThrottle: NOTIFICATION_THROTTLE_RULE, - expectedThrottle: NOTIFICATION_THROTTLE_RULE, + expectedThrottle: undefined, }, { payloadThrottle: '1h', - expectedThrottle: '1h', + expectedThrottle: undefined, }, { payloadThrottle: '1d', - expectedThrottle: '1d', + expectedThrottle: undefined, }, { payloadThrottle: '7d', - expectedThrottle: '7d', + expectedThrottle: undefined, }, ]; [BulkActionEditType.set_rule_actions, BulkActionEditType.add_rule_actions].forEach( @@ -2295,10 +2315,23 @@ export default ({ getService }: FtrProviderContext): void => { // Check that the updated rule is returned with the response expect(body.attributes.results.updated[0].throttle).to.eql(expectedThrottle); + const expectedActions = body.attributes.results.updated[0].actions.map( + (action: any) => ({ + ...action, + frequency: { + summary: true, + throttle: payloadThrottle !== 'rule' ? payloadThrottle : null, + notifyWhen: + payloadThrottle !== 'rule' ? 'onThrottleInterval' : 'onActiveAlert', + }, + }) + ); + // Check that the updates have been persisted const { body: rule } = await fetchRule(ruleId).expect(200); expect(rule.throttle).to.eql(expectedThrottle); + expect(rule.actions).to.eql(expectedActions); }); }); } @@ -2311,11 +2344,11 @@ export default ({ getService }: FtrProviderContext): void => { const cases = [ { payload: { throttle: '1d' }, - expected: { notifyWhen: 'onThrottleInterval' }, + expected: { notifyWhen: null }, }, { payload: { throttle: NOTIFICATION_THROTTLE_RULE }, - expected: { notifyWhen: 'onActiveAlert' }, + expected: { notifyWhen: null }, }, ]; cases.forEach(({ payload, expected }) => { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts index d2162e02d443e..31904342e294f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/read_rules.ts @@ -135,8 +135,13 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeServerGeneratedProperties(body); const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: bodyToCompare.actions[0].uuid }], - throttle: 'rule', + actions: [ + { + ...action, + uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], }; expect(bodyToCompare).to.eql(ruleWithActions); }); @@ -174,8 +179,13 @@ export default ({ getService }: FtrProviderContext) => { const bodyToCompare = removeServerGeneratedProperties(body); const ruleWithActions: ReturnType = { ...getSimpleRuleOutput(), - actions: [{ ...action, uuid: bodyToCompare.actions[0].uuid }], - throttle: '1h', // <-- throttle makes this a scheduled action + actions: [ + { + ...action, + uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }, + ], }; expect(bodyToCompare).to.eql(ruleWithActions); }); @@ -236,9 +246,9 @@ export default ({ getService }: FtrProviderContext) => { 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', }, action_type_id: hookAction.actionTypeId, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, }, ], - throttle: '1h', }; expect(bodyToCompare).to.eql(ruleWithActions); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts index eedfaee3ec531..4b218d07b7613 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/throttle.ts @@ -56,7 +56,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('creating a rule', () => { - it('When creating a new action and attaching it to a rule, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating a new action and attaching it to a rule, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -66,10 +66,16 @@ export default ({ getService }: FtrProviderContext) => { const rule = await createRule(supertest, log, getRuleWithWebHookAction(hookAction.id)); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: null, + notify_when: 'onActiveAlert', + }); + expect(notifyWhen).to.eql(null); }); it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { @@ -105,7 +111,7 @@ export default ({ getService }: FtrProviderContext) => { expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { const ruleWithThrottle: RuleCreateProps = { ...getSimpleRule(), throttle: NOTIFICATION_THROTTLE_RULE, @@ -115,7 +121,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -125,10 +131,10 @@ export default ({ getService }: FtrProviderContext) => { throttle: NOTIFICATION_THROTTLE_RULE, }; const rule = await createRule(supertest, log, ruleWithThrottle); - expect(rule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(rule.throttle).to.eql(undefined); }); - it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onActiveAlert"', async () => { + it('When creating throttle with "NOTIFICATION_THROTTLE_RULE" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -142,13 +148,19 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: null, + notify_when: 'onActiveAlert', + }); + expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "1h" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onThrottleInterval"', async () => { + it('When creating throttle with "1h" set and no actions, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { const ruleWithThrottle: RuleCreateProps = { ...getSimpleRule(), throttle: '1h', @@ -158,10 +170,10 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onThrottleInterval'); + expect(notifyWhen).to.eql(null); }); - it('When creating throttle with "1h" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to "onThrottleInterval"', async () => { + it('When creating throttle with "1h" set and actions set, the rule should have its kibana alerting "mute_all" set to "false" and notify_when set to null', async () => { // create a new action const { body: hookAction } = await supertest .post('/api/actions/action') @@ -175,10 +187,16 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const { - body: { mute_all: muteAll, notify_when: notifyWhen }, + body: { mute_all: muteAll, notify_when: notifyWhen, actions }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onThrottleInterval'); + expect(actions.length).to.eql(1); + expect(actions[0].frequency).to.eql({ + summary: true, + throttle: '1h', + notify_when: 'onThrottleInterval', + }); + expect(notifyWhen).to.eql(null); }); }); @@ -193,7 +211,7 @@ export default ({ getService }: FtrProviderContext) => { const rule = await createRule(supertest, log, getRuleWithWebHookAction(hookAction.id)); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(readRule.throttle).to.eql(undefined); }); it('When creating throttle with "NOTIFICATION_THROTTLE_NO_ACTIONS" set and no actions, we should return "NOTIFICATION_THROTTLE_NO_ACTIONS" when doing a read', async () => { @@ -203,7 +221,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -214,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, log, ruleWithThrottle); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); it('When creating a new action and attaching it to a rule, if we change the alert to a "muteAll" through the kibana alerting API, we should get back "NOTIFICATION_THROTTLE_NO_ACTIONS" ', async () => { @@ -232,7 +250,7 @@ export default ({ getService }: FtrProviderContext) => { .send() .expect(204); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); }); @@ -249,7 +267,7 @@ export default ({ getService }: FtrProviderContext) => { await createRule(supertest, log, ruleWithWebHookAction); ruleWithWebHookAction.name = 'some other name'; const updated = await updateRule(supertest, log, ruleWithWebHookAction); - expect(updated.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(updated.throttle).to.eql(undefined); }); it('will not change the "muteAll" or "notifyWhen" if we update some part of the rule', async () => { @@ -268,7 +286,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${updated.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -284,7 +302,7 @@ export default ({ getService }: FtrProviderContext) => { await createRule(supertest, log, ruleWithWebHookAction); ruleWithWebHookAction.actions = []; const updated = await updateRule(supertest, log, ruleWithWebHookAction); - expect(updated.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(updated.throttle).to.eql(undefined); }); }); @@ -306,7 +324,7 @@ export default ({ getService }: FtrProviderContext) => { .send({ rule_id: rule.rule_id, name: 'some other name' }) .expect(200); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_RULE); + expect(readRule.throttle).to.eql(undefined); }); it('will not change the "muteAll" or "notifyWhen" if we patch part of the rule', async () => { @@ -329,7 +347,7 @@ export default ({ getService }: FtrProviderContext) => { body: { mute_all: muteAll, notify_when: notifyWhen }, } = await supertest.get(`/api/alerting/rule/${rule.id}`); expect(muteAll).to.eql(false); - expect(notifyWhen).to.eql('onActiveAlert'); + expect(notifyWhen).to.eql(null); }); // NOTE: This shows A side effect of how we do not set data on side cars anymore where the user is told they have no actions since the array is empty. @@ -350,7 +368,7 @@ export default ({ getService }: FtrProviderContext) => { .send({ rule_id: rule.rule_id, actions: [] }) .expect(200); const readRule = await getRule(supertest, log, rule.rule_id); - expect(readRule.throttle).to.eql(NOTIFICATION_THROTTLE_NO_ACTIONS); + expect(readRule.throttle).to.eql(undefined); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts index 8396f72a9bb08..82b23e8b381d1 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts @@ -7,7 +7,13 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + NOTIFICATION_DEFAULT_FREQUENCY, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, +} from '@kbn/security-solution-plugin/common/constants'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { @@ -28,7 +34,14 @@ import { createLegacyRuleAction, getThresholdRuleForSignalTesting, getLegacyActionSO, + getSimpleRuleWithoutRuleId, } from '../../utils'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -185,8 +198,6 @@ export default ({ getService }: FtrProviderContext) => { outputRule.revision = 1; // Expect an empty array outputRule.actions = []; - // Expect "no_actions" - outputRule.throttle = 'no_actions'; const bodyToCompare = removeServerGeneratedPropertiesIncludingRuleId(body); expect(bodyToCompare).to.eql(outputRule); }); @@ -221,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { id: connector.body.id, action_type_id: connector.body.connector_type_id, params: { - message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, }; // update a simple rule's name @@ -229,7 +240,6 @@ export default ({ getService }: FtrProviderContext) => { updatedRule.rule_id = createRuleBody.rule_id; updatedRule.name = 'some other name'; updatedRule.actions = [action1]; - updatedRule.throttle = '1d'; delete updatedRule.id; const { body } = await supertest @@ -249,13 +259,12 @@ export default ({ getService }: FtrProviderContext) => { group: 'default', id: connector.body.id, params: { - message: - 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions![0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]; - outputRule.throttle = '1d'; expect(bodyToCompare).to.eql(outputRule); @@ -662,6 +671,176 @@ export default ({ getService }: FtrProviderContext) => { expect(outputRule.saved_id).to.be(undefined); }); }); + + describe('per-action frequencies', () => { + const updateSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + // update a simple rule's `throttle` and `actions` + const ruleToUpdate = getSimpleRuleUpdate(); + ruleToUpdate.throttle = throttle; + ruleToUpdate.actions = actions; + ruleToUpdate.id = ruleId; + delete ruleToUpdate.rule_id; + + const { body: updatedRule } = await supertest + .put(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleToUpdate) + .expect(200); + + updatedRule.actions = removeUUIDFromActions(updatedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(updatedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await updateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts index 0c8dcacd0ebbf..5da2e8f1c10cd 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts @@ -11,8 +11,12 @@ import { RuleResponse } from '@kbn/security-solution-plugin/common/detection_eng import { DETECTION_ENGINE_RULES_URL, DETECTION_ENGINE_RULES_BULK_UPDATE, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + NOTIFICATION_DEFAULT_FREQUENCY, } from '@kbn/security-solution-plugin/common/constants'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; +import { RuleActionArray, RuleActionThrottle } from '@kbn/securitysolution-io-ts-alerting-types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -25,7 +29,16 @@ import { getSimpleRule, createLegacyRuleAction, getLegacyActionSO, + removeServerGeneratedPropertiesIncludingRuleId, + getSimpleRuleWithoutRuleId, + getSimpleRuleOutputWithoutRuleId, } from '../../utils'; +import { removeUUIDFromActions } from '../../utils/remove_uuid_from_actions'; +import { + getActionsWithFrequencies, + getActionsWithoutFrequencies, + getSomeActionsWithFrequencies, +} from '../../utils/get_rule_actions'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { @@ -138,7 +151,7 @@ export default ({ getService }: FtrProviderContext) => { id: connector.body.id, action_type_id: connector.body.connector_type_id, params: { - message: 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, }; const [rule1, rule2] = await Promise.all([ @@ -160,12 +173,10 @@ export default ({ getService }: FtrProviderContext) => { const updatedRule1 = getSimpleRuleUpdate('rule-1'); updatedRule1.name = 'some other name'; updatedRule1.actions = [action1]; - updatedRule1.throttle = '1d'; const updatedRule2 = getSimpleRuleUpdate('rule-2'); updatedRule2.name = 'some other name'; updatedRule2.actions = [action1]; - updatedRule2.throttle = '1d'; // update both rule names const { body }: { body: RuleResponse[] } = await supertest @@ -189,13 +200,12 @@ export default ({ getService }: FtrProviderContext) => { group: 'default', id: connector.body.id, params: { - message: - 'Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts', + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', }, uuid: bodyToCompare.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, }, ]; - outputRule.throttle = '1d'; expect(bodyToCompare).to.eql(outputRule); }); @@ -247,7 +257,6 @@ export default ({ getService }: FtrProviderContext) => { outputRule.name = 'some other name'; outputRule.revision = 1; outputRule.actions = []; - outputRule.throttle = 'no_actions'; const bodyToCompare = removeServerGeneratedProperties(response); expect(bodyToCompare).to.eql(outputRule); }); @@ -603,5 +612,177 @@ export default ({ getService }: FtrProviderContext) => { ]); }); }); + + describe('bulk per-action frequencies', () => { + const bulkUpdateSingleRule = async ( + ruleId: string, + throttle: RuleActionThrottle | undefined, + actions: RuleActionArray + ) => { + // update a simple rule's `throttle` and `actions` + const ruleToUpdate = getSimpleRuleUpdate(); + ruleToUpdate.throttle = throttle; + ruleToUpdate.actions = actions; + ruleToUpdate.id = ruleId; + delete ruleToUpdate.rule_id; + + const { body } = await supertest + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) + .set('kbn-xsrf', 'true') + .send([ruleToUpdate]) + .expect(200); + + const updatedRule = body[0]; + updatedRule.actions = removeUUIDFromActions(updatedRule.actions); + return removeServerGeneratedPropertiesIncludingRuleId(updatedRule); + }; + + describe('actions without frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it sets each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['300s', '5m', '3h', '4d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and sets it as a frequency of each action`, async () => { + const actionsWithoutFrequencies = await getActionsWithoutFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithoutFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithoutFrequencies.map((action) => ({ + ...action, + frequency: { summary: true, throttle, notifyWhen: 'onThrottleInterval' }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('actions with frequencies', () => { + [ + undefined, + NOTIFICATION_THROTTLE_NO_ACTIONS, + NOTIFICATION_THROTTLE_RULE, + '321s', + '6m', + '10h', + '2d', + ].forEach((throttle) => { + it(`it does not change actions frequency attributes when 'throttle' is '${throttle}'`, async () => { + const actionsWithFrequencies = await getActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + actionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = actionsWithFrequencies; + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + + describe('some actions with frequencies', () => { + [undefined, NOTIFICATION_THROTTLE_NO_ACTIONS, NOTIFICATION_THROTTLE_RULE].forEach( + (throttle) => { + it(`it overrides each action's frequency attribute to default value when 'throttle' is ${throttle}`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? NOTIFICATION_DEFAULT_FREQUENCY, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + } + ); + + // Action throttle cannot be shorter than the schedule interval which is by default is 5m + ['430s', '7m', '1h', '8d'].forEach((throttle) => { + it(`it correctly transforms 'throttle = ${throttle}' and overrides frequency attribute of each action`, async () => { + const someActionsWithFrequencies = await getSomeActionsWithFrequencies(supertest); + + // create simple rule + const createdRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); + + // update a simple rule's `throttle` and `actions` + const updatedRule = await bulkUpdateSingleRule( + createdRule.id, + throttle, + someActionsWithFrequencies + ); + + const expectedRule = getSimpleRuleOutputWithoutRuleId(); + expectedRule.revision = 1; + expectedRule.actions = someActionsWithFrequencies.map((action) => ({ + ...action, + frequency: action.frequency ?? { + summary: true, + throttle, + notifyWhen: 'onThrottleInterval', + }, + })); + + expect(updatedRule).to.eql(expectedRule); + }); + }); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts index af66123014383..31b8c4571e2f5 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_complex_rule_output.ts @@ -92,7 +92,6 @@ export const getComplexRuleOutput = (ruleId = 'rule-1'): Partial = 'http://www.example.com/some-article-about-attack', 'Some plain text string here explaining why this is a valid thing to look out for', ], - throttle: 'no_actions', timeline_id: 'timeline_id', timeline_title: 'timeline_title', updated_by: 'elastic', diff --git a/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts b/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts new file mode 100644 index 0000000000000..519cd9136c604 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/get_rule_actions.ts @@ -0,0 +1,96 @@ +/* + * 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 SuperTest from 'supertest'; + +import { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; +import { getSlackAction } from './get_slack_action'; +import { getWebHookAction } from './get_web_hook_action'; + +const createConnector = async ( + supertest: SuperTest.SuperTest, + payload: Record +) => + (await supertest.post('/api/actions/action').set('kbn-xsrf', 'true').send(payload).expect(200)) + .body; +const createWebHookConnector = (supertest: SuperTest.SuperTest) => + createConnector(supertest, getWebHookAction()); +const createSlackConnector = (supertest: SuperTest.SuperTest) => + createConnector(supertest, getSlackAction()); + +export const getActionsWithoutFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + }, + ]; +}; + +export const getActionsWithFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + frequency: { summary: false, throttle: '3d', notifyWhen: 'onThrottleInterval' }, + }, + ]; +}; + +export const getSomeActionsWithFrequencies = async ( + supertest: SuperTest.SuperTest +): Promise => { + const webHookAction = await createWebHookConnector(supertest); + const slackConnector = await createSlackConnector(supertest); + return [ + { + group: 'default', + id: webHookAction.id, + action_type_id: '.webhook', + params: { message: 'Email message' }, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + frequency: { summary: false, throttle: '3d', notifyWhen: 'onThrottleInterval' }, + }, + { + group: 'default', + id: slackConnector.id, + action_type_id: '.slack', + params: { message: 'Slack message' }, + }, + ]; +}; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts index 9826d9a2b98b4..cdadcce39e0a8 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output.ts @@ -40,7 +40,7 @@ export const getMockSharedResponseSchema = ( tags: [], to: 'now', threat: [], - throttle: 'no_actions', + throttle: undefined, exceptions_list: [], version: 1, revision: 0, diff --git a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts index 25776fefd5687..7ecee679e50b3 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_simple_rule_output_with_web_hook_action.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { NOTIFICATION_DEFAULT_FREQUENCY } from '@kbn/security-solution-plugin/common/constants'; import { getSimpleRuleOutput } from './get_simple_rule_output'; import { RuleWithoutServerGeneratedProperties } from './remove_server_generated_properties'; @@ -13,7 +14,6 @@ export const getSimpleRuleOutputWithWebHookAction = ( uuid: string ): RuleWithoutServerGeneratedProperties => ({ ...getSimpleRuleOutput(), - throttle: 'rule', actions: [ { action_type_id: '.webhook', @@ -23,6 +23,7 @@ export const getSimpleRuleOutputWithWebHookAction = ( body: '{}', }, uuid, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, }, ], }); diff --git a/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts b/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts new file mode 100644 index 0000000000000..08d95bc750212 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/utils/remove_uuid_from_actions.ts @@ -0,0 +1,14 @@ +/* + * 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 { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; + +export const removeUUIDFromActions = (actions: RuleActionArray): RuleActionArray => { + return actions.map(({ uuid, ...restOfAction }) => ({ + ...restOfAction, + })); +}; From 158ea8546829cbeac3246c75762901382c0ac232 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Sun, 23 Apr 2023 13:01:19 -0400 Subject: [PATCH 25/29] feat(slo): Synchronize cursor on SLO charts (#155560) --- .../pages/slo_details/components/wide_chart.tsx | 16 ++++++++++++++-- .../pages/slo_details/slo_details.test.tsx | 2 +- .../utils/kibana_react.storybook_decorator.tsx | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx index 7fc212dbd4275..dbbacc6b1be91 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx @@ -15,10 +15,11 @@ import { ScaleType, Settings, } from '@elastic/charts'; -import React from 'react'; +import React, { useRef } from 'react'; import { EuiIcon, EuiLoadingChart, useEuiTheme } from '@elastic/eui'; import numeral from '@elastic/numeral'; import moment from 'moment'; +import { useActiveCursor } from '@kbn/charts-plugin/public'; import { ChartData } from '../../../typings'; import { useKibana } from '../../../utils/kibana_react'; @@ -45,18 +46,29 @@ export function WideChart({ chart, data, id, isLoading, state }: Props) { const color = state === 'error' ? euiTheme.colors.danger : euiTheme.colors.success; const ChartComponent = chart === 'area' ? AreaSeries : LineSeries; + const chartRef = useRef(null); + const handleCursorUpdate = useActiveCursor(charts.activeCursor, chartRef, { + isDateHistogram: true, + }); + if (isLoading) { return ; } return ( - + } + onPointerUpdate={handleCursorUpdate} + externalPointerEvents={{ + tooltip: { visible: true }, + }} + pointerUpdateDebounce={0} + pointerUpdateTrigger={'x'} /> { useKibanaMock.mockReturnValue({ services: { application: { navigateToUrl: mockNavigate }, - charts: chartPluginMock.createSetupContract(), + charts: chartPluginMock.createStartContract(), http: { basePath: { prepend: mockBasePathPrepend, diff --git a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx index 5a562958cb299..a0e5a1f0e037b 100644 --- a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx +++ b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx @@ -65,6 +65,7 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { useChartsBaseTheme: () => {}, useChartsTheme: () => {}, }, + activeCursor: () => {}, }, data: {}, dataViews: { From 9c3111e57f9d046012bf66222dc71c40d3afbd8a Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Sun, 23 Apr 2023 13:02:46 -0400 Subject: [PATCH 26/29] chore(slo): change no rule badge style (#155546) --- .../slos/components/badges/slo_rules_badge.tsx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx index c60617152cb00..2b80e8852bdae 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_rules_badge.tsx @@ -6,9 +6,10 @@ */ import React from 'react'; -import { EuiBadge, EuiIcon, EuiToolTip } from '@elastic/eui'; +import { EuiBadge, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; + import { SloRule } from '../../../../hooks/slo/use_fetch_rules_for_slo'; export interface Props { @@ -26,16 +27,9 @@ export function SloRulesBadge({ rules, onClick }: Props) { })} display="block" > - - - + + + ); } From 50bb555e7af7b6a9b73799461d3278fb7d42352f Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Sun, 23 Apr 2023 13:04:12 -0400 Subject: [PATCH 27/29] feat(slo): add feedback button (#155468) --- .../slo/feedback_button/feedback_button.tsx | 28 +++++++++++++++++++ .../public/pages/slo_details/slo_details.tsx | 2 ++ .../public/pages/slo_edit/slo_edit.tsx | 3 +- .../observability/public/pages/slos/slos.tsx | 4 ++- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx diff --git a/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx b/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx new file mode 100644 index 0000000000000..f72e4b008f390 --- /dev/null +++ b/x-pack/plugins/observability/public/components/slo/feedback_button/feedback_button.tsx @@ -0,0 +1,28 @@ +/* + * 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 { EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +const SLO_FEEDBACK_LINK = 'https://ela.st/slo-feedback'; + +export function FeedbackButton() { + return ( + + {i18n.translate('xpack.observability.slo.feedbackButtonLabel', { + defaultMessage: 'Tell us what you think!', + })} + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx index bec6e6608abcc..48401ca29f94e 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.tsx @@ -26,6 +26,7 @@ import { paths } from '../../config/paths'; import type { SloDetailsPathParams } from './types'; import type { ObservabilityAppServices } from '../../application/types'; import { AutoRefreshButton } from '../slos/components/auto_refresh_button'; +import { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SloDetailsPage() { const { @@ -69,6 +70,7 @@ export function SloDetailsPage() { isAutoRefreshing={isAutoRefreshing} onClick={handleToggleAutoRefresh} />, + , ], bottomBorder: false, }} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx index 57e27810815d6..ede2d15d3f23d 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx @@ -16,6 +16,7 @@ import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; import { useLicense } from '../../hooks/use_license'; import { SloEditForm } from './components/slo_edit_form'; +import { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SloEditPage() { const { @@ -58,7 +59,7 @@ export function SloEditPage() { : i18n.translate('xpack.observability.sloCreatePageTitle', { defaultMessage: 'Create new SLO', }), - rightSideItems: [], + rightSideItems: [], bottomBorder: false, }} data-test-subj="slosEditPage" diff --git a/x-pack/plugins/observability/public/pages/slos/slos.tsx b/x-pack/plugins/observability/public/pages/slos/slos.tsx index e224158ef9c2a..b81998d8452de 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.tsx @@ -21,6 +21,7 @@ import { AutoRefreshButton } from './components/auto_refresh_button'; import { paths } from '../../config/paths'; import type { ObservabilityAppServices } from '../../application/types'; import { HeaderTitle } from './components/header_title'; +import { FeedbackButton } from '../../components/slo/feedback_button/feedback_button'; export function SlosPage() { const { @@ -69,7 +70,7 @@ export function SlosPage() { rightSideItems: [ , + , ], bottomBorder: false, }} From 743325dc85c39209131112575accde14577bae36 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 24 Apr 2023 01:02:05 -0400 Subject: [PATCH 28/29] [api-docs] 2023-04-24 Daily api_docs build (#155584) Generated by https://buildkite.com/elastic/kibana-api-docs-daily/builds/317 --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.devdocs.json | 71 +- api_docs/alerting.mdx | 4 +- api_docs/apm.devdocs.json | 6 + api_docs/apm.mdx | 2 +- api_docs/asset_manager.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_chat.mdx | 2 +- api_docs/cloud_data_migration.mdx | 2 +- api_docs/cloud_defend.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/content_management.devdocs.json | 140 ++- api_docs/content_management.mdx | 4 +- api_docs/controls.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 2 +- api_docs/deprecations_by_plugin.mdx | 2 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/ecs_data_quality_dashboard.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.devdocs.json | 6 +- api_docs/event_log.mdx | 2 +- api_docs/exploratory_view.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/files_management.mdx | 2 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.devdocs.json | 32 +- api_docs/guided_onboarding.mdx | 4 +- api_docs/home.mdx | 2 +- api_docs/image_embeddable.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerting_state_types.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_alerts_as_data_utils.mdx | 2 +- api_docs/kbn_alerts_ui_shared.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.devdocs.json | 8 + api_docs/kbn_analytics_client.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_analytics_shippers_gainsight.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- .../kbn_apm_synthtrace_client.devdocs.json | 18 +- api_docs/kbn_apm_synthtrace_client.mdx | 4 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_cases_components.mdx | 2 +- api_docs/kbn_cell_actions.mdx | 2 +- api_docs/kbn_chart_expressions_common.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_code_editor.mdx | 2 +- api_docs/kbn_code_editor_mocks.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_content_editor.mdx | 2 +- ...content_management_table_list.devdocs.json | 2 +- .../kbn_content_management_table_list.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_apps_server_internal.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_browser.mdx | 2 +- ..._core_custom_branding_browser_internal.mdx | 2 +- ...kbn_core_custom_branding_browser_mocks.mdx | 2 +- api_docs/kbn_core_custom_branding_common.mdx | 2 +- api_docs/kbn_core_custom_branding_server.mdx | 2 +- ...n_core_custom_branding_server_internal.mdx | 2 +- .../kbn_core_custom_branding_server_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...re_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_resources_server.mdx | 2 +- ...bn_core_http_resources_server_internal.mdx | 2 +- .../kbn_core_http_resources_server_mocks.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_server.mdx | 2 +- api_docs/kbn_core_lifecycle_server_mocks.mdx | 2 +- api_docs/kbn_core_logging_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_common_internal.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_server.mdx | 2 +- api_docs/kbn_core_plugins_server_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_rendering_server_internal.mdx | 2 +- api_docs/kbn_core_rendering_server_mocks.mdx | 2 +- api_docs/kbn_core_root_server_internal.mdx | 2 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- .../kbn_core_saved_objects_api_server.mdx | 2 +- ...core_saved_objects_api_server_internal.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ...ore_saved_objects_base_server_internal.mdx | 2 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_test_helpers_kbn_server.mdx | 2 +- ...n_core_test_helpers_so_type_serializer.mdx | 2 +- api_docs/kbn_core_test_helpers_test_utils.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_cypress_config.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_dom_drag_drop.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_ecs.mdx | 2 +- api_docs/kbn_ecs_data_quality_dashboard.mdx | 2 +- api_docs/kbn_es.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.devdocs.json | 4 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_expandable_flyout.devdocs.json | 470 +++++++++- api_docs/kbn_expandable_flyout.mdx | 10 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_generate_csv.mdx | 2 +- api_docs/kbn_generate_csv_types.mdx | 2 +- api_docs/kbn_guided_onboarding.devdocs.json | 44 +- api_docs/kbn_guided_onboarding.mdx | 4 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_health_gateway_server.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_i18n_react.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_json_ast.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- .../kbn_language_documentation_popover.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_date_picker.mdx | 2 +- api_docs/kbn_ml_error_utils.devdocs.json | 800 ++++++++++++++++++ api_docs/kbn_ml_error_utils.mdx | 39 + api_docs/kbn_ml_is_defined.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_local_storage.mdx | 2 +- api_docs/kbn_ml_nested_property.mdx | 2 +- api_docs/kbn_ml_number_utils.mdx | 2 +- api_docs/kbn_ml_query_utils.mdx | 2 +- api_docs/kbn_ml_random_sampler_utils.mdx | 2 +- api_docs/kbn_ml_route_utils.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_ml_trained_models_utils.mdx | 2 +- api_docs/kbn_ml_url_state.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_object_versioning.devdocs.json | 44 + api_docs/kbn_object_versioning.mdx | 4 +- api_docs/kbn_observability_alert_details.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_repo_file_maps.mdx | 2 +- api_docs/kbn_repo_linter.mdx | 2 +- api_docs/kbn_repo_path.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_reporting_common.mdx | 2 +- api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- api_docs/kbn_saved_objects_settings.mdx | 2 +- api_docs/kbn_security_solution_side_nav.mdx | 2 +- ...kbn_security_solution_storybook_config.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_data_table.mdx | 2 +- api_docs/kbn_securitysolution_ecs.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...ritysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_grouping.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ...solution_io_ts_alerting_types.devdocs.json | 524 +++++++++++- ..._securitysolution_io_ts_alerting_types.mdx | 4 +- ...ritysolution_io_ts_list_types.devdocs.json | 336 +++++++- .../kbn_securitysolution_io_ts_list_types.mdx | 4 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- ...kbn_securitysolution_list_api.devdocs.json | 51 ++ api_docs/kbn_securitysolution_list_api.mdx | 4 +- .../kbn_securitysolution_list_constants.mdx | 2 +- ...n_securitysolution_list_hooks.devdocs.json | 64 +- api_docs/kbn_securitysolution_list_hooks.mdx | 4 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_solution.mdx | 2 +- ...ared_ux_avatar_user_profile_components.mdx | 2 +- .../kbn_shared_ux_button_exit_full_screen.mdx | 2 +- ...hared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_context.mdx | 2 +- api_docs/kbn_shared_ux_file_image.mdx | 2 +- api_docs/kbn_shared_ux_file_image_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_picker.mdx | 2 +- api_docs/kbn_shared_ux_file_types.mdx | 2 +- api_docs/kbn_shared_ux_file_upload.mdx | 2 +- api_docs/kbn_shared_ux_file_util.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_markdown.mdx | 2 +- api_docs/kbn_shared_ux_markdown_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_prompt_not_found.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_ts_projects.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_actions_browser.mdx | 2 +- api_docs/kbn_ui_shared_deps_src.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_url_state.devdocs.json | 99 +++ api_docs/kbn_url_state.mdx | 30 + api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.devdocs.json | 4 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.devdocs.json | 35 - api_docs/ml.mdx | 4 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/notifications.mdx | 2 +- api_docs/observability.mdx | 2 +- api_docs/observability_shared.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 34 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.devdocs.json | 25 + api_docs/triggers_actions_ui.mdx | 4 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_field_list.mdx | 2 +- api_docs/unified_histogram.mdx | 2 +- api_docs/unified_search.devdocs.json | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 522 files changed, 3240 insertions(+), 674 deletions(-) create mode 100644 api_docs/kbn_ml_error_utils.devdocs.json create mode 100644 api_docs/kbn_ml_error_utils.mdx create mode 100644 api_docs/kbn_url_state.devdocs.json create mode 100644 api_docs/kbn_url_state.mdx diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 6bfdc25b53149..9f6783794ad2c 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 4cf73a72168eb..f41313a90546b 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index c0fa0ef3e4c02..50cb3ba36668e 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index cfec38128269c..264f3c10d43f3 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -5261,12 +5261,20 @@ { "parentPluginId": "alerting", "id": "def-common.AlertsFilter.query", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "query", "description": [], "signature": [ - "{ kql: string; dsl?: string | undefined; } | null" + "{ kql: string; filters: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; dsl?: string | undefined; } | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -5275,7 +5283,7 @@ { "parentPluginId": "alerting", "id": "def-common.AlertsFilter.timeframe", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "timeframe", "description": [], @@ -5287,7 +5295,7 @@ "section": "def-common.AlertsFilterTimeframe", "text": "AlertsFilterTimeframe" }, - " | null" + " | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -5541,6 +5549,20 @@ "path": "x-pack/plugins/alerting/common/alert_summary.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AlertStatus.maintenanceWindowIds", + "type": "Array", + "tags": [], + "label": "maintenanceWindowIds", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/alerting/common/alert_summary.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -5755,6 +5777,17 @@ "path": "x-pack/plugins/alerting/common/alert_summary.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.AlertSummary.revision", + "type": "number", + "tags": [], + "label": "revision", + "description": [], + "path": "x-pack/plugins/alerting/common/alert_summary.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -6322,6 +6355,20 @@ "path": "x-pack/plugins/alerting/common/execution_log_types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "alerting", + "id": "def-common.IExecutionLog.maintenance_window_ids", + "type": "Array", + "tags": [], + "label": "maintenance_window_ids", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/alerting/common/execution_log_types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -8864,12 +8911,20 @@ { "parentPluginId": "alerting", "id": "def-common.SanitizedAlertsFilter.query", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "query", "description": [], "signature": [ - "{ kql: string; } | null" + "{ kql: string; filters: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]; } | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, @@ -8878,7 +8933,7 @@ { "parentPluginId": "alerting", "id": "def-common.SanitizedAlertsFilter.timeframe", - "type": "CompoundType", + "type": "Object", "tags": [], "label": "timeframe", "description": [], @@ -8890,7 +8945,7 @@ "section": "def-common.AlertsFilterTimeframe", "text": "AlertsFilterTimeframe" }, - " | null" + " | undefined" ], "path": "x-pack/plugins/alerting/common/rule.ts", "deprecated": false, diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index ad9cd7c82bac4..05cd2479d10f4 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 603 | 1 | 582 | 42 | +| 606 | 1 | 585 | 42 | ## Client diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index 93eeed1f13395..f3585cfd924b4 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -4193,6 +4193,8 @@ "AggregationType", ".P99>]>; serviceName: ", "StringC", + "; errorGroupingKey: ", + "StringC", "; transactionType: ", "StringC", "; transactionName: ", @@ -4269,6 +4271,8 @@ "AggregationType", ".P99>]>; serviceName: ", "StringC", + "; errorGroupingKey: ", + "StringC", "; transactionType: ", "StringC", "; transactionName: ", @@ -4343,6 +4347,8 @@ "AggregationType", ".P99>]>; serviceName: ", "StringC", + "; errorGroupingKey: ", + "StringC", "; transactionType: ", "StringC", "; transactionName: ", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index a5226b9ec1a9f..39984c4c85e92 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index 5704f9f7b9f81..3fdc2e696e3f0 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 5fcf3933483c9..f79d12ad047ef 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index d3a45d4595553..fc6dbe517b369 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index edb1a65ab929d..bbeae3846322f 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 48d5f0ea25d03..15ec086b8eaf2 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index e4c1c729146d8..8a55c2821385f 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 0ef73b05d8bb0..c09cbda53a15c 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_chat.mdx b/api_docs/cloud_chat.mdx index 28bf235d4e803..53a1a634088eb 100644 --- a/api_docs/cloud_chat.mdx +++ b/api_docs/cloud_chat.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudChat title: "cloudChat" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudChat plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudChat'] --- import cloudChatObj from './cloud_chat.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index ea00c024dabf2..cf1dca265856b 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index b6680d6d7dbd1..815fae9c2fb42 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 08bce01d73062..c56ad03988470 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index a88a3ac25809b..7b95ed904f226 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index aaa30afdee1ef..2f84a61fdae2e 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.devdocs.json b/api_docs/content_management.devdocs.json index b1fc3a2404541..7a800426b50ad 100644 --- a/api_docs/content_management.devdocs.json +++ b/api_docs/content_management.devdocs.json @@ -1259,7 +1259,7 @@ "section": "def-server.ContentStorage", "text": "ContentStorage" }, - "" + "" ], "path": "src/plugins/content_management/server/core/types.ts", "deprecated": false, @@ -1773,15 +1773,147 @@ { "parentPluginId": "contentManagement", "id": "def-server.ContentStorage.mSearch", - "type": "Object", + "type": "Uncategorized", "tags": [], "label": "mSearch", "description": [ "\nOpt-in to multi-type search.\nCan only be supported if the content type is backed by a saved object since `mSearch` is using the `savedObjects.find` API." ], "signature": [ - "MSearchConfig", - " | undefined" + "TMSearchConfig | undefined" + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig", + "type": "Interface", + "tags": [], + "label": "MSearchConfig", + "description": [ + "\nA configuration for multi-type search.\nBy configuring a content type with a `MSearchConfig`, it can be searched in the multi-type search.\nUnderneath content management is using the `savedObjects.find` API to search the saved objects." + ], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "server", + "docId": "kibContentManagementPluginApi", + "section": "def-server.MSearchConfig", + "text": "MSearchConfig" + }, + "" + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.savedObjectType", + "type": "string", + "tags": [], + "label": "savedObjectType", + "description": [ + "\nThe saved object type that corresponds to this content type." + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.toItemResult", + "type": "Function", + "tags": [], + "label": "toItemResult", + "description": [ + "\nMapper function that transforms the saved object into the content item result." + ], + "signature": [ + "(ctx: ", + { + "pluginId": "contentManagement", + "scope": "server", + "docId": "kibContentManagementPluginApi", + "section": "def-server.StorageContext", + "text": "StorageContext" + }, + ", savedObject: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsFindResult", + "text": "SavedObjectsFindResult" + }, + ") => T" + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.toItemResult.$1", + "type": "Object", + "tags": [], + "label": "ctx", + "description": [], + "signature": [ + { + "pluginId": "contentManagement", + "scope": "server", + "docId": "kibContentManagementPluginApi", + "section": "def-server.StorageContext", + "text": "StorageContext" + } + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.toItemResult.$2", + "type": "Object", + "tags": [], + "label": "savedObject", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsFindResult", + "text": "SavedObjectsFindResult" + }, + "" + ], + "path": "src/plugins/content_management/server/core/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "contentManagement", + "id": "def-server.MSearchConfig.additionalSearchFields", + "type": "Array", + "tags": [], + "label": "additionalSearchFields", + "description": [ + "\nAdditional fields to search on. These fields will be added to the search query.\nBy default, only `title` and `description` are searched." + ], + "signature": [ + "string[] | undefined" ], "path": "src/plugins/content_management/server/core/types.ts", "deprecated": false, diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index 1f578689d0a00..21f71777a11e1 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 143 | 0 | 124 | 7 | +| 149 | 0 | 126 | 6 | ## Client diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 9e673b08f099a..c280197a6417a 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 5f75f99f25e24..53316962496a5 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index eee68e6d1bd7b..ee4d46dc9145f 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 707e03a38207b..e79d7c3abd944 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index f22998f41aaf7..f58d4af3b6e61 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index d08cdb5429fa8..8fd1bc5eca79f 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 810eacf23336f..f56c297a354ba 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 9c8b060ef7919..776cf231e1e62 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index c5097f4dc01c1..4ac05486048f3 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index ddb9680520f03..1e27fc11d34a9 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 9301c42220b3a..57c1558717fa7 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index aca7cffe95904..1a34b399f959c 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index f730084172123..b98c2fea8fc27 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index cacf63a726fb4..b49d0197cbea0 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index c210b2af4a6b3..1a29e749e4dc7 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 880a19dbd0e91..73de9827ebc22 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index f6cb112f22cc5..cde28fb9b20e0 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index b3793bce178b0..79aea49ce0239 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index fb9dfb81dacae..ec0192478b19a 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 1dca0ef9fb7d2..1de1e6f809d6b 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 5826149eca609..24c39169fe91b 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index bc44d093b0ff4..6004d2b66f179 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 9ad8338b79886..68f4430bbeaae 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 7b08e077f39e1..9e32f4ae6e75b 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 7d9413f9555b1..77ff102f16bcb 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_log.devdocs.json b/api_docs/event_log.devdocs.json index c4bf518f79333..b3075cced41d8 100644 --- a/api_docs/event_log.devdocs.json +++ b/api_docs/event_log.devdocs.json @@ -1514,7 +1514,7 @@ "label": "data", "description": [], "signature": [ - "(Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; uuid?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" + "(Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; uuid?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; revision?: string | number | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" ], "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", "deprecated": false, @@ -1534,7 +1534,7 @@ "label": "IEvent", "description": [], "signature": [ - "DeepPartial | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; uuid?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" + "DeepPartial | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; uuid?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; revision?: string | number | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}>>> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, @@ -1549,7 +1549,7 @@ "label": "IValidatedEvent", "description": [], "signature": [ - "Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; uuid?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined" + "Readonly<{ log?: Readonly<{ logger?: string | undefined; level?: string | undefined; } & {}> | undefined; error?: Readonly<{ type?: string | undefined; id?: string | undefined; message?: string | undefined; code?: string | undefined; stack_trace?: string | undefined; } & {}> | undefined; '@timestamp'?: string | undefined; message?: string | undefined; tags?: string[] | undefined; rule?: Readonly<{ id?: string | undefined; name?: string | undefined; description?: string | undefined; category?: string | undefined; uuid?: string | undefined; version?: string | undefined; license?: string | undefined; reference?: string | undefined; author?: string[] | undefined; ruleset?: string | undefined; } & {}> | undefined; kibana?: Readonly<{ action?: Readonly<{ id?: string | undefined; name?: string | undefined; execution?: Readonly<{ source?: string | undefined; uuid?: string | undefined; } & {}> | undefined; } & {}> | undefined; alerting?: Readonly<{ outcome?: string | undefined; summary?: Readonly<{ recovered?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; new?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; ongoing?: Readonly<{ count?: string | number | undefined; } & {}> | undefined; } & {}> | undefined; status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; alert?: Readonly<{ rule?: Readonly<{ consumer?: string | undefined; revision?: string | number | undefined; execution?: Readonly<{ uuid?: string | undefined; metrics?: Readonly<{ number_of_triggered_actions?: string | number | undefined; number_of_generated_actions?: string | number | undefined; alert_counts?: Readonly<{ recovered?: string | number | undefined; active?: string | number | undefined; new?: string | number | undefined; } & {}> | undefined; number_of_searches?: string | number | undefined; total_indexing_duration_ms?: string | number | undefined; es_search_duration_ms?: string | number | undefined; total_search_duration_ms?: string | number | undefined; execution_gap_duration_s?: string | number | undefined; rule_type_run_duration_ms?: string | number | undefined; process_alerts_duration_ms?: string | number | undefined; trigger_actions_duration_ms?: string | number | undefined; process_rule_duration_ms?: string | number | undefined; claim_to_start_duration_ms?: string | number | undefined; prepare_rule_duration_ms?: string | number | undefined; total_run_duration_ms?: string | number | undefined; total_enrichment_duration_ms?: string | number | undefined; } & {}> | undefined; status?: string | undefined; status_order?: string | number | undefined; } & {}> | undefined; rule_type_id?: string | undefined; } & {}> | undefined; uuid?: string | undefined; flapping?: boolean | undefined; maintenance_window_ids?: string[] | undefined; } & {}> | undefined; version?: string | undefined; server_uuid?: string | undefined; task?: Readonly<{ id?: string | undefined; schedule_delay?: string | number | undefined; scheduled?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; namespace?: string | undefined; rel?: string | undefined; type_id?: string | undefined; space_agnostic?: boolean | undefined; } & {}>[] | undefined; space_ids?: string[] | undefined; } & {}> | undefined; event?: Readonly<{ type?: string[] | undefined; reason?: string | undefined; action?: string | undefined; id?: string | undefined; start?: string | undefined; end?: string | undefined; category?: string[] | undefined; outcome?: string | undefined; code?: string | undefined; url?: string | undefined; severity?: string | number | undefined; duration?: string | number | undefined; created?: string | undefined; dataset?: string | undefined; hash?: string | undefined; ingested?: string | undefined; kind?: string | undefined; module?: string | undefined; original?: string | undefined; provider?: string | undefined; reference?: string | undefined; risk_score?: number | undefined; risk_score_norm?: number | undefined; sequence?: string | number | undefined; timezone?: string | undefined; } & {}> | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; } & {}> | undefined" ], "path": "x-pack/plugins/event_log/generated/schemas.ts", "deprecated": false, diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index bfe5735a5b5aa..5e2e0f6c10292 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 2ebfc71ebc060..7315c69e13377 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index 5f952a34778d8..4f23479cfbfc6 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 17b1e1a7dd0c3..3c92eace2b8e3 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 8e23328ac3938..0a35f4158d179 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 2a5739fbf1ce6..92cf80d1ab61a 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index e5641b9c5fd0d..62c32bae5f5f1 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index b8a570a50ce47..8ccde120fd543 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 7ea3ec6b861eb..6432d4714f1e3 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 92e6b7076b109..d8a88c5d95960 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 5641f62c19926..a238dd8036fd1 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 10142b9bf030a..333d1ba0e6264 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 26f353bb29f5f..079a9d1bbced9 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 4961a72b260d7..8af091771bd3e 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index a70dc43040e76..1f6d6a9ba1c68 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 823159e766547..631a20f0bcdf6 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 711def3633bf5..6dedd249971e1 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 0996e6259bf11..fc537a030e52b 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 307f54008c991..624bbf01a981d 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index b3f4dba8b7273..2e7af1d73120f 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index bbf97bb4e042f..49c9a40ff8946 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index f1a34342d9ed3..fe95c91406e4c 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index e5180ea54c5f9..37e44d7a88a33 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.devdocs.json b/api_docs/guided_onboarding.devdocs.json index cd8f47f8cf277..a73976c10b794 100644 --- a/api_docs/guided_onboarding.devdocs.json +++ b/api_docs/guided_onboarding.devdocs.json @@ -690,7 +690,15 @@ "section": "def-common.GuideStepIds", "text": "GuideStepIds" }, - ") => Promise<{ pluginState: ", + ", params?: ", + { + "pluginId": "@kbn/guided-onboarding", + "scope": "common", + "docId": "kibKbnGuidedOnboardingPluginApi", + "section": "def-common.GuideParams", + "text": "GuideParams" + }, + " | undefined) => Promise<{ pluginState: ", { "pluginId": "guidedOnboarding", "scope": "common", @@ -745,6 +753,28 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "guidedOnboarding", + "id": "def-public.GuidedOnboardingApi.completeGuideStep.$3", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + { + "pluginId": "@kbn/guided-onboarding", + "scope": "common", + "docId": "kibKbnGuidedOnboardingPluginApi", + "section": "def-common.GuideParams", + "text": "GuideParams" + }, + " | undefined" + ], + "path": "src/plugins/guided_onboarding/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [] diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index d01239408c028..475a8a2c05da3 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/pla | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 56 | 0 | 55 | 0 | +| 57 | 0 | 56 | 0 | ## Client diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 9a3c60b620a1a..f62e10db50461 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index f46b769442430..3477ece3819e0 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 3d5fedb9c28b1..51b3707ff4cda 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index c56e4e1ac00e1..7e62154cabf29 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 2ef0550b3c7ec..f95130c7fda77 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index be420e800114c..627ca307b241f 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 77f9f7caf3d13..5794f30eaf867 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 9d79fbd636e02..ec79dddfe1fdb 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index d38801a007d3a..027622873bb5d 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index d5b19befd4e62..3ba110b4d641e 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 9d8f4dce4e4e6..9209559d4c511 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 496c97c35fa87..fd625f04df24b 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] --- import kbnAlertsObj from './kbn_alerts.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index af3a71ef4577b..3529d77abdd16 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 73cb3b3c1b270..c39f9c81be753 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index c41d203f9bad1..90704b67958d8 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json index 82163bbf3b11d..0671e818e1d3f 100644 --- a/api_docs/kbn_analytics_client.devdocs.json +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -762,6 +762,14 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/public/common/lib/telemetry/telemetry_client.ts" + }, { "plugin": "@kbn/core-analytics-browser-mocks", "path": "packages/core/analytics/core-analytics-browser-mocks/src/analytics_service.mock.ts" diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index cdc8ea6d81beb..5bdc58d85dc6f 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index eaf3487e3149d..98e6154aa8a60 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 9110cc32f4c31..5cedf9017c0c3 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index df8a64c48bd63..1900aafa94eb7 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 05865a67ce0b7..506c2ef17b8a8 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index f960174f73859..51e1daf9c0a4c 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 8900077fd2ace..28663f15a680c 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index c8432ebc4205c..1b0583f4267b7 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.devdocs.json b/api_docs/kbn_apm_synthtrace_client.devdocs.json index 10dcb002bbdd0..fe2bbd03ef6ab 100644 --- a/api_docs/kbn_apm_synthtrace_client.devdocs.json +++ b/api_docs/kbn_apm_synthtrace_client.devdocs.json @@ -731,7 +731,7 @@ "label": "error", "description": [], "signature": [ - "({ message, type, groupingName, }: { message: string; type?: string | undefined; groupingName?: string | undefined; }) => ", + "({ message, type }: { message: string; type?: string | undefined; }) => ", "ApmError" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts", @@ -743,7 +743,7 @@ "id": "def-common.Instance.error.$1", "type": "Object", "tags": [], - "label": "{\n message,\n type,\n groupingName,\n }", + "label": "{ message, type }", "description": [], "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts", "deprecated": false, @@ -773,20 +773,6 @@ "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.Instance.error.$1.groupingName", - "type": "string", - "tags": [], - "label": "groupingName", - "description": [], - "signature": [ - "string | undefined" - ], - "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/instance.ts", - "deprecated": false, - "trackAdoption": false } ] } diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index b76de3fe835c9..080fc9faa8d69 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) for ques | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 154 | 0 | 154 | 17 | +| 153 | 0 | 153 | 17 | ## Common diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 61e2869b4e841..9509d2d37a5f2 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index ac22df442633d..c9bac589f98e1 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 738eaa012d294..9609f525002a3 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 891ae75c684ea..936150c592f65 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index ea357626d903d..55c5bc95f3389 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index d47f7c20a984c..81991f4c3596a 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index b003b37a45c17..3eeac77d30d96 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 6d8ba1ec6ec6d..4210e48cd996c 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index c9b004312e1fc..dd1eeae4185e8 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 23afc0f33949d..674d7849ea278 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 2128bc3924343..2352502cb6168 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index 2c4aa38ed970a..299b419bc62ee 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 1f2f008178f13..d0b05022cb683 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 0ce1b008677ff..25ba7ec1f2e1f 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index b7e89d00b3240..c9cb8f54bf0b4 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 81635d8b9bd51..11c17046984bd 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 1288f3330685d..54ab8505a8c85 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list.devdocs.json b/api_docs/kbn_content_management_table_list.devdocs.json index 937dddb17c4de..8f1e0a68c22ae 100644 --- a/api_docs/kbn_content_management_table_list.devdocs.json +++ b/api_docs/kbn_content_management_table_list.devdocs.json @@ -177,7 +177,7 @@ "CoreStart contract" ], "signature": [ - "{ application: { capabilities: { advancedSettings?: { save: boolean; } | undefined; }; getUrlForApp: (app: string, options: { path: string; }) => string; currentAppId$: ", + "{ application: { capabilities: { [key: string]: Readonly>>; }; getUrlForApp: (app: string, options: { path: string; }) => string; currentAppId$: ", "Observable", "; navigateToUrl: (url: string) => void | Promise; }; notifications: { toasts: { addDanger: (notifyArgs: { title: ", { diff --git a/api_docs/kbn_content_management_table_list.mdx b/api_docs/kbn_content_management_table_list.mdx index 3dfb0e5bff18e..4c8960148995d 100644 --- a/api_docs/kbn_content_management_table_list.mdx +++ b/api_docs/kbn_content_management_table_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list title: "@kbn/content-management-table-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list'] --- import kbnContentManagementTableListObj from './kbn_content_management_table_list.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 5562e1bf3070f..61fd9c2863537 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index d81b99987512d..8a3895b535171 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index fb39ec52f5459..685a28bea6242 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 5a1af3d3f7d7d..5db1b0e6e16a6 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index 111fe6d8e5318..4272953c4d763 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index d7be4f034346a..e0082181ed949 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index b57a29bb0ad0a..d3f1f8cfa0925 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index 00a6fa0cb815d..6ff37b3089b42 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index e1a48c61d3b9b..02a3fd158ec19 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 4bad226894e0c..d6a70b2b0157f 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 5c042475509cb..d7c038b7a711f 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 69f9212722cc9..eb6b4e1963417 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 65e3d10226f52..1fcfd9f49e0fe 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 86f50bf667604..7d8bb7252a866 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index cf75d243d2b43..69cc08dec9190 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 16fa31f01e292..eef24003ab660 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 4ca1597fcfadc..58aff21c3f633 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index a86cc6571c831..14f3d8712b6f3 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index 027e44a2e349d..d0b5db8e3f5b1 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 492519d0fef3a..ced47f174e1bf 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index a8ef5c7e24843..236d416f8d3f5 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index cf04b0d4146b0..c286a69723583 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index c3ba2d14d0f84..c4201d23a52f5 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 69c93fed8bdf6..5524f5d6c7f3a 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 3447e44fdeb14..c06544f094756 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index 512e0a4658e11..47db10d316e4b 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 57674f1a86f54..74eb25b3e0989 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 3271ecbc40835..2852e74f1d272 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 947830e94c31a..6b39d83563325 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index fed153ea6f806..efcb0ef0d682e 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 14c84336e55d8..1b84c3249cedb 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index f5dac83c7c859..d9183c76749eb 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index bd675bc3f02fb..2dabde1158feb 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index 41ca6aa3b4e82..1f150624000c0 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 5d235b665bccf..f6098418f65b8 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index e11a7361be6fe..18f41498f2022 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index 86023b4693bda..4da7cae29afc1 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index 1b1996813ec5e..7efaa1d260ede 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 014a6cff4fb8f..3a8b4f7733b89 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 8a827ee08a3cb..6d30b7f184a66 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index cf25c21fc3e7b..7e04c291cf2e5 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index d81566d93b87b..bd6d7e5360355 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 52c845ba3e159..108a226e77328 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index 25bdcf097fc8f..36da5cccdf92f 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index d3e52e4498e97..a54b680f5d90d 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index d7116ffefd9e3..22c91cf01d375 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 8763333e3c1ca..50e70544b210b 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index c72123128deb8..f538a5f8e0b4e 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 2bca835abcc60..8f1461db2e66f 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index e0133ab2eceac..77a377a7db9c4 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index f4285539090c7..d7dcc38efe471 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index e88005161eee4..d1ac9f6597bd6 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 9bb759d225d18..4d4c0cdc35fd7 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 391aab7e7c207..d81bd38aa1916 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index b0a32a23bc8fd..05d5f2ae35765 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index d044bcd313597..68eb012274a21 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 3185c3568f7cd..4bdeef98bbfde 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index 6d4e823e86bc7..d94568e3ed0e6 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 2379a6b30140a..306f52219527d 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 3a8785ce36e04..ab7a2167ee8d3 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index cd9f5e1f32fae..3a484f5c5bb94 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 030701d5c5535..4c6f719e62bb6 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 3041948b6c08e..867ab6116747a 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index 6c55bb1acc35f..ad72f4f7242bc 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 1fc1939592f1d..8bca969b22064 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 1ce4c26ce2a2e..408b53256f7db 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index c4dd257493cdb..c87dfa9f9fccf 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 7baa129a75c92..f165267f56e33 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 9115db4edf6cc..e9e7dc64f0942 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 152820cbb488b..f0cdaa85851dc 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 1dc544d47e781..f38f74d167a98 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 12f4523bca3e3..c59347545a328 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index 290dc43e0f6a4..c38585619f6c0 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 1892f31f90652..0fe31a6d87fa1 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 8cd6df89cee80..a9a56e81b982d 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index 71388b8df33ef..ec02bcea6b7f7 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index b46f5cadc7bf9..1a2c897736245 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 188df6cc9052d..3c162c9189752 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 75b8dc9819d5a..494208c4c5914 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index a9e58cbc2c4fb..a037d65732f72 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index d29ee50a427bf..99067d79f0a9f 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index a3f5c62d35e87..534bb78d4eaba 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index 336ba1167e471..81ac149482e3c 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index ead03154da97c..26ba420c08d5c 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 70dcc85174431..9ba0f825772fa 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index eaaab07d66216..a960a65461e1d 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 764140b0a5e6c..3fd09f6263c27 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 96a65db189368..f3acbc7fc8045 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 7edad6a24183a..c3ea743d26d84 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 1bed7b5faea0a..71e8faae9eed4 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index f8d4fffbfe021..2eabbc2bacc62 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index daf89498a1cfc..f37d52033dd7a 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 2faf2f55b3dae..a7217eaf16db7 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 9a208c0343c78..59ea42f319e9d 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 22a3bada69676..466745e2ce499 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index a85a0e176e836..98986e42b5bb0 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 0418f4ca39d60..d8b7087087930 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index fef44b08e7f61..b45a40d461b52 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 5b4c9e57d0c5e..c8536a941c46f 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 1c53d4f1151a2..7146b20a88240 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index ac5a768548470..2518700bd2a9d 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 6538de0675581..38f32ad1bd2c1 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index 2be250444e13d..d158e2d4ff953 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 36a17d070c6e5..e2c79208c0674 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 7a0bd26bc2490..8d655eb267f5a 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 94f1a573c6e08..77ea3d979a1fa 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 20d7ec12378e1..44b22b20623c4 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 1186e3108b3e3..c032049f2dd4f 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 9e81829a0ef67..08eca2b502206 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 920f32db8a28e..971dc60fbd402 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index ff75e7da8fd40..f7cb4f618bbf9 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 5d8f7df7ac176..91d2f9cd5b3a7 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index fc7ce48733faa..e114fbfb6c870 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 66f6d3135417b..12234cdb3c57c 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 814eebd1bcbfa..2ae4ebd0c693b 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 4c5f84aee2327..55646f1f382b6 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_internal.mdx b/api_docs/kbn_core_saved_objects_api_server_internal.mdx index 3f8241f9ab849..165d452af821e 100644 --- a/api_docs/kbn_core_saved_objects_api_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-internal title: "@kbn/core-saved-objects-api-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-internal'] --- import kbnCoreSavedObjectsApiServerInternalObj from './kbn_core_saved_objects_api_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 72ad46898861d..99c35f7983158 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index 7e7ccf32b70ab..1978b36015f7e 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 76f857265d07b..79c73b9220d0c 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 395382f11c5a6..10eb5868ecf47 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index e30fc42495448..4615be9c11ce1 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index 7797116f838b6..a37c978cfb343 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index c7820ecbca374..706710e2cb824 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 34d40c1a0e34b..e0122dffcacb0 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 95f19589a3e7b..08cc43ae6a54d 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 1237af41a106a..49dcee745ce38 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 2b1be8ab9ccf3..612f215e8d922 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 01110761151d4..391665fd14f8c 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index c1570d44d6a4e..8f7af4ecd077d 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index b4d9023205b5b..0a1e5d1292b0a 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index 68054fd030ba7..b99ce47f08e8a 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 0099c87fe71c1..de9b1e18cae5d 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index 942ceea0dddb0..301494e7b2388 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index b9013fd6ca323..df15953abc315 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index e9c7d05fc7a4f..075a12e1c6f24 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 0c6549c2c40a8..d47e6f46490f1 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 0148b222dc307..a38afc5d4cc28 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index 5c3b407e8d7e1..ef13c55e07c61 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 9f82264d3416a..cf75dcf5478b4 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index 361d7ee4abaef..f11a869c92ee1 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index 729bf46fe8b67..484dd22728fbc 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 59ca3b555e77c..63cd2de86d6e4 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index d8e9d7d490f07..54a121b8df78a 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] --- import kbnCoreThemeBrowserInternalObj from './kbn_core_theme_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index fcbeaaf620f5b..a21b5fa4ab454 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index db1ea4bacbeff..e741c75cc66b0 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index 4bd232bbc6d3d..92c21046ca2bb 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 50362b87de095..9b8babfa1fcf3 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index 955203aeab79b..b95f699d6d050 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 5e5c93b62137f..a88c23c489891 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 586bb305927ea..06432816c1788 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 8bde395eb141e..e1477ab3eeb87 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index ac875acccae8d..914a5468667b5 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index e9e8099f4692e..b5596d8ce5bc7 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index d98b4e73ab7f0..c5fc08f39d31e 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index a367c613df999..ff42ee5669ec3 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 0b4c15845ebc7..6af342c88de2c 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index e951de4adfb35..845a8d13f6268 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 3d864f429b0c3..3b0b33c77b227 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index af952748b1e5a..afafe20cb2e1e 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index b66e4a59edd0e..4de5774e12693 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 74b88642009bd..683f4f5f7821b 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index df18c4a7ae5e6..36602e1f31d55 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 237b608f7aa52..3aaf17560db47 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index d12f4055fc03a..b1465811e2295 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index a9c6b2c6c0a28..ead5751fa35c3 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index f7d904bd7bce7..17263518fb4d1 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index a070106c4a4f4..6b880006797c0 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index d6933f82a8a56..0b8d0d939e4ae 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index b90987c9e70ef..2969a379e7d3c 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 9664b87b6910b..eaf29c3077a8b 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 8a8c25e5981a8..ab41505be21e9 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.devdocs.json b/api_docs/kbn_es_query.devdocs.json index 4aac051beec95..02a3068d88627 100644 --- a/api_docs/kbn_es_query.devdocs.json +++ b/api_docs/kbn_es_query.devdocs.json @@ -4243,8 +4243,6 @@ "FilterMetaParams", " | undefined; from?: string | number | undefined; to?: string | number | undefined; gt?: string | number | undefined; lt?: string | number | undefined; gte?: string | number | undefined; lte?: string | number | undefined; format?: string | undefined; } | { query: ", "FilterMetaParams", - " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): number | undefined; push(...items: number[]): number; concat(...items: ConcatArray[]): number[]; concat(...items: (number | ConcatArray)[]): number[]; join(separator?: string | undefined): string; reverse(): number[]; shift(): number | undefined; slice(start?: number | undefined, end?: number | undefined): number[]; sort(compareFn?: ((a: number, b: number) => number) | undefined): number[]; splice(start: number, deleteCount?: number | undefined): number[]; splice(start: number, deleteCount: number, ...items: number[]): number[]; unshift(...items: number[]): number; indexOf(searchElement: number, fromIndex?: number | undefined): number; lastIndexOf(searchElement: number, fromIndex?: number | undefined): number; every(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; some(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void; map(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): U[]; filter(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; find(predicate: (this: void, value: number, index: number, obj: number[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number | undefined; findIndex(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; fill(value: number, start?: number | undefined, end?: number | undefined): number[]; copyWithin(target: number, start: number, end?: number | undefined): number[]; entries(): IterableIterator<[number, number]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: number, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): number | undefined; } | { query: ", - "FilterMetaParams", " | undefined; $state?: { store: ", { "pluginId": "@kbn/es-query", @@ -4263,6 +4261,8 @@ }, "; } | { query: ", "FilterMetaParams", + " | undefined; length: number; toString(): string; toLocaleString(): string; pop(): number | undefined; push(...items: number[]): number; concat(...items: ConcatArray[]): number[]; concat(...items: (number | ConcatArray)[]): number[]; join(separator?: string | undefined): string; reverse(): number[]; shift(): number | undefined; slice(start?: number | undefined, end?: number | undefined): number[]; sort(compareFn?: ((a: number, b: number) => number) | undefined): number[]; splice(start: number, deleteCount?: number | undefined): number[]; splice(start: number, deleteCount: number, ...items: number[]): number[]; unshift(...items: number[]): number; indexOf(searchElement: number, fromIndex?: number | undefined): number; lastIndexOf(searchElement: number, fromIndex?: number | undefined): number; every(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; every(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; some(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void; map(callbackfn: (value: number, index: number, array: number[]) => U, thisArg?: any): U[]; filter(predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): S[]; filter(predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): number[]; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number): number; reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: number[]) => number, initialValue: number): number; reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: number[]) => U, initialValue: U): U; find(predicate: (this: void, value: number, index: number, obj: number[]) => value is S, thisArg?: any): S | undefined; find(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number | undefined; findIndex(predicate: (value: number, index: number, obj: number[]) => unknown, thisArg?: any): number; fill(value: number, start?: number | undefined, end?: number | undefined): number[]; copyWithin(target: number, start: number, end?: number | undefined): number[]; entries(): IterableIterator<[number, number]>; keys(): IterableIterator; values(): IterableIterator; includes(searchElement: number, fromIndex?: number | undefined): boolean; flatMap(callback: (this: This, value: number, index: number, array: number[]) => U | readonly U[], thisArg?: This | undefined): U[]; flat(this: A, depth?: D | undefined): FlatArray[]; [Symbol.iterator](): IterableIterator; [Symbol.unscopables](): { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }; at(index: number): number | undefined; } | { query: ", + "FilterMetaParams", " | undefined; alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type: \"range\"; key?: string | undefined; params?: (", "FilterMetaParams", " & ", diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 56f69b7a09446..9dac7b564cfc4 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 4de4378862f0f..9d1a75a856f82 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 99046ee7a9a8d..cd2e3feaf7128 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.devdocs.json b/api_docs/kbn_expandable_flyout.devdocs.json index 1590d2e5ac52d..7fcd69c291a2e 100644 --- a/api_docs/kbn_expandable_flyout.devdocs.json +++ b/api_docs/kbn_expandable_flyout.devdocs.json @@ -80,31 +80,38 @@ "\nWrap your plugin with this context for the ExpandableFlyout React component." ], "signature": [ - "({ children }: ", + "React.ForwardRefExoticComponent<", "ExpandableFlyoutProviderProps", - ") => JSX.Element" + " & React.RefAttributes<", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutApi", + "text": "ExpandableFlyoutApi" + }, + ">>" ], "path": "packages/kbn-expandable-flyout/src/context.tsx", "deprecated": false, "trackAdoption": false, + "returnComment": [], "children": [ { "parentPluginId": "@kbn/expandable-flyout", "id": "def-common.ExpandableFlyoutProvider.$1", - "type": "Object", + "type": "Uncategorized", "tags": [], - "label": "{ children }", + "label": "props", "description": [], "signature": [ - "ExpandableFlyoutProviderProps" + "P" ], - "path": "packages/kbn-expandable-flyout/src/context.tsx", + "path": "node_modules/@types/react/index.d.ts", "deprecated": false, - "trackAdoption": false, - "isRequired": true + "trackAdoption": false } ], - "returnComment": [], "initialIsOpen": false }, { @@ -118,7 +125,13 @@ ], "signature": [ "() => ", - "ExpandableFlyoutContext" + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutContext", + "text": "ExpandableFlyoutContext" + } ], "path": "packages/kbn-expandable-flyout/src/context.tsx", "deprecated": false, @@ -129,6 +142,389 @@ } ], "interfaces": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext", + "type": "Interface", + "tags": [], + "label": "ExpandableFlyoutContext", + "description": [], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.panels", + "type": "Object", + "tags": [], + "label": "panels", + "description": [ + "\nRight, left and preview panels" + ], + "signature": [ + "State" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout", + "type": "Function", + "tags": [], + "label": "openFlyout", + "description": [ + "\nOpen the flyout with left, right and/or preview panels" + ], + "signature": [ + "(panels: { left?: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined; right?: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined; preview?: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined; }) => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout.$1", + "type": "Object", + "tags": [], + "label": "panels", + "description": [], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout.$1.left", + "type": "Object", + "tags": [], + "label": "left", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout.$1.right", + "type": "Object", + "tags": [], + "label": "right", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openFlyout.$1.preview", + "type": "Object", + "tags": [], + "label": "preview", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + " | undefined" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openRightPanel", + "type": "Function", + "tags": [], + "label": "openRightPanel", + "description": [ + "\nReplaces the current right panel with a new one" + ], + "signature": [ + "(panel: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + ") => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openRightPanel.$1", + "type": "Object", + "tags": [], + "label": "panel", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + } + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openLeftPanel", + "type": "Function", + "tags": [], + "label": "openLeftPanel", + "description": [ + "\nReplaces the current left panel with a new one" + ], + "signature": [ + "(panel: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + ") => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openLeftPanel.$1", + "type": "Object", + "tags": [], + "label": "panel", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + } + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openPreviewPanel", + "type": "Function", + "tags": [], + "label": "openPreviewPanel", + "description": [ + "\nAdd a new preview panel to the list of current preview panels" + ], + "signature": [ + "(panel: ", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + }, + ") => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.openPreviewPanel.$1", + "type": "Object", + "tags": [], + "label": "panel", + "description": [], + "signature": [ + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.FlyoutPanel", + "text": "FlyoutPanel" + } + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.closeRightPanel", + "type": "Function", + "tags": [], + "label": "closeRightPanel", + "description": [ + "\nCloses right panel" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.closeLeftPanel", + "type": "Function", + "tags": [], + "label": "closeLeftPanel", + "description": [ + "\nCloses left panel" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.closePreviewPanel", + "type": "Function", + "tags": [], + "label": "closePreviewPanel", + "description": [ + "\nCloses all preview panels" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.previousPreviewPanel", + "type": "Function", + "tags": [], + "label": "previousPreviewPanel", + "description": [ + "\nGo back to previous preview panel" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext.closeFlyout", + "type": "Function", + "tags": [], + "label": "closeFlyout", + "description": [ + "\nClose all panels and closes flyout" + ], + "signature": [ + "() => void" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/expandable-flyout", "id": "def-common.ExpandableFlyoutProps", @@ -267,7 +663,57 @@ } ], "enums": [], - "misc": [], - "objects": [] + "misc": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutApi", + "type": "Type", + "tags": [], + "label": "ExpandableFlyoutApi", + "description": [], + "signature": [ + "Pick<", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutContext", + "text": "ExpandableFlyoutContext" + }, + ", \"openFlyout\"> & { getState: () => ", + "State", + "; }" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/expandable-flyout", + "id": "def-common.ExpandableFlyoutContext", + "type": "Object", + "tags": [], + "label": "ExpandableFlyoutContext", + "description": [], + "signature": [ + "React.Context<", + { + "pluginId": "@kbn/expandable-flyout", + "scope": "common", + "docId": "kibKbnExpandableFlyoutPluginApi", + "section": "def-common.ExpandableFlyoutContext", + "text": "ExpandableFlyoutContext" + }, + " | undefined>" + ], + "path": "packages/kbn-expandable-flyout/src/context.tsx", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] } } \ No newline at end of file diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 86a0743c821f5..d6f3aa5be6f6b 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; @@ -21,13 +21,19 @@ Contact [@elastic/security-threat-hunting-investigations](https://github.com/org | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 13 | 0 | 4 | 3 | +| 33 | 0 | 13 | 3 | ## Common +### Objects + + ### Functions ### Interfaces +### Consts, variables and types + + diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index d0b208fb97a8e..4d30f7635f93f 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 46afc78d5608e..602d4b76e4b69 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 7ae2b45126f93..512b1b3bf1c1c 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 7d52e4ad2721e..c0b9ff1ab00b0 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index bf0035738a250..3927471f08514 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_generate_csv_types.mdx b/api_docs/kbn_generate_csv_types.mdx index 62ba0711eb511..0e35a01c9f842 100644 --- a/api_docs/kbn_generate_csv_types.mdx +++ b/api_docs/kbn_generate_csv_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv-types title: "@kbn/generate-csv-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv-types'] --- import kbnGenerateCsvTypesObj from './kbn_generate_csv_types.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.devdocs.json b/api_docs/kbn_guided_onboarding.devdocs.json index ec142f58f5b6c..80777a0b54764 100644 --- a/api_docs/kbn_guided_onboarding.devdocs.json +++ b/api_docs/kbn_guided_onboarding.devdocs.json @@ -271,6 +271,27 @@ "path": "packages/kbn-guided-onboarding/src/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/guided-onboarding", + "id": "def-common.GuideState.params", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + { + "pluginId": "@kbn/guided-onboarding", + "scope": "common", + "docId": "kibKbnGuidedOnboardingPluginApi", + "section": "def-common.GuideParams", + "text": "GuideParams" + }, + " | undefined" + ], + "path": "packages/kbn-guided-onboarding/src/types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -294,7 +315,7 @@ "label": "id", "description": [], "signature": [ - "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\"" + "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\" | \"step4\"" ], "path": "packages/kbn-guided-onboarding/src/types.ts", "deprecated": false, @@ -336,7 +357,7 @@ "label": "id", "description": [], "signature": [ - "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\"" + "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\" | \"step4\"" ], "path": "packages/kbn-guided-onboarding/src/types.ts", "deprecated": false, @@ -558,6 +579,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/guided-onboarding", + "id": "def-common.GuideParams", + "type": "Type", + "tags": [], + "label": "GuideParams", + "description": [], + "signature": [ + "{ [x: string]: string; }" + ], + "path": "packages/kbn-guided-onboarding/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/guided-onboarding", "id": "def-common.GuideStatus", @@ -583,7 +619,7 @@ "label": "GuideStepIds", "description": [], "signature": [ - "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\"" + "\"rules\" | \"add_data\" | \"view_dashboard\" | \"tour_observability\" | \"alertsCases\" | \"search_experience\" | \"step1\" | \"step2\" | \"step3\" | \"step4\"" ], "path": "packages/kbn-guided-onboarding/src/types.ts", "deprecated": false, @@ -757,7 +793,7 @@ "label": "steps", "description": [], "signature": [ - "({ id: \"step1\"; title: string; descriptionList: string[]; location: { appID: string; path: string; }; integration: string; } | { id: \"step2\"; title: string; descriptionList: (string | { descriptionText: string; linkText: string; linkUrl: string; isLinkExternal: true; })[]; location: { appID: string; path: string; }; manualCompletion: { title: string; description: string; readyToCompleteOnNavigation: true; }; } | { id: \"step3\"; title: string; description: string; manualCompletion: { title: string; description: string; }; location: { appID: string; path: string; }; })[]" + "({ id: \"step1\"; title: string; descriptionList: string[]; location: { appID: string; path: string; }; integration: string; } | { id: \"step2\"; title: string; descriptionList: (string | { descriptionText: string; linkText: string; linkUrl: string; isLinkExternal: true; })[]; location: { appID: string; path: string; }; manualCompletion: { title: string; description: string; readyToCompleteOnNavigation: true; }; } | { id: \"step3\"; title: string; description: string; manualCompletion: { title: string; description: string; }; location: { appID: string; path: string; }; } | { id: \"step4\"; title: string; description: string; location: { appID: string; path: string; }; })[]" ], "path": "packages/kbn-guided-onboarding/src/common/test_guide_config.ts", "deprecated": false, diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index dd29963efcb66..fa3960f635cfa 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/pla | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 52 | 0 | 50 | 3 | +| 54 | 0 | 52 | 3 | ## Common diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 7f02b89475b3e..52466ad20426f 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 4955b1030ea07..cd8245459b82a 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 26528fbcb1d05..f6df58ece9340 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index ca6f2a8e2ad02..65d82c8aa5eb7 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index ade2366ab0e33..139ab20896b0d 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 0a6a407c727d5..c5d491f697f42 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 7bf9b5d9dd0c8..50c599f41ff0c 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index fd1da1af10952..d6377597c1189 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 3bfb04e709422..4d82a84859ef6 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 503dbab8e3754..d739403a3224c 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index dea3262170428..479812a7adc8e 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index aa82431ea0e3f..66f07ded66198 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index cb2330485201b..9ba905d696133 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 7954d741bfcb3..9b3835108da87 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index 7ceb5757539a4..34e8d2903cb26 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 81a30f0763d92..6c2e32868b8c7 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 8cd4373bdcf17..c5fa54588432d 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index f660720b2133c..a0492d6bad71a 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 05c475dfc93c2..2cfeb2a59556c 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index d82c025dbab1e..0faf79405aec0 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index f92ec1a980b78..6d638b542f37f 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.devdocs.json b/api_docs/kbn_ml_error_utils.devdocs.json new file mode 100644 index 0000000000000..8872d1df30f9a --- /dev/null +++ b/api_docs/kbn_ml_error_utils.devdocs.json @@ -0,0 +1,800 @@ +{ + "id": "@kbn/ml-error-utils", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLRequestFailure", + "type": "Class", + "tags": [ + "export", + "class", + "typedef", + "extends" + ], + "label": "MLRequestFailure", + "description": [ + "\nML Request Failure\n" + ], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLRequestFailure", + "text": "MLRequestFailure" + }, + " extends Error" + ], + "path": "x-pack/packages/ml/error_utils/src/request_error.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLRequestFailure.Unnamed", + "type": "Function", + "tags": [ + "constructor" + ], + "label": "Constructor", + "description": [ + "\nCreates an instance of MLRequestFailure.\n" + ], + "signature": [ + "any" + ], + "path": "x-pack/packages/ml/error_utils/src/request_error.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLRequestFailure.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLErrorObject", + "text": "MLErrorObject" + } + ], + "path": "x-pack/packages/ml/error_utils/src/request_error.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLRequestFailure.Unnamed.$2", + "type": "CompoundType", + "tags": [], + "label": "resp", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + } + ], + "path": "x-pack/packages/ml/error_utils/src/request_error.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.extractErrorMessage", + "type": "Function", + "tags": [], + "label": "extractErrorMessage", + "description": [ + "\nExtract only the error message within the response error\ncoming from Kibana, Elasticsearch, and our own ML messages.\n" + ], + "signature": [ + "(error: ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + }, + ") => string" + ], + "path": "x-pack/packages/ml/error_utils/src/process_errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.extractErrorMessage.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + } + ], + "path": "x-pack/packages/ml/error_utils/src/process_errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.extractErrorProperties", + "type": "Function", + "tags": [], + "label": "extractErrorProperties", + "description": [ + "\nExtract properties of the error object from within the response error\ncoming from Kibana, Elasticsearch, and our own ML messages.\n" + ], + "signature": [ + "(error: ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + }, + ") => ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLErrorObject", + "text": "MLErrorObject" + } + ], + "path": "x-pack/packages/ml/error_utils/src/process_errors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.extractErrorProperties.$1", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorType", + "text": "ErrorType" + } + ], + "path": "x-pack/packages/ml/error_utils/src/process_errors.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isBoomError", + "type": "Function", + "tags": [ + "export" + ], + "label": "isBoomError", + "description": [ + "\nType guard to check if error is of type Boom." + ], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isBoomError.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isErrorString", + "type": "Function", + "tags": [ + "export" + ], + "label": "isErrorString", + "description": [ + "\nType guard to check if error is a string." + ], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isErrorString.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isEsErrorBody", + "type": "Function", + "tags": [ + "export" + ], + "label": "isEsErrorBody", + "description": [ + "\nType guard to check if error is of type EsErrorBody" + ], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isEsErrorBody.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isMLResponseError", + "type": "Function", + "tags": [ + "export" + ], + "label": "isMLResponseError", + "description": [ + "\nType guard to check if error is of type MLResponseError." + ], + "signature": [ + "(error: unknown) => boolean" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.isMLResponseError.$1", + "type": "Unknown", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "unknown" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.ErrorMessage", + "type": "Interface", + "tags": [ + "export", + "interface", + "typedef" + ], + "label": "ErrorMessage", + "description": [ + "\nInterface holding error message" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.ErrorMessage.message", + "type": "string", + "tags": [ + "type" + ], + "label": "message", + "description": [ + "\nmessage" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject", + "type": "Interface", + "tags": [ + "export", + "interface", + "typedef" + ], + "label": "MLErrorObject", + "description": [ + "\nML Error Object" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject.causedBy", + "type": "string", + "tags": [ + "type" + ], + "label": "causedBy", + "description": [ + "\nOptional causedBy" + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject.message", + "type": "string", + "tags": [ + "type" + ], + "label": "message", + "description": [ + "\nmessage" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject.statusCode", + "type": "number", + "tags": [ + "type" + ], + "label": "statusCode", + "description": [ + "\nOptional statusCode" + ], + "signature": [ + "number | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLErrorObject.fullError", + "type": "Object", + "tags": [ + "type" + ], + "label": "fullError", + "description": [ + "\nOptional fullError" + ], + "signature": [ + "ErrorResponseBase", + " | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLHttpFetchErrorBase", + "type": "Interface", + "tags": [ + "export", + "interface", + "typedef", + "template", + "extends" + ], + "label": "MLHttpFetchErrorBase", + "description": [ + "\nMLHttpFetchErrorBase" + ], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLHttpFetchErrorBase", + "text": "MLHttpFetchErrorBase" + }, + " extends ", + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.IHttpFetchError", + "text": "IHttpFetchError" + }, + "" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLHttpFetchErrorBase.body", + "type": "Uncategorized", + "tags": [ + "type" + ], + "label": "body", + "description": [ + "\nbody" + ], + "signature": [ + "T" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError", + "type": "Interface", + "tags": [ + "export", + "interface", + "typedef" + ], + "label": "MLResponseError", + "description": [ + "\nML Response error" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError.statusCode", + "type": "number", + "tags": [ + "type" + ], + "label": "statusCode", + "description": [ + "\nstatusCode" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError.error", + "type": "string", + "tags": [ + "type" + ], + "label": "error", + "description": [ + "\nerror" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError.message", + "type": "string", + "tags": [ + "type" + ], + "label": "message", + "description": [ + "\nmessage" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLResponseError.attributes", + "type": "Object", + "tags": [ + "type" + ], + "label": "attributes", + "description": [ + "\nOptional attributes" + ], + "signature": [ + "{ body: ", + "ErrorResponseBase", + "; } | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.QueryErrorMessage", + "type": "Interface", + "tags": [], + "label": "QueryErrorMessage", + "description": [ + "\nTo be used for client side errors related to search query bars." + ], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.QueryErrorMessage", + "text": "QueryErrorMessage" + }, + " extends ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.ErrorMessage", + "text": "ErrorMessage" + } + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.QueryErrorMessage.query", + "type": "string", + "tags": [ + "type" + ], + "label": "query", + "description": [ + "\nquery" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.ErrorType", + "type": "Type", + "tags": [ + "export", + "typedef" + ], + "label": "ErrorType", + "description": [ + "\nUnion type of error types" + ], + "signature": [ + "string | ", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLHttpFetchError", + "text": "MLHttpFetchError" + }, + " | ", + "ErrorResponseBase", + " | ", + "Boom", + " | undefined" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.EsErrorBody", + "type": "Type", + "tags": [ + "typedef" + ], + "label": "EsErrorBody", + "description": [ + "\nShort hand type of estypes.ErrorResponseBase." + ], + "signature": [ + "ErrorResponseBase" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.EsErrorRootCause", + "type": "Type", + "tags": [ + "typedef" + ], + "label": "EsErrorRootCause", + "description": [ + "\nShort hand type of estypes.ErrorCause." + ], + "signature": [ + "ErrorCauseKeys", + " & { [property: string]: any; }" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/ml-error-utils", + "id": "def-common.MLHttpFetchError", + "type": "Type", + "tags": [ + "export", + "typedef" + ], + "label": "MLHttpFetchError", + "description": [ + "\nMLHttpFetchError" + ], + "signature": [ + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLHttpFetchErrorBase", + "text": "MLHttpFetchErrorBase" + }, + "<", + { + "pluginId": "@kbn/ml-error-utils", + "scope": "common", + "docId": "kibKbnMlErrorUtilsPluginApi", + "section": "def-common.MLResponseError", + "text": "MLResponseError" + }, + ">" + ], + "path": "x-pack/packages/ml/error_utils/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx new file mode 100644 index 0000000000000..39fbc8c7d9cc2 --- /dev/null +++ b/api_docs/kbn_ml_error_utils.mdx @@ -0,0 +1,39 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnMlErrorUtilsPluginApi +slug: /kibana-dev-docs/api/kbn-ml-error-utils +title: "@kbn/ml-error-utils" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/ml-error-utils plugin +date: 2023-04-24 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] +--- +import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; + + + +Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 36 | 0 | 8 | 0 | + +## Common + +### Functions + + +### Classes + + +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 9d000095378bd..22473afe43b10 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 40ce5731de31e..6418b8eaa65f6 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index f14deff209d77..b6ab8338b1be1 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index ef15635229b53..20e06ed847ebb 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index 05c5432c4f57f..e17712d9bddd9 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 8327a7ed0b4d5..ceef878888760 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index 4998fe175ef71..e065a2d921874 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index 8e1d50e0f9364..619f44aa95904 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index f18f1327cb256..c338805fe37b6 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 43aeec4bf32ca..77df8b9fc96a9 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 390009d4685e5..271d8d798bb8b 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 605d07152c5b0..0d286dc40dd46 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.devdocs.json b/api_docs/kbn_object_versioning.devdocs.json index bb1210067600d..551e1feda1d0b 100644 --- a/api_docs/kbn_object_versioning.devdocs.json +++ b/api_docs/kbn_object_versioning.devdocs.json @@ -768,6 +768,28 @@ "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/object-versioning", + "id": "def-common.ServicesDefinition.mSearch", + "type": "Object", + "tags": [], + "label": "mSearch", + "description": [], + "signature": [ + "{ out?: { result?: ", + { + "pluginId": "@kbn/object-versioning", + "scope": "common", + "docId": "kibKbnObjectVersioningPluginApi", + "section": "def-common.VersionableObject", + "text": "VersionableObject" + }, + " | undefined; } | undefined; } | undefined" + ], + "path": "packages/kbn-object-versioning/lib/content_management_types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -978,6 +1000,28 @@ "path": "packages/kbn-object-versioning/lib/content_management_types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "@kbn/object-versioning", + "id": "def-common.ServiceTransforms.mSearch", + "type": "Object", + "tags": [], + "label": "mSearch", + "description": [], + "signature": [ + "{ out: { result: ", + { + "pluginId": "@kbn/object-versioning", + "scope": "common", + "docId": "kibKbnObjectVersioningPluginApi", + "section": "def-common.ObjectTransforms", + "text": "ObjectTransforms" + }, + "; }; }" + ], + "path": "packages/kbn-object-versioning/lib/content_management_types.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 8d3135bdd7f76..bf8dc9c9b2edb 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 53 | 1 | 48 | 0 | +| 55 | 1 | 50 | 0 | ## Common diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 48886f59e6160..109f0b15690e7 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 58204fde5d980..2be790b6675bf 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index ac5cab95f43ec..c03883615b27e 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index d351b9e974a46..39eafb2553e2a 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 820b381b56e3b..fe2a28edfb759 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 4f63a60e98d21..dfafc2995046a 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index 481b254612404..90bfd0727dc86 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 5d88f2bb7cd87..894f6e1897cb7 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index fb0c69e268327..9baafe7d47198 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index fa756525922ae..0dcb15d07b2b0 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index dc04d8563bc52..44df1cb8c7ba8 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index af0847941070f..3725fe5986da2 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index 52ff2a0ac41b5..660528756fcdf 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 5f654ffa57ef8..ee1fd9df0086e 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 16eb8b268e12a..56b40b98c6111 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index 2d5a7a078d24c..ac3326c810bdf 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 476ecb69cdbcc..dbd27615a2886 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 5bec888bb4e5a..31bc6c9d9cbc9 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 7d0c70dbb7b39..f51ac2903abc5 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 1c5b3548f92c4..cbb7736cb66be 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 085a9a78a69cf..2d549e62753a9 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 1df63d4877934..966df1d43affc 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 35453fb0b1a31..ef163cd54d784 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index 7276dcef35bd3..548914f17feb8 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index a7cb93e0ca41c..012e37a5de10c 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json b/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json index 59129f93d4ed5..9450fd73b6671 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.devdocs.json @@ -303,7 +303,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alerts_filter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; }" + "; } & { uuid?: string | undefined; alerts_filter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -326,7 +326,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alerts_filter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; })[]" + "; } & { uuid?: string | undefined; alerts_filter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; })[]" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -349,7 +349,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alertsFilter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; })[]" + "; } & { uuid?: string | undefined; alertsFilter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; })[]" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -372,13 +372,30 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alertsFilter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; }" + "; } & { uuid?: string | undefined; alertsFilter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; }" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionFrequency", + "type": "Type", + "tags": [], + "label": "RuleActionFrequency", + "description": [ + "\nThe action frequency defines when the action runs (for example, only on rule execution or at specific time intervals)." + ], + "signature": [ + "{ summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; }" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionGroup", @@ -409,6 +426,23 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionNotifyWhen", + "type": "Type", + "tags": [], + "label": "RuleActionNotifyWhen", + "description": [ + "\nThe condition for throttling the notification: `onActionGroupChange`, `onActiveAlert`, or `onThrottleInterval`" + ], + "signature": [ + "\"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionParams", @@ -434,6 +468,23 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionSummary", + "type": "Type", + "tags": [], + "label": "RuleActionSummary", + "description": [ + "\nAction summary indicates whether we will send a summary notification about all the generate alerts or notification per individual alert" + ], + "signature": [ + "boolean" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionThrottle", @@ -1013,7 +1064,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alerts_filter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; })[], ({ group: string; id: string; action_type_id: string; params: ", + "; } & { uuid?: string | undefined; alerts_filter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; })[], ({ group: string; id: string; action_type_id: string; params: ", { "pluginId": "@kbn/securitysolution-io-ts-alerting-types", "scope": "common", @@ -1021,7 +1072,7 @@ "section": "def-common.SavedObjectAttributes", "text": "SavedObjectAttributes" }, - "; } & { uuid?: string | undefined; alerts_filter?: { query: ({ kql: string; } & { dsl?: string | undefined; }) | null; timeframe: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | null; } | undefined; })[] | undefined, unknown>" + "; } & { uuid?: string | undefined; alerts_filter?: { query?: ({ kql: string; filters: ({ meta: { alias?: string | null | undefined; disabled?: boolean | undefined; negate?: boolean | undefined; controlledBy?: string | undefined; group?: string | undefined; index?: string | undefined; isMultiIndex?: boolean | undefined; type?: string | undefined; key?: string | undefined; params?: any; value?: string | undefined; }; } & { $state?: { store: any; } | undefined; query?: { [x: string]: any; } | undefined; })[]; } & { dsl?: string | undefined; }) | undefined; timeframe?: { timezone: string; days: (2 | 7 | 6 | 5 | 4 | 3 | 1)[]; hours: { start: string; end: string; }; } | undefined; } | undefined; frequency?: { summary: boolean; notifyWhen: \"onActionGroupChange\" | \"onActiveAlert\" | \"onThrottleInterval\"; throttle: string | null; } | undefined; })[] | undefined, unknown>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/default_actions_array/index.ts", "deprecated": false, @@ -1570,13 +1621,11 @@ "<{ uuid: ", "Type", "; alerts_filter: ", - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1585,14 +1634,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -1625,7 +1720,31 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>; }>]>>" + "; }>>; }>>]>; }>; frequency: ", + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>; }>]>>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -1640,13 +1759,11 @@ "label": "RuleActionAlertsFilter", "description": [], "signature": [ - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1655,14 +1772,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -1695,7 +1858,7 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>" + "; }>>; }>>]>; }>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -1746,13 +1909,11 @@ "<{ uuid: ", "Type", "; alerts_filter: ", - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1761,14 +1922,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -1801,7 +2008,31 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>; }>]>>>" + "; }>>; }>>]>; }>; frequency: ", + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>; }>]>>>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -1852,13 +2083,11 @@ "<{ uuid: ", "Type", "; alertsFilter: ", - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1867,14 +2096,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -1907,7 +2182,31 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>; }>]>>>" + "; }>>; }>>]>; }>; frequency: ", + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>; }>]>>>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, @@ -1956,13 +2255,11 @@ "<{ uuid: ", "Type", "; alertsFilter: ", - "ExactC", - "<", - "TypeC", + "PartialC", "<{ query: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "IntersectionC", "<[", @@ -1971,14 +2268,60 @@ "TypeC", "<{ kql: ", "StringC", - "; }>>, ", + "; filters: ", + "ArrayC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ meta: ", + "PartialC", + "<{ alias: ", + "UnionC", + "<[", + "StringC", + ", ", + "NullC", + "]>; disabled: ", + "BooleanC", + "; negate: ", + "BooleanC", + "; controlledBy: ", + "StringC", + "; group: ", + "StringC", + "; index: ", + "StringC", + "; isMultiIndex: ", + "BooleanC", + "; type: ", + "StringC", + "; key: ", + "StringC", + "; params: ", + "AnyC", + "; value: ", + "StringC", + "; }>; }>, ", + "PartialC", + "<{ $state: ", + "TypeC", + "<{ store: ", + "AnyC", + "; }>; query: ", + "RecordC", + "<", + "StringC", + ", ", + "AnyC", + ">; }>]>>; }>>, ", "PartialC", "<{ dsl: ", "StringC", "; }>]>]>; timeframe: ", "UnionC", "<[", - "NullC", + "UndefinedC", ", ", "ExactC", "<", @@ -2011,13 +2354,75 @@ "StringC", "; end: ", "StringC", - "; }>>; }>>]>; }>>; }>]>>" + "; }>>; }>>]>; }>; frequency: ", + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>; }>]>>" ], "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/actions/index.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionFrequency", + "type": "Object", + "tags": [], + "label": "RuleActionFrequency", + "description": [], + "signature": [ + "TypeC", + "<{ summary: ", + "BooleanC", + "; notifyWhen: ", + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>; throttle: ", + "UnionC", + "<[", + "UnionC", + "<[", + "LiteralC", + "<\"no_actions\">, ", + "LiteralC", + "<\"rule\">, ", + "Type", + "]>, ", + "NullC", + "]>; }>" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionGroup", @@ -2048,6 +2453,28 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionNotifyWhen", + "type": "Object", + "tags": [], + "label": "RuleActionNotifyWhen", + "description": [], + "signature": [ + "UnionC", + "<[", + "LiteralC", + "<\"onActionGroupChange\">, ", + "LiteralC", + "<\"onActiveAlert\">, ", + "LiteralC", + "<\"onThrottleInterval\">]>" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionParams", @@ -2080,6 +2507,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", + "id": "def-common.RuleActionSummary", + "type": "Object", + "tags": [], + "label": "RuleActionSummary", + "description": [], + "signature": [ + "BooleanC" + ], + "path": "packages/kbn-securitysolution-io-ts-alerting-types/src/frequency/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-alerting-types", "id": "def-common.RuleActionThrottle", diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 1a5fe7a8eff63..c19977b427358 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 141 | 0 | 122 | 0 | +| 147 | 0 | 125 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json b/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json index cbd27335f2f35..44e52b99ed031 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json +++ b/api_docs/kbn_securitysolution_io_ts_list_types.devdocs.json @@ -106,7 +106,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -148,7 +154,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -204,7 +216,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -260,7 +278,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -327,7 +351,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -439,7 +469,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1015,6 +1051,102 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps", + "type": "Interface", + "tags": [], + "label": "ApiListDuplicateProps", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.ApiListDuplicateProps", + "text": "ApiListDuplicateProps" + }, + " extends Omit<", + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.DuplicateExceptionListProps", + "text": "DuplicateExceptionListProps" + }, + ", \"http\" | \"signal\">" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps.onError", + "type": "Function", + "tags": [], + "label": "onError", + "description": [], + "signature": [ + "(err: Error) => void" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps.onError.$1", + "type": "Object", + "tags": [], + "label": "err", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps.onSuccess", + "type": "Function", + "tags": [], + "label": "onSuccess", + "description": [], + "signature": [ + "(newList: { _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; }) => void" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.ApiListDuplicateProps.onSuccess.$1", + "type": "Object", + "tags": [], + "label": "newList", + "description": [], + "signature": [ + "{ _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; }" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.ApiListExportProps", @@ -1140,6 +1272,66 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListProps", + "type": "Interface", + "tags": [], + "label": "DuplicateExceptionListProps", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.DuplicateExceptionListProps", + "text": "DuplicateExceptionListProps" + }, + " extends BaseParams" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListProps.listId", + "type": "string", + "tags": [], + "label": "listId", + "description": [], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListProps.namespaceType", + "type": "CompoundType", + "tags": [], + "label": "namespaceType", + "description": [], + "signature": [ + "\"single\" | \"agnostic\"" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListProps.includeExpiredExceptions", + "type": "boolean", + "tags": [], + "label": "includeExpiredExceptions", + "description": [], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.ExceptionFilterResponse", @@ -1393,7 +1585,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1538,7 +1736,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1597,7 +1801,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1759,7 +1969,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1855,7 +2071,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -1911,7 +2133,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -2015,7 +2243,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -2270,7 +2504,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -2293,12 +2533,18 @@ { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.UseExceptionListsProps.notifications", - "type": "Any", + "type": "Object", "tags": [], "label": "notifications", "description": [], "signature": [ - "any" + { + "pluginId": "@kbn/core-notifications-browser", + "scope": "common", + "docId": "kibKbnCoreNotificationsBrowserPluginApi", + "section": "def-common.NotificationsStart", + "text": "NotificationsStart" + } ], "path": "packages/kbn-securitysolution-io-ts-list-types/src/typescript_types/index.ts", "deprecated": false, @@ -3081,6 +3327,36 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListQuerySchema", + "type": "Type", + "tags": [], + "label": "DuplicateExceptionListQuerySchema", + "description": [], + "signature": [ + "{ list_id: string; namespace_type: \"single\" | \"agnostic\" | undefined; include_expired_exceptions: \"true\" | \"false\" | undefined; }" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.DuplicateExceptionListQuerySchemaDecoded", + "type": "Type", + "tags": [], + "label": "DuplicateExceptionListQuerySchemaDecoded", + "description": [], + "signature": [ + "Omit<{ list_id: string; namespace_type: \"single\" | \"agnostic\"; include_expired_exceptions: \"true\" | \"false\" | undefined; }, \"namespace_type\"> & { namespace_type: \"single\" | \"agnostic\"; }" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.EndpointEntriesArray", @@ -6256,6 +6532,34 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-io-ts-list-types", + "id": "def-common.duplicateExceptionListQuerySchema", + "type": "Object", + "tags": [], + "label": "duplicateExceptionListQuerySchema", + "description": [], + "signature": [ + "ExactC", + "<", + "TypeC", + "<{ list_id: ", + "Type", + "; namespace_type: ", + "Type", + "<\"single\" | \"agnostic\", \"single\" | \"agnostic\" | undefined, unknown>; include_expired_exceptions: ", + "UnionC", + "<[", + "KeyofC", + "<{ true: null; false: null; }>, ", + "UndefinedC", + "]>; }>>" + ], + "path": "packages/kbn-securitysolution-io-ts-list-types/src/request/duplicate_exception_list_query_schema/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-io-ts-list-types", "id": "def-common.endpointEntriesArray", diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index fae1f7cdd2313..61c51d7af0af1 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 516 | 1 | 503 | 0 | +| 528 | 0 | 515 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 360fd19200755..97859ef4b0ff6 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 2b6f02cc7215b..087b27374d68b 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.devdocs.json b/api_docs/kbn_securitysolution_list_api.devdocs.json index b4c5635b5b281..1a07636db6652 100644 --- a/api_docs/kbn_securitysolution_list_api.devdocs.json +++ b/api_docs/kbn_securitysolution_list_api.devdocs.json @@ -348,6 +348,57 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/securitysolution-list-api", + "id": "def-common.duplicateExceptionList", + "type": "Function", + "tags": [ + "throws" + ], + "label": "duplicateExceptionList", + "description": [ + "\nDuplicate an ExceptionList and its items by providing a ExceptionList list_id\n" + ], + "signature": [ + "({ http, includeExpiredExceptions, listId, namespaceType, signal, }: ", + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.DuplicateExceptionListProps", + "text": "DuplicateExceptionListProps" + }, + ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; }>" + ], + "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-list-api", + "id": "def-common.duplicateExceptionList.$1", + "type": "Object", + "tags": [], + "label": "{\n http,\n includeExpiredExceptions,\n listId,\n namespaceType,\n signal,\n}", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.DuplicateExceptionListProps", + "text": "DuplicateExceptionListProps" + } + ], + "path": "packages/kbn-securitysolution-list-api/src/api/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/securitysolution-list-api", "id": "def-common.exportExceptionList", diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 0255ba2088e5e..aadca05334b92 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 67 | 0 | 64 | 0 | +| 69 | 0 | 65 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 02b65ca6fde37..2d19d6111ef53 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.devdocs.json b/api_docs/kbn_securitysolution_list_hooks.devdocs.json index a1741a54232f0..14c2ee5a4b122 100644 --- a/api_docs/kbn_securitysolution_list_hooks.devdocs.json +++ b/api_docs/kbn_securitysolution_list_hooks.devdocs.json @@ -255,7 +255,15 @@ "label": "useApi", "description": [], "signature": [ - "(http: HttpStart) => ", + "(http: ", + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + }, + ") => ", { "pluginId": "@kbn/securitysolution-list-hooks", "scope": "common", @@ -276,7 +284,13 @@ "label": "http", "description": [], "signature": [ - "HttpStart" + { + "pluginId": "@kbn/core-http-browser", + "scope": "common", + "docId": "kibKbnCoreHttpBrowserPluginApi", + "section": "def-common.HttpSetup", + "text": "HttpSetup" + } ], "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", "deprecated": false, @@ -1023,6 +1037,52 @@ ], "returnComment": [] }, + { + "parentPluginId": "@kbn/securitysolution-list-hooks", + "id": "def-common.ExceptionsApi.duplicateExceptionList", + "type": "Function", + "tags": [], + "label": "duplicateExceptionList", + "description": [], + "signature": [ + "(arg: ", + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.ApiListDuplicateProps", + "text": "ApiListDuplicateProps" + }, + ") => Promise" + ], + "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/securitysolution-list-hooks", + "id": "def-common.ExceptionsApi.duplicateExceptionList.$1", + "type": "Object", + "tags": [], + "label": "arg", + "description": [], + "signature": [ + { + "pluginId": "@kbn/securitysolution-io-ts-list-types", + "scope": "common", + "docId": "kibKbnSecuritysolutionIoTsListTypesPluginApi", + "section": "def-common.ApiListDuplicateProps", + "text": "ApiListDuplicateProps" + } + ], + "path": "packages/kbn-securitysolution-list-hooks/src/use_api/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, { "parentPluginId": "@kbn/securitysolution-list-hooks", "id": "def-common.ExceptionsApi.getExceptionItem", diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 786b822ce39de..e5f63ce69a791 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution-platform](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 60 | 0 | 47 | 0 | +| 62 | 0 | 49 | 0 | ## Common diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 7f1e021d2997e..9fbc74f2281fa 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 7b8ea7f8fa153..d99c895cfbb07 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index bbaae18e81926..fc1a7126299da 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index b22479ca8cb62..5b42ba79461cd 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index e959b8f8857f1..c538168844e59 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 4dce3d0bcc354..7c066060f57b3 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index b3241f80861cf..57b4b0757c666 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index 7fdc62d57e0e5..0e0a7de059854 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx index 74b168d876714..cbe1bbc592105 100644 --- a/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx +++ b/api_docs/kbn_shared_ux_avatar_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-user-profile-components title: "@kbn/shared-ux-avatar-user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-user-profile-components plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-user-profile-components'] --- import kbnSharedUxAvatarUserProfileComponentsObj from './kbn_shared_ux_avatar_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index c67019c024447..fd754396fe38d 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index 523f937ac0308..704bf0d55a588 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 7e026fc462e17..d5cbbf1440008 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 6744cc7da9aa8..6e187d6a67d6d 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 604622664685d..f1eafa0073b75 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 2218b967c0672..345346e831748 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 961468e71949a..e242bd651fa0d 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index f82ac77b98773..4e391b9d8ab27 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 1ab3bafb4ee5f..662c634769dd1 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index f48a321a34b54..1e22cbdd34b00 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index cf77561da9e6e..43c10d7423ef7 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 84e8d32366c10..7a684a3ddf9b6 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index d79e2ec066afd..7a12cb245836a 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index 35763b229e400..dc82b967602d2 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 714f376ffb7c5..18b157f050418 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index f4f56efd30485..8d43b2bc2609d 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 5d46068526cc8..6ad8500d4ffa1 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index a703893f75dec..da16a3294719e 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index c6146d86359c0..a79ab025d8f39 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 2fc62d3203dab..675d8ca547baa 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index e4a702e9d4de6..87063d4968ca9 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 0281befb594ed..d83b54deb377f 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index bc4addd3208ad..a915fe7ce8d2f 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 0144374aab4fb..d386725c0f96e 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index da46bad1d78e5..b609f61949e3a 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 4681ccfbf18d8..3b8a6aea768c4 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index ac7fbb18cee1d..b549d3be8ce48 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index c5f2135a7e18e..57b7f77917dbf 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 90f8c67503a8a..cde75adfcbd03 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 0c4bc9ae73323..449ec14d0e89b 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index da5e9ae1eb9a1..54e8a60fb3a71 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index aa816570472a8..64431ebf0a9f6 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 135cdbf742f31..8f903a6a4599b 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 9ca85a30f64b6..aa8aeb5ed628b 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index ce99698fd9f1b..1f594a6b27503 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 0e8f337108e82..35030e8f65b69 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 97a606deb5c99..b86313f3a585f 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index d1a8eebc6a352..8a4c14deb442e 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 0217dc9a965db..8a33d6033f83b 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index b4b67a76d6345..8bb33b6f9fc6c 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index fd6b8221cc8fd..0dcbe8b65314a 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index a2a8c3a870b1a..009ae2d00a260 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 245f8573362f4..077c288dbbd06 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 7c8dcb3038c28..ac6e29488cd2d 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index f4a6b85e91f0c..4b38e19d2c144 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 2da057aa80b43..1e093e1c87a67 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index efa271163a527..ec139fac154dc 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index e36f3796a6a55..c335bc8053866 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 3cd1a6aef2f77..e4f4818385008 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 11107207bbef6..cfc3a2c905dbb 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index a6c35e39c2b54..2306fe2c1523e 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_url_state.devdocs.json b/api_docs/kbn_url_state.devdocs.json new file mode 100644 index 0000000000000..e7659b142aa8e --- /dev/null +++ b/api_docs/kbn_url_state.devdocs.json @@ -0,0 +1,99 @@ +{ + "id": "@kbn/url-state", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/url-state", + "id": "def-common.useSyncToUrl", + "type": "Function", + "tags": [], + "label": "useSyncToUrl", + "description": [ + "\nSync any object with browser query string using @knb/rison" + ], + "signature": [ + "(key: string, restore: (data: TValueToSerialize) => void, cleanupOnHistoryNavigation?: boolean) => (valueToSerialize?: TValueToSerialize | undefined) => void" + ], + "path": "packages/kbn-url-state/use_sync_to_url.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/url-state", + "id": "def-common.useSyncToUrl.$1", + "type": "string", + "tags": [], + "label": "key", + "description": [ + "query string param to use" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-url-state/use_sync_to_url.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/url-state", + "id": "def-common.useSyncToUrl.$2", + "type": "Function", + "tags": [], + "label": "restore", + "description": [ + "use this to handle restored state" + ], + "signature": [ + "(data: TValueToSerialize) => void" + ], + "path": "packages/kbn-url-state/use_sync_to_url.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/url-state", + "id": "def-common.useSyncToUrl.$3", + "type": "boolean", + "tags": [], + "label": "cleanupOnHistoryNavigation", + "description": [ + "use history events to cleanup state on back / forward naviation. true by default" + ], + "signature": [ + "boolean" + ], + "path": "packages/kbn-url-state/use_sync_to_url.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx new file mode 100644 index 0000000000000..1ecdc4cf5f6b8 --- /dev/null +++ b/api_docs/kbn_url_state.mdx @@ -0,0 +1,30 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnUrlStatePluginApi +slug: /kibana-dev-docs/api/kbn-url-state +title: "@kbn/url-state" +image: https://source.unsplash.com/400x175/?github +description: API docs for the @kbn/url-state plugin +date: 2023-04-24 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] +--- +import kbnUrlStateObj from './kbn_url_state.devdocs.json'; + + + +Contact [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 0 | 0 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 99b0de88ef5ef..9678a8f3d941e 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index d7c2e09558b57..0847fe990eba3 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 18348076c4a98..bd7b6dc7e7ca0 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index a743985adbe80..3bb37ce9fb475 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 73c55462f3c81..89bec78fbe1d0 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index e20a39ed1d8b5..35b6ab39c77a0 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index cb370c5b41c00..277ae838dc455 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index e4252f4bb1d23..9710a9b016f88 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index f13e3882eba31..e9b23fb4642c8 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 0a8741d076772..7e35673a3ef08 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index b9b65d3c94bc6..18e54115992df 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index e3bb7df026945..f791025b3e353 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 704afaaf19d4d..348e30ab776d8 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/lists.devdocs.json b/api_docs/lists.devdocs.json index cf0f553770aee..a5d1cf263468f 100644 --- a/api_docs/lists.devdocs.json +++ b/api_docs/lists.devdocs.json @@ -736,7 +736,7 @@ "\nCreate the Trusted Apps Agnostic list if it does not yet exist (`null` is returned if it does exist)" ], "signature": [ - "({ listId, namespaceType, }: ", + "({ list, namespaceType, includeExpiredExceptions, }: ", "DuplicateExceptionListOptions", ") => Promise<{ _version: string | undefined; created_at: string; created_by: string; description: string; id: string; immutable: boolean; list_id: string; meta: object | undefined; name: string; namespace_type: \"single\" | \"agnostic\"; os_types: (\"windows\" | \"linux\" | \"macos\")[]; tags: string[]; tie_breaker_id: string; type: \"endpoint\" | \"detection\" | \"rule_default\" | \"endpoint_trusted_apps\" | \"endpoint_events\" | \"endpoint_host_isolation_exceptions\" | \"endpoint_blocklists\"; updated_at: string; updated_by: string; version: number; } | null>" ], @@ -749,7 +749,7 @@ "id": "def-server.ExceptionListClient.duplicateExceptionListAndItems.$1", "type": "Object", "tags": [], - "label": "{\n listId,\n namespaceType,\n }", + "label": "{\n list,\n namespaceType,\n includeExpiredExceptions,\n }", "description": [], "signature": [ "DuplicateExceptionListOptions" diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 6a3f788c87be3..7a4f5df515fb3 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index f2a3ee2fecf34..e86efc700e7ea 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 3a3a5d8d917d1..d5ef557d05116 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 4205c9b472f0b..97034dddf8ac6 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/ml.devdocs.json b/api_docs/ml.devdocs.json index a235055918f57..a714fc22ec0c9 100644 --- a/api_docs/ml.devdocs.json +++ b/api_docs/ml.devdocs.json @@ -3306,41 +3306,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "ml", - "id": "def-common.extractErrorMessage", - "type": "Function", - "tags": [], - "label": "extractErrorMessage", - "description": [], - "signature": [ - "(error: ", - "ErrorType", - ") => string" - ], - "path": "x-pack/plugins/ml/common/util/errors/process_errors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "ml", - "id": "def-common.extractErrorMessage.$1", - "type": "CompoundType", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "ErrorType" - ], - "path": "x-pack/plugins/ml/common/util/errors/process_errors.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": false - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "ml", "id": "def-common.getDefaultCapabilities", diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index d7fab7ce5ee29..d977d47492384 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 259 | 9 | 83 | 40 | +| 257 | 9 | 81 | 39 | ## Client diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index fbb5d4166d7c0..90c474ac78f1a 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 390050d8aca21..8d7ae64cb77ba 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index c1da45703e1e0..59a2eae887fb5 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index ea9412b022b55..f1ec49c987768 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 45300a85184d2..bf6aade55bc14 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index caed04a1ba9d5..01cac0e1d423a 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index 85db14ed31dc3..321906e5ba6ff 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 57b293ee7b2e3..171effe7c5467 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index dc3d7eab17a06..e0ee93073348b 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -15,13 +15,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Count | Plugins or Packages with a
public API | Number of teams | |--------------|----------|------------------------| -| 595 | 491 | 37 | +| 597 | 493 | 37 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 69056 | 526 | 59639 | 1335 | +| 69151 | 525 | 59683 | 1333 | ## Plugin Directory @@ -30,7 +30,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 259 | 8 | 254 | 26 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 36 | 1 | 32 | 2 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | AIOps plugin maintained by ML team. | 39 | 0 | 24 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 603 | 1 | 582 | 42 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 606 | 1 | 585 | 42 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | The user interface for Elastic APM | 43 | 0 | 43 | 110 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | Asset manager plugin for entity assets (inventory, topology, etc) | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | @@ -48,7 +48,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | cloudLinks | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | Adds the links to the Elastic Cloud console | 0 | 0 | 0 | 0 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | The cloud security posture plugin | 17 | 0 | 2 | 2 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 13 | 0 | 13 | 1 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 143 | 0 | 124 | 7 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Content management app | 149 | 0 | 126 | 6 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Controls Plugin contains embeddable components intended to create a simple query interface for end users, and a powerful editing suite that allows dashboard authors to build controls | 301 | 0 | 294 | 13 | | crossClusterReplication | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | | customBranding | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Enables customization of Kibana | 0 | 0 | 0 | 0 | @@ -99,7 +99,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | globalSearchProviders | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | | graph | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 0 | 0 | 0 | 0 | | grokdebugger | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | -| | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | Guided onboarding framework | 56 | 0 | 55 | 0 | +| | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | Guided onboarding framework | 57 | 0 | 56 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 143 | 0 | 104 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Image embeddable | 3 | 0 | 3 | 1 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 0 | @@ -123,7 +123,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 41 | 0 | 41 | 6 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 269 | 0 | 268 | 29 | | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 67 | 0 | 67 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 259 | 9 | 83 | 40 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 257 | 9 | 81 | 39 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 15 | 3 | 13 | 1 | | | [@elastic/infra-monitoring-ui](https://github.com/orgs/elastic/teams/infra-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 34 | 0 | 34 | 2 | @@ -167,7 +167,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 257 | 1 | 214 | 20 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [@elastic/kibana-localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 540 | 10 | 511 | 49 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 542 | 10 | 513 | 49 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 134 | 2 | 92 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 206 | 0 | 140 | 9 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list which can be integrated into apps | 296 | 0 | 270 | 6 | @@ -214,7 +214,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 18 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 27 | 0 | 27 | 3 | -| | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 154 | 0 | 154 | 17 | +| | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 153 | 0 | 153 | 17 | | | [@elastic/apm-ui](https://github.com/orgs/elastic/teams/apm-ui) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-qa](https://github.com/orgs/elastic/teams/kibana-qa) | - | 12 | 0 | 12 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 19 | 0 | 17 | 0 | @@ -408,14 +408,14 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 255 | 1 | 197 | 15 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | -| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 13 | 0 | 4 | 3 | +| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 33 | 0 | 13 | 3 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 16 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 0 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 29 | 0 | 29 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 10 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 10 | 0 | 10 | 0 | -| | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | - | 52 | 0 | 50 | 3 | +| | [@elastic/platform-onboarding](https://github.com/orgs/elastic/teams/platform-onboarding) | - | 54 | 0 | 52 | 3 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 33 | 3 | 24 | 6 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 3 | 0 | 3 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 1 | 0 | 1 | 0 | @@ -437,6 +437,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | - | 534 | 1 | 1 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 93 | 2 | 61 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 52 | 0 | 4 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 36 | 0 | 8 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 2 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 3 | 0 | 2 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 5 | 0 | 3 | 0 | @@ -449,7 +450,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 8 | 1 | 8 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 31 | 1 | 24 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 71 | 0 | 69 | 3 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 53 | 1 | 48 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 55 | 1 | 50 | 0 | | | [@elastic/actionable-observability](https://github.com/orgs/elastic/teams/actionable-observability) | - | 7 | 0 | 7 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 45 | 0 | 45 | 10 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 51 | 5 | 34 | 0 | @@ -475,13 +476,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 104 | 0 | 93 | 1 | | | [@elastic/security-threat-hunting-explore](https://github.com/orgs/elastic/teams/security-threat-hunting-explore) | - | 20 | 0 | 15 | 4 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 15 | 0 | 7 | 0 | -| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 141 | 0 | 122 | 0 | -| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 516 | 1 | 503 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 147 | 0 | 125 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 528 | 0 | 515 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 65 | 0 | 36 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 28 | 0 | 21 | 0 | -| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 67 | 0 | 64 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 69 | 0 | 65 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 35 | 0 | 23 | 0 | -| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 60 | 0 | 47 | 0 | +| | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 62 | 0 | 49 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 210 | 10 | 163 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 26 | 0 | 23 | 0 | | | [@elastic/security-solution-platform](https://github.com/orgs/elastic/teams/security-solution-platform) | - | 120 | 0 | 116 | 0 | @@ -542,6 +543,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 18 | 0 | 8 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 45 | 0 | 36 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 7 | 0 | 6 | 0 | +| | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 4 | 0 | 0 | 0 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 58 | 0 | 5 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 36 | 0 | 15 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 2 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index bd5fa373355d3..bb5dd22da4a5d 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 94ec36d0e142d..fa7c514839f1d 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 4d93f65d4f588..052f465958e43 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index dc655be6ea3b1..b3e643f23c824 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 22004d4715863..3ad92d06484ef 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 7af25a7132259..2230c7e475fbf 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index bf014f1590fc2..fbb641e9ec07b 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 1ce68779192c7..4775f7bab3e25 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index ba213be4dd0b6..c5cc15bc31edd 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index b3d06632c1757..8ed4d9ce7ea8b 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 28ee6a6fdb1e9..28d4dfa912c94 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index 50a27cc71b42c..c4e422b6d4bf5 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index 2f0a64f88e254..b41919c5f029f 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index b87efc25bb18e..eee83610aebe7 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 179ea1d1861aa..8b105e90157ca 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index b93687377e8a6..6a3ef50e5d725 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index d193ee3ae36cf..49cd868cf6aee 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 863bb87c48c26..89f21abe997f4 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 2bb9768f311c2..41725af018c9e 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 44b56f58ba474..582bfce7f3253 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 563274dd2f750..2467dff1cd464 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index bfd22efb5d897..f2fd4e7fff1d3 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 7426f4bf3c2e2..12ffc30623e60 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 07663807996de..ec588948cbe18 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 507775a14db43..439a2431b4376 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index b0d73ccf96937..a445edd5a138f 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index 3870cdba63b35..29ee2d6178438 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 27c23a0eb88db..fbe7face22441 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index b54750be2d606..5ca7b1d27e7d4 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index acb04c0a79a88..41df96ec361dd 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index a5cb1bf5bb0f1..e0a7c8eca1a70 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.devdocs.json b/api_docs/triggers_actions_ui.devdocs.json index 8b881a6bf3005..ae29d4e3e787a 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -2831,6 +2831,20 @@ "path": "x-pack/plugins/alerting/common/alert_summary.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertStatus.maintenanceWindowIds", + "type": "Array", + "tags": [], + "label": "maintenanceWindowIds", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/alerting/common/alert_summary.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -3045,6 +3059,17 @@ "path": "x-pack/plugins/alerting/common/alert_summary.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.AlertSummary.revision", + "type": "number", + "tags": [], + "label": "revision", + "description": [], + "path": "x-pack/plugins/alerting/common/alert_summary.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index fc92b4f9ff615..0dfa7fa50555b 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 540 | 10 | 511 | 49 | +| 542 | 10 | 513 | 49 | ## Client diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 2fe2650841250..ba54dfe3328c8 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index a46c6ed8f86f6..0111328be34a0 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_field_list.mdx b/api_docs/unified_field_list.mdx index 604ae78db8a3e..97fceac316e4d 100644 --- a/api_docs/unified_field_list.mdx +++ b/api_docs/unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedFieldList title: "unifiedFieldList" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedFieldList plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedFieldList'] --- import unifiedFieldListObj from './unified_field_list.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 33f08e0514e8c..e0acc17411f17 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.devdocs.json b/api_docs/unified_search.devdocs.json index 05f2215a9a913..8f697ba358c0c 100644 --- a/api_docs/unified_search.devdocs.json +++ b/api_docs/unified_search.devdocs.json @@ -487,7 +487,7 @@ "OnSaveTextLanguageQueryProps", ") => void) | undefined; showSubmitButton?: boolean | undefined; submitButtonStyle?: \"full\" | \"auto\" | \"iconOnly\" | undefined; suggestionsSize?: ", "SuggestionsListSize", - " | undefined; isScreenshotMode?: boolean | undefined; onFiltersUpdated?: ((filters: ", + " | undefined; isScreenshotMode?: boolean | undefined; submitOnBlur?: boolean | undefined; onFiltersUpdated?: ((filters: ", { "pluginId": "@kbn/es-query", "scope": "common", diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 5c23136a98a7a..9655a20a1dda9 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index 1b5beadfecf13..f70714d1e5938 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index b6d4f177afce2..fb8870f2325d2 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 919ee29502898..569cd4f8baf09 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index de9da5f50c4dd..71a2488190eb8 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index d17bcc483e1c3..aa3747f8476f0 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index d73b04c29be18..24c99b2aa19f8 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index dfab14fdd94de..d7649559452e8 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 096f49cdb1541..c493a75a57076 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index e36aeeed77738..b954ce0b719d2 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index eba3c262b3db4..b98b43d726106 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 504196c391a34..7a80a887ba186 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index bc9c59bd6a49c..b760568fac4e3 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index b8ac18365cd3c..27174661c8845 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 409866a3315d4..f64e0c6adbde7 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 348c6c015d348..a8c2abb735484 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-04-21 +date: 2023-04-24 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From 67927e12b2723fb8360478c99c2f9c00a7ae4c0a Mon Sep 17 00:00:00 2001 From: Ido Cohen <90558359+CohenIdo@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:26:39 +0300 Subject: [PATCH 29/29] [Cloud Security] update cloud security telemetry interface --- .../server/lib/telemetry/collectors/types.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts index 8651a3f577c1d..fe24101dfa8d4 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { BaseCspSetupBothPolicy } from '../../../../common/types'; +import { CspStatusCode } from '../../../../common/types'; export interface CspmUsage { indices: CspmIndicesStats; @@ -14,6 +14,12 @@ export interface CspmUsage { rules_stats: CspmRulesStats[]; } +export interface PackageSetupStatus { + status: CspStatusCode; + installedPackagePolicies: number; + healthyAgents: number; +} + export interface CspmIndicesStats { findings: IndexStats | {}; latest_findings: IndexStats | {}; @@ -21,9 +27,9 @@ export interface CspmIndicesStats { latest_vulnerabilities: IndexStats | {}; score: IndexStats | {}; latestPackageVersion: string; - cspm: BaseCspSetupBothPolicy; - kspm: BaseCspSetupBothPolicy; - vuln_mgmt: BaseCspSetupBothPolicy; + cspm: PackageSetupStatus; + kspm: PackageSetupStatus; + vuln_mgmt: PackageSetupStatus; } export interface IndexStats {