From 2de84ce4ddf959177bc226247d682c200fd9b1e5 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher Date: Thu, 16 May 2024 15:41:42 +0100 Subject: [PATCH 01/71] [One Discover] Push Smart Fields / Virtual Columns down into Discover (#181870) ## Summary Implements **part one** of https://github.com/elastic/kibana/issues/181674. The main focus here is to get the code for Smart Fields and Virtual Columns located within Discover. Whilst some hooks have been added to decide what to register / render these should most definitely be treated as "subject to change" once the real data type context awareness lands in Discover. For now these hooks rely on the old extension points to say "yes, this is a logs context". As far as I could see other solutions are not using the extension points that have been temporarily altered here. As this is functionality related to a data type (logs) and not a solution (observability, for example) this is all treated as a first class citizen. Some code has been moved to the `discover-utils`. ## Reviewer notes - Nothing should really have *changed*, this is mostly about verifying that the Smart Fields and Virtual Columns still work as expected when using Logs Explorer (and that core Discover still works as expected). Please also check the Flyout still works as expected as this was rebased on top of https://github.com/elastic/kibana/pull/180262/files and there were some conflicts between file relocation in the two. - There are a couple of components now duplicated in Discover and Logs Explorer as the `actions` column still uses them and hasn't been moved across yet. Screenshot 2024-04-28 at 15 39 15 Screenshot 2024-04-28 at 15 39 21 --------- Co-authored-by: Achyut Jhunjhunwala --- .../src/data_types/index.ts | 9 + .../src/data_types/logs/index.ts | 10 + .../src/data_types/logs/types.ts | 9 +- .../logs/utils/get_field_from_doc.ts | 7 +- .../src/data_types/logs/utils/index.ts | 9 + packages/kbn-discover-utils/src/index.ts | 1 + .../common/data_types/logs/constants.ts | 65 ++++ .../common/data_types/logs/display_options.ts | 14 + .../layout/discover_documents.test.tsx | 20 +- .../components/layout/discover_documents.tsx | 11 +- .../discover_sidebar_responsive.test.tsx | 18 +- .../sidebar/discover_sidebar_responsive.tsx | 5 +- .../main/hooks/grid_customisations/index.ts | 28 ++ .../main/hooks/grid_customisations/logs.tsx | 31 ++ .../use_virtual_column_services.tsx | 23 ++ .../sidebar/use_additional_field_groups.tsx | 37 +++ .../data_types/logs}/copy_button.tsx | 5 +- .../data_types/logs}/filter_in_button.tsx | 9 +- .../data_types/logs}/filter_out_button.tsx | 9 +- .../components/data_types/logs}/log_level.tsx | 11 +- .../data_types/logs}/popover_chip.tsx | 5 +- .../data_types/logs/translations.tsx | 300 ++++++++++++++++++ .../virtual_columns/logs}/cell_renderer.tsx | 13 +- .../virtual_columns/logs}/column.tsx | 7 +- .../content_column_tooltip.tsx | 9 +- .../logs/column_tooltips/field_with_token.tsx | 44 +++ .../logs/column_tooltips/hover_popover.tsx | 59 ++++ .../resource_column_tooltip.tsx | 12 +- .../logs/column_tooltips/tooltip_button.tsx | 79 +++++ .../virtual_columns/logs}/content.tsx | 19 +- .../virtual_columns/logs}/resource.tsx | 13 +- .../virtual_columns/logs}/utils/resource.ts | 10 +- .../data_table_customisation.ts | 9 +- .../field_list_customisation.ts | 7 +- src/plugins/discover/tsconfig.json | 6 +- .../customizations/custom_cell_renderer.tsx | 17 - .../public/customizations/custom_column.tsx | 14 - .../customizations/custom_control_column.tsx | 2 +- .../customizations/custom_field_list.ts | 24 -- .../customizations/logs_explorer_profile.tsx | 10 +- .../public/utils/get_stack_trace.ts | 3 +- .../logs_explorer/tsconfig.json | 2 - .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../columns_selection.ts | 4 +- .../columns_selection.ts | 4 +- 47 files changed, 812 insertions(+), 194 deletions(-) create mode 100644 packages/kbn-discover-utils/src/data_types/index.ts create mode 100644 packages/kbn-discover-utils/src/data_types/logs/index.ts rename x-pack/plugins/observability_solution/logs_explorer/common/document.ts => packages/kbn-discover-utils/src/data_types/logs/types.ts (89%) rename x-pack/plugins/observability_solution/logs_explorer/public/utils/get_field_from_flattened_doc.ts => packages/kbn-discover-utils/src/data_types/logs/utils/get_field_from_doc.ts (65%) create mode 100644 packages/kbn-discover-utils/src/data_types/logs/utils/index.ts create mode 100644 src/plugins/discover/common/data_types/logs/constants.ts create mode 100644 src/plugins/discover/common/data_types/logs/display_options.ts create mode 100644 src/plugins/discover/public/application/main/hooks/grid_customisations/index.ts create mode 100644 src/plugins/discover/public/application/main/hooks/grid_customisations/logs.tsx create mode 100644 src/plugins/discover/public/application/main/hooks/grid_customisations/use_virtual_column_services.tsx create mode 100644 src/plugins/discover/public/application/main/hooks/sidebar/use_additional_field_groups.tsx rename {x-pack/plugins/observability_solution/logs_explorer/public/components/common => src/plugins/discover/public/components/data_types/logs}/copy_button.tsx (82%) rename {x-pack/plugins/observability_solution/logs_explorer/public/components/common => src/plugins/discover/public/components/data_types/logs}/filter_in_button.tsx (75%) rename {x-pack/plugins/observability_solution/logs_explorer/public/components/common => src/plugins/discover/public/components/data_types/logs}/filter_out_button.tsx (76%) rename {x-pack/plugins/observability_solution/logs_explorer/public/components/common => src/plugins/discover/public/components/data_types/logs}/log_level.tsx (79%) rename {x-pack/plugins/observability_solution/logs_explorer/public/components/common => src/plugins/discover/public/components/data_types/logs}/popover_chip.tsx (95%) create mode 100644 src/plugins/discover/public/components/data_types/logs/translations.tsx rename {x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns => src/plugins/discover/public/components/discover_grid/virtual_columns/logs}/cell_renderer.tsx (63%) rename {x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns => src/plugins/discover/public/components/discover_grid/virtual_columns/logs}/column.tsx (76%) rename {x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns => src/plugins/discover/public/components/discover_grid/virtual_columns/logs}/column_tooltips/content_column_tooltip.tsx (80%) create mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/field_with_token.tsx create mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/hover_popover.tsx rename {x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns => src/plugins/discover/public/components/discover_grid/virtual_columns/logs}/column_tooltips/resource_column_tooltip.tsx (77%) create mode 100644 src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/tooltip_button.tsx rename {x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns => src/plugins/discover/public/components/discover_grid/virtual_columns/logs}/content.tsx (86%) rename {x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns => src/plugins/discover/public/components/discover_grid/virtual_columns/logs}/resource.tsx (82%) rename {x-pack/plugins/observability_solution/logs_explorer/public => src/plugins/discover/public/components/discover_grid/virtual_columns/logs}/utils/resource.ts (80%) delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_cell_renderer.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_column.tsx delete mode 100644 x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_field_list.ts diff --git a/packages/kbn-discover-utils/src/data_types/index.ts b/packages/kbn-discover-utils/src/data_types/index.ts new file mode 100644 index 0000000000000..1bb391b454f28 --- /dev/null +++ b/packages/kbn-discover-utils/src/data_types/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 * from './logs'; diff --git a/packages/kbn-discover-utils/src/data_types/logs/index.ts b/packages/kbn-discover-utils/src/data_types/logs/index.ts new file mode 100644 index 0000000000000..21d54fd754a3e --- /dev/null +++ b/packages/kbn-discover-utils/src/data_types/logs/index.ts @@ -0,0 +1,10 @@ +/* + * 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 * from './types'; +export * from './utils'; diff --git a/x-pack/plugins/observability_solution/logs_explorer/common/document.ts b/packages/kbn-discover-utils/src/data_types/logs/types.ts similarity index 89% rename from x-pack/plugins/observability_solution/logs_explorer/common/document.ts rename to packages/kbn-discover-utils/src/data_types/logs/types.ts index 778d8546c2d1c..27a4313fe2548 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/common/document.ts +++ b/packages/kbn-discover-utils/src/data_types/logs/types.ts @@ -1,11 +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. + * 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 type { DataTableRecord } from '@kbn/discover-utils/src/types'; +import { DataTableRecord } from '../../types'; export interface LogDocument extends DataTableRecord { flattened: { @@ -40,7 +41,7 @@ export interface LogDocument extends DataTableRecord { }; } -export interface FlyoutDoc { +export interface LogFlyoutDoc { '@timestamp': string; 'log.level'?: string; message?: string; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/utils/get_field_from_flattened_doc.ts b/packages/kbn-discover-utils/src/data_types/logs/utils/get_field_from_doc.ts similarity index 65% rename from x-pack/plugins/observability_solution/logs_explorer/public/utils/get_field_from_flattened_doc.ts rename to packages/kbn-discover-utils/src/data_types/logs/utils/get_field_from_doc.ts index 7d05d9ab61583..f399976dcb85a 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/utils/get_field_from_flattened_doc.ts +++ b/packages/kbn-discover-utils/src/data_types/logs/utils/get_field_from_doc.ts @@ -1,11 +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. + * 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 { LogDocument } from '../../common/document'; +import { LogDocument } from '../types'; type Field = keyof LogDocument['flattened']; diff --git a/packages/kbn-discover-utils/src/data_types/logs/utils/index.ts b/packages/kbn-discover-utils/src/data_types/logs/utils/index.ts new file mode 100644 index 0000000000000..1c26d5efff12d --- /dev/null +++ b/packages/kbn-discover-utils/src/data_types/logs/utils/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 * from './get_field_from_doc'; diff --git a/packages/kbn-discover-utils/src/index.ts b/packages/kbn-discover-utils/src/index.ts index b1dc190111746..0e811d862b6ff 100644 --- a/packages/kbn-discover-utils/src/index.ts +++ b/packages/kbn-discover-utils/src/index.ts @@ -10,3 +10,4 @@ export * from './constants'; export * as fieldConstants from './field_constants'; export * from './hooks'; export * from './utils'; +export * from './data_types'; diff --git a/src/plugins/discover/common/data_types/logs/constants.ts b/src/plugins/discover/common/data_types/logs/constants.ts new file mode 100644 index 0000000000000..5726bf8439b8f --- /dev/null +++ b/src/plugins/discover/common/data_types/logs/constants.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { fieldConstants } from '@kbn/discover-utils'; +import { SmartFieldGridColumnOptions } from './display_options'; + +export * from '@kbn/discover-utils/src/field_constants'; + +export const LOGS_EXPLORER_PROFILE_ID = 'logs-explorer'; + +// Virtual column fields +export const CONTENT_FIELD = 'content'; +export const RESOURCE_FIELD = 'resource'; + +// Sizing +export const DATA_GRID_COLUMN_WIDTH_SMALL = 240; +export const DATA_GRID_COLUMN_WIDTH_MEDIUM = 320; +export const ACTIONS_COLUMN_WIDTH = 80; + +export const RESOURCE_FIELD_CONFIGURATION: SmartFieldGridColumnOptions = { + type: 'smart-field', + smartField: RESOURCE_FIELD, + fallbackFields: [fieldConstants.HOST_NAME_FIELD, fieldConstants.SERVICE_NAME_FIELD], + width: DATA_GRID_COLUMN_WIDTH_MEDIUM, +}; + +export const CONTENT_FIELD_CONFIGURATION: SmartFieldGridColumnOptions = { + type: 'smart-field', + smartField: CONTENT_FIELD, + fallbackFields: [fieldConstants.MESSAGE_FIELD], +}; + +export const SMART_FALLBACK_FIELDS = { + [CONTENT_FIELD]: CONTENT_FIELD_CONFIGURATION, + [RESOURCE_FIELD]: RESOURCE_FIELD_CONFIGURATION, +}; + +// UI preferences +export const DEFAULT_COLUMNS = [RESOURCE_FIELD_CONFIGURATION, CONTENT_FIELD_CONFIGURATION]; +export const DEFAULT_ROWS_PER_PAGE = 100; + +// List of prefixes which needs to be filtered out for Display in Content Column +export const FILTER_OUT_FIELDS_PREFIXES_FOR_CONTENT = [ + '_', // Filter fields like '_id', '_score' + '@timestamp', + 'agent.', + 'elastic_agent.', + 'data_stream.', + 'ecs.', + 'host.', + 'container.', + 'cloud.', + 'kubernetes.', + 'orchestrator.', + 'log.', + 'service.', +]; + +export const DEFAULT_ALLOWED_DATA_VIEWS = ['logs', 'auditbeat', 'filebeat', 'winlogbeat']; +export const DEFAULT_ALLOWED_LOGS_DATA_VIEWS = ['logs', 'auditbeat', 'filebeat', 'winlogbeat']; diff --git a/src/plugins/discover/common/data_types/logs/display_options.ts b/src/plugins/discover/common/data_types/logs/display_options.ts new file mode 100644 index 0000000000000..13c760649bbbe --- /dev/null +++ b/src/plugins/discover/common/data_types/logs/display_options.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 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 interface SmartFieldGridColumnOptions { + type: 'smart-field'; + smartField: 'content' | 'resource'; + fallbackFields: string[]; + width?: number; +} diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx index bc739d9433e63..ae4b05f495cfa 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.test.tsx @@ -109,17 +109,6 @@ describe('Discover documents layout', () => { }); test('should render customisations', async () => { - const customCellRenderer = { - content: () => Test, - }; - - const customGridColumnsConfiguration = { - content: () => ({ - id: 'content', - displayText: Column, - }), - }; - const customControlColumnsConfiguration = () => ({ leadingControlColumns: [], trailingControlColumns: [], @@ -127,8 +116,7 @@ describe('Discover documents layout', () => { const customization: DiscoverCustomization = { id: 'data_table', - customCellRenderer, - customGridColumnsConfiguration, + logsEnabled: true, customControlColumnsConfiguration, }; @@ -137,12 +125,10 @@ describe('Discover documents layout', () => { const discoverGridComponent = component.find(DiscoverGrid); expect(discoverGridComponent.exists()).toBeTruthy(); - expect(discoverGridComponent.prop('externalCustomRenderers')).toEqual(customCellRenderer); - expect(discoverGridComponent.prop('customGridColumnsConfiguration')).toEqual( - customGridColumnsConfiguration - ); expect(discoverGridComponent.prop('customControlColumnsConfiguration')).toEqual( customControlColumnsConfiguration ); + expect(discoverGridComponent.prop('externalCustomRenderers')).toBeDefined(); + expect(discoverGridComponent.prop('customGridColumnsConfiguration')).toBeDefined(); }); }); diff --git a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx index 57df6e8639ead..b5f50f2ca9c14 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_documents.tsx @@ -67,6 +67,7 @@ import { useFetchMoreRecords } from './use_fetch_more_records'; import { SelectedVSAvailableCallout } from './selected_vs_available_callout'; import { useDiscoverCustomization } from '../../../../customizations'; import { onResizeGridColumn } from '../../../../utils/on_resize_grid_column'; +import { useContextualGridCustomisations } from '../../hooks/grid_customisations'; const containerStyles = css` position: relative; @@ -258,11 +259,9 @@ function DiscoverDocumentsComponent({ [dataView, onAddColumn, onAddFilter, onRemoveColumn, query, savedSearch.id, setExpandedDoc] ); - const { - customCellRenderer: externalCustomRenderers, - customGridColumnsConfiguration, - customControlColumnsConfiguration, - } = useDiscoverCustomization('data_table') || {}; + const { customControlColumnsConfiguration } = useDiscoverCustomization('data_table') || {}; + const { customCellRenderer, customGridColumnsConfiguration } = + useContextualGridCustomisations() || {}; const documents = useObservable(stateContainer.dataState.data$.documents$); @@ -432,7 +431,7 @@ function DiscoverDocumentsComponent({ totalHits={totalHits} onFetchMoreRecords={onFetchMoreRecords} componentsTourSteps={TOUR_STEPS} - externalCustomRenderers={externalCustomRenderers} + externalCustomRenderers={customCellRenderer} customGridColumnsConfiguration={customGridColumnsConfiguration} customControlColumnsConfiguration={customControlColumnsConfiguration} /> diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx index d245eb337bff4..d915821d88e25 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.test.tsx @@ -37,7 +37,6 @@ import { buildDataTableRecord } from '@kbn/discover-utils'; import type { DataTableRecord } from '@kbn/discover-utils/types'; import type { DiscoverCustomizationId } from '../../../../customizations/customization_service'; import { FieldListCustomization, SearchBarCustomization } from '../../../../customizations'; -import { DataViewField } from '@kbn/data-views-plugin/common'; const mockSearchBarCustomization: SearchBarCustomization = { id: 'search_bar', @@ -46,22 +45,9 @@ const mockSearchBarCustomization: SearchBarCustomization = { .mockName('CustomDataViewPickerMock'), }; -const smartFields = [ - new DataViewField({ - name: 'mock_field', - type: 'mock_field', - searchable: false, - aggregatable: false, - }), -]; - -const additionalFieldGroups = { - smartFields, -}; - const mockFieldListCustomisation: FieldListCustomization = { id: 'field_list', - additionalFieldGroups, + logsFieldsEnabled: true, }; let mockUseCustomizations = false; @@ -786,7 +772,7 @@ describe('discover responsive sidebar', function () { const smartFieldsCount = findTestSubject(comp, 'fieldListGroupedSmartFields-count'); - expect(smartFieldsCount.text()).toBe('1'); + expect(smartFieldsCount.text()).toBe('2'); }); }); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx index e9caffaecccbc..67c7bd28f0b0b 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/discover_sidebar_responsive.tsx @@ -38,6 +38,7 @@ import { DiscoverSidebarReducerStatus, } from './lib/sidebar_reducer'; import { useDiscoverCustomization } from '../../../../customizations'; +import { useAdditionalFieldGroups } from '../../hooks/sidebar/use_additional_field_groups'; const EMPTY_FIELD_COUNTS = {}; @@ -331,7 +332,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) ); const searchBarCustomization = useDiscoverCustomization('search_bar'); - const fieldListCustomization = useDiscoverCustomization('field_list'); + const additionalFieldGroups = useAdditionalFieldGroups(); const CustomDataViewPicker = searchBarCustomization?.CustomDataViewPicker; const createField = unifiedFieldListSidebarContainerApi?.createField; @@ -408,7 +409,7 @@ export function DiscoverSidebarResponsive(props: DiscoverSidebarResponsiveProps) onAddFilter={onAddFilter} onFieldEdited={onFieldEdited} prependInFlyout={prependDataViewPickerForMobile} - additionalFieldGroups={fieldListCustomization?.additionalFieldGroups} + additionalFieldGroups={additionalFieldGroups} /> ) : null} diff --git a/src/plugins/discover/public/application/main/hooks/grid_customisations/index.ts b/src/plugins/discover/public/application/main/hooks/grid_customisations/index.ts new file mode 100644 index 0000000000000..301c96695de02 --- /dev/null +++ b/src/plugins/discover/public/application/main/hooks/grid_customisations/index.ts @@ -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 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 { useMemo } from 'react'; +import { useDiscoverServices } from '../../../../hooks/use_discover_services'; +import { useDiscoverCustomization } from '../../../../customizations'; +import { getLogsVirtualColumnsConfiguration } from './logs'; + +export * from './logs'; + +export const useContextualGridCustomisations = () => { + const { data } = useDiscoverServices(); + // TODO / NOTE: This will eventually rely on Discover's context resolution to determine which fields + // are returned based on the data type. + const isLogsContext = useDiscoverCustomization('data_table')?.logsEnabled; + + const virtualColumnsConfiguration = useMemo(() => { + if (!isLogsContext) return null; + if (isLogsContext) return getLogsVirtualColumnsConfiguration(data); + }, [data, isLogsContext]); + + return virtualColumnsConfiguration; +}; diff --git a/src/plugins/discover/public/application/main/hooks/grid_customisations/logs.tsx b/src/plugins/discover/public/application/main/hooks/grid_customisations/logs.tsx new file mode 100644 index 0000000000000..ae5c51c265c5c --- /dev/null +++ b/src/plugins/discover/public/application/main/hooks/grid_customisations/logs.tsx @@ -0,0 +1,31 @@ +/* + * 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 { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { CONTENT_FIELD, RESOURCE_FIELD } from '../../../../../common/data_types/logs/constants'; +import { renderCell } from '../../../../components/discover_grid/virtual_columns/logs/cell_renderer'; +import { renderColumn } from '../../../../components/discover_grid/virtual_columns/logs/column'; + +export const getLogsVirtualColumnsConfiguration = (data: DataPublicPluginStart) => { + return { + customCellRenderer: createCustomCellRenderer({ data }), + customGridColumnsConfiguration: createCustomGridColumnsConfiguration(), + }; +}; + +export const createCustomCellRenderer = ({ data }: { data: DataPublicPluginStart }) => { + return { + [CONTENT_FIELD]: renderCell(CONTENT_FIELD, { data }), + [RESOURCE_FIELD]: renderCell(RESOURCE_FIELD, { data }), + }; +}; + +export const createCustomGridColumnsConfiguration = () => ({ + [CONTENT_FIELD]: renderColumn(CONTENT_FIELD), + [RESOURCE_FIELD]: renderColumn(RESOURCE_FIELD), +}); diff --git a/src/plugins/discover/public/application/main/hooks/grid_customisations/use_virtual_column_services.tsx b/src/plugins/discover/public/application/main/hooks/grid_customisations/use_virtual_column_services.tsx new file mode 100644 index 0000000000000..386fc6586258d --- /dev/null +++ b/src/plugins/discover/public/application/main/hooks/grid_customisations/use_virtual_column_services.tsx @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 createContainer from 'constate'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; + +export interface UseVirtualColumnServices { + services: { + data: DataPublicPluginStart; + dataView: DataView; + }; +} + +const useVirtualColumns = ({ services }: UseVirtualColumnServices) => services; + +export const [VirtualColumnServiceProvider, useVirtualColumnServiceContext] = + createContainer(useVirtualColumns); diff --git a/src/plugins/discover/public/application/main/hooks/sidebar/use_additional_field_groups.tsx b/src/plugins/discover/public/application/main/hooks/sidebar/use_additional_field_groups.tsx new file mode 100644 index 0000000000000..6f9ab0073904f --- /dev/null +++ b/src/plugins/discover/public/application/main/hooks/sidebar/use_additional_field_groups.tsx @@ -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 { DataViewField } from '@kbn/data-views-plugin/common'; +import { useDiscoverCustomization } from '../../../../customizations'; +import * as constants from '../../../../../common/data_types/logs/constants'; + +export const useAdditionalFieldGroups = () => { + // TODO / NOTE: This will eventually rely on Discover's context resolution to determine which fields + // are returned based on the data type. + const isLogsContext = useDiscoverCustomization('field_list')?.logsFieldsEnabled; + + if (isLogsContext) { + const smartFields = [ + new DataViewField({ + name: constants.RESOURCE_FIELD, + type: 'smart_field', + searchable: false, + aggregatable: false, + }), + new DataViewField({ + name: constants.CONTENT_FIELD, + type: 'smart_field', + searchable: false, + aggregatable: false, + }), + ]; + return { + smartFields, + }; + } +}; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/copy_button.tsx b/src/plugins/discover/public/components/data_types/logs/copy_button.tsx similarity index 82% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/common/copy_button.tsx rename to src/plugins/discover/public/components/data_types/logs/copy_button.tsx index fe02a7a872720..83da6f3896edf 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/copy_button.tsx +++ b/src/plugins/discover/public/components/data_types/logs/copy_button.tsx @@ -1,8 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { EuiButtonEmpty, EuiFlexItem, copyToClipboard } from '@elastic/eui'; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/filter_in_button.tsx b/src/plugins/discover/public/components/data_types/logs/filter_in_button.tsx similarity index 75% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/common/filter_in_button.tsx rename to src/plugins/discover/public/components/data_types/logs/filter_in_button.tsx index e2f43d1b0c5fc..71412a90002bd 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/filter_in_button.tsx +++ b/src/plugins/discover/public/components/data_types/logs/filter_in_button.tsx @@ -1,15 +1,16 @@ /* * 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. + * 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 { EuiButtonEmpty, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import { generateFilters } from '@kbn/data-plugin/public'; -import { filterForText, actionFilterForText } from './translations'; -import { useVirtualColumnServiceContext } from '../../hooks/use_virtual_column_services'; +import { useVirtualColumnServiceContext } from '../../../application/main/hooks/grid_customisations/use_virtual_column_services'; +import { actionFilterForText, filterForText } from './translations'; export const FilterInButton = ({ property, value }: { property: string; value: string }) => { const ariaFilterForText = actionFilterForText(value); diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/filter_out_button.tsx b/src/plugins/discover/public/components/data_types/logs/filter_out_button.tsx similarity index 76% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/common/filter_out_button.tsx rename to src/plugins/discover/public/components/data_types/logs/filter_out_button.tsx index 9291e17cc44fd..3c3892b31ba5e 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/filter_out_button.tsx +++ b/src/plugins/discover/public/components/data_types/logs/filter_out_button.tsx @@ -1,15 +1,16 @@ /* * 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. + * 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 { EuiButtonEmpty, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import { generateFilters } from '@kbn/data-plugin/public'; -import { filterOutText, actionFilterOutText } from './translations'; -import { useVirtualColumnServiceContext } from '../../hooks/use_virtual_column_services'; +import { useVirtualColumnServiceContext } from '../../../application/main/hooks/grid_customisations/use_virtual_column_services'; +import { actionFilterOutText, filterOutText } from './translations'; export const FilterOutButton = ({ property, value }: { property: string; value: string }) => { const ariaFilterOutText = actionFilterOutText(value); diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/log_level.tsx b/src/plugins/discover/public/components/data_types/logs/log_level.tsx similarity index 79% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/common/log_level.tsx rename to src/plugins/discover/public/components/data_types/logs/log_level.tsx index 9f5dfef23ce0d..bddc6486f3c81 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/log_level.tsx +++ b/src/plugins/discover/public/components/data_types/logs/log_level.tsx @@ -1,15 +1,16 @@ /* * 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. + * 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 React from 'react'; import { useEuiTheme } from '@elastic/eui'; -import { FlyoutDoc } from '../../../common/document'; +import { LogFlyoutDoc } from '@kbn/discover-utils/src'; +import * as constants from '../../../../common/data_types/logs/constants'; import { ChipWithPopover } from './popover_chip'; -import * as constants from '../../../common/constants'; const LEVEL_DICT = { error: 'danger', @@ -19,7 +20,7 @@ const LEVEL_DICT = { } as const; interface LogLevelProps { - level: FlyoutDoc['log.level']; + level: LogFlyoutDoc['log.level']; dataTestSubj?: string; renderInFlyout?: boolean; } diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/popover_chip.tsx b/src/plugins/discover/public/components/data_types/logs/popover_chip.tsx similarity index 95% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/common/popover_chip.tsx rename to src/plugins/discover/public/components/data_types/logs/popover_chip.tsx index 2811bbf5480c4..8631cb563cddb 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/common/popover_chip.tsx +++ b/src/plugins/discover/public/components/data_types/logs/popover_chip.tsx @@ -1,8 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 React, { useCallback, useState } from 'react'; diff --git a/src/plugins/discover/public/components/data_types/logs/translations.tsx b/src/plugins/discover/public/components/data_types/logs/translations.tsx new file mode 100644 index 0000000000000..fcf846d0ea891 --- /dev/null +++ b/src/plugins/discover/public/components/data_types/logs/translations.tsx @@ -0,0 +1,300 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiCode } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +export const flyoutContentLabel = i18n.translate('discover.logs.flyoutDetail.label.message', { + defaultMessage: 'Content breakdown', +}); + +export const contentLabel = i18n.translate('discover.logs.dataTable.header.popover.content', { + defaultMessage: 'Content', +}); + +export const resourceLabel = i18n.translate('discover.logs.dataTable.header.popover.resource', { + defaultMessage: 'Resource', +}); + +export const actionsLabel = i18n.translate('discover.logs.dataTable.header.popover.actions', { + defaultMessage: 'Actions', +}); + +export const actionsLabelLowerCase = i18n.translate( + 'discover.logs.dataTable.header.popover.actions.lowercase', + { + defaultMessage: 'actions', + } +); + +export const flyoutServiceLabel = i18n.translate('discover.logs.flyoutDetail.label.service', { + defaultMessage: 'Service', +}); + +export const flyoutTraceLabel = i18n.translate('discover.logs.flyoutDetail.label.trace', { + defaultMessage: 'Trace', +}); + +export const flyoutHostNameLabel = i18n.translate('discover.logs.flyoutDetail.label.hostName', { + defaultMessage: 'Host name', +}); + +export const serviceInfraAccordionTitle = i18n.translate( + 'discover.logs.flyoutDetail.accordion.title.serviceInfra', + { + defaultMessage: 'Service & Infrastructure', + } +); + +export const cloudAccordionTitle = i18n.translate( + 'discover.logs.flyoutDetail.accordion.title.cloud', + { + defaultMessage: 'Cloud', + } +); + +export const otherAccordionTitle = i18n.translate( + 'discover.logs.flyoutDetail.accordion.title.other', + { + defaultMessage: 'Other', + } +); + +export const flyoutOrchestratorClusterNameLabel = i18n.translate( + 'discover.logs.flyoutDetail.label.orchestratorClusterName', + { + defaultMessage: 'Orchestrator cluster Name', + } +); + +export const flyoutOrchestratorResourceIdLabel = i18n.translate( + 'discover.logs.flyoutDetail.label.orchestratorResourceId', + { + defaultMessage: 'Orchestrator resource ID', + } +); + +export const flyoutCloudProviderLabel = i18n.translate( + 'discover.logs.flyoutDetail.label.cloudProvider', + { + defaultMessage: 'Cloud provider', + } +); + +export const flyoutCloudRegionLabel = i18n.translate( + 'discover.logs.flyoutDetail.label.cloudRegion', + { + defaultMessage: 'Cloud region', + } +); + +export const flyoutCloudAvailabilityZoneLabel = i18n.translate( + 'discover.logs.flyoutDetail.label.cloudAvailabilityZone', + { + defaultMessage: 'Cloud availability zone', + } +); + +export const flyoutCloudProjectIdLabel = i18n.translate( + 'discover.logs.flyoutDetail.label.cloudProjectId', + { + defaultMessage: 'Cloud project ID', + } +); + +export const flyoutCloudInstanceIdLabel = i18n.translate( + 'discover.logs.flyoutDetail.label.cloudInstanceId', + { + defaultMessage: 'Cloud instance ID', + } +); + +export const flyoutLogPathFileLabel = i18n.translate( + 'discover.logs.flyoutDetail.label.logPathFile', + { + defaultMessage: 'Log path file', + } +); + +export const flyoutNamespaceLabel = i18n.translate('discover.logs.flyoutDetail.label.namespace', { + defaultMessage: 'Namespace', +}); + +export const flyoutDatasetLabel = i18n.translate('discover.logs.flyoutDetail.label.dataset', { + defaultMessage: 'Dataset', +}); + +export const flyoutShipperLabel = i18n.translate('discover.logs.flyoutDetail.label.shipper', { + defaultMessage: 'Shipper', +}); + +export const actionFilterForText = (text: string) => + i18n.translate('discover.logs.flyoutDetail.value.hover.filterFor', { + defaultMessage: 'Filter for this {value}', + values: { + value: text, + }, + }); + +export const actionFilterOutText = (text: string) => + i18n.translate('discover.logs.flyoutDetail.value.hover.filterOut', { + defaultMessage: 'Filter out this {value}', + values: { + value: text, + }, + }); + +export const filterOutText = i18n.translate('discover.logs.popoverAction.filterOut', { + defaultMessage: 'Filter out', +}); + +export const filterForText = i18n.translate('discover.logs.popoverAction.filterFor', { + defaultMessage: 'Filter for', +}); + +export const flyoutHoverActionFilterForFieldPresentText = i18n.translate( + 'discover.logs.flyoutDetail.value.hover.filterForFieldPresent', + { + defaultMessage: 'Filter for field present', + } +); + +export const flyoutHoverActionToggleColumnText = i18n.translate( + 'discover.logs.flyoutDetail.value.hover.toggleColumn', + { + defaultMessage: 'Toggle column in table', + } +); + +export const flyoutHoverActionCopyToClipboardText = i18n.translate( + 'discover.logs.flyoutDetail.value.hover.copyToClipboard', + { + defaultMessage: 'Copy to clipboard', + } +); + +export const copyValueText = i18n.translate('discover.logs.popoverAction.copyValue', { + defaultMessage: 'Copy value', +}); + +export const copyValueAriaText = (fieldName: string) => + i18n.translate('discover.logs.popoverAction.copyValueAriaText', { + defaultMessage: 'Copy value of {fieldName}', + values: { + fieldName, + }, + }); + +export const flyoutAccordionShowMoreText = (count: number) => + i18n.translate('discover.logs.flyoutDetail.section.showMore', { + defaultMessage: '+ {hiddenCount} more', + values: { + hiddenCount: count, + }, + }); + +export const openCellActionPopoverAriaText = i18n.translate( + 'discover.logs.popoverAction.openPopover', + { + defaultMessage: 'Open popover', + } +); + +export const closeCellActionPopoverText = i18n.translate( + 'discover.logs.popoverAction.closePopover', + { + defaultMessage: 'Close popover', + } +); + +export const contentHeaderTooltipParagraph1 = ( + log.level, + message: message, + }} + /> +); + +export const contentHeaderTooltipParagraph2 = i18n.translate( + 'discover.logs.dataTable.header.content.tooltip.paragraph2', + { + defaultMessage: 'When the message field is empty, one of the following is displayed:', + } +); + +export const resourceHeaderTooltipParagraph = i18n.translate( + 'discover.logs.dataTable.header.resource.tooltip.paragraph', + { + defaultMessage: "Fields that provide information on the document's source, such as:", + } +); + +export const actionsHeaderTooltipParagraph = i18n.translate( + 'discover.logs.dataTable.header.actions.tooltip.paragraph', + { + defaultMessage: 'Fields that provide actionable information, such as:', + } +); + +export const actionsHeaderTooltipExpandAction = i18n.translate( + 'discover.logs.dataTable.header.actions.tooltip.expand', + { defaultMessage: 'Expand log details' } +); + +export const actionsHeaderTooltipDegradedAction = ( + + _ignored + + ), + }} + /> +); + +export const actionsHeaderTooltipStacktraceAction = i18n.translate( + 'discover.logs.dataTable.header.actions.tooltip.stacktrace', + { defaultMessage: 'Access to available stacktraces based on:' } +); + +export const degradedDocButtonLabelWhenPresent = i18n.translate( + 'discover.logs.dataTable.controlColumn.actions.button.degradedDocPresent', + { + defaultMessage: + "This document couldn't be parsed correctly. Not all fields are properly populated", + } +); + +export const degradedDocButtonLabelWhenNotPresent = i18n.translate( + 'discover.logs.dataTable.controlColumn.actions.button.degradedDocNotPresent', + { + defaultMessage: 'All fields in this document were parsed correctly', + } +); + +export const stacktraceAvailableControlButton = i18n.translate( + 'discover.logs.dataTable.controlColumn.actions.button.stacktrace.available', + { + defaultMessage: 'Stacktraces available', + } +); + +export const stacktraceNotAvailableControlButton = i18n.translate( + 'discover.logs.dataTable.controlColumn.actions.button.stacktrace.notAvailable', + { + defaultMessage: 'Stacktraces not available', + } +); diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/cell_renderer.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/cell_renderer.tsx similarity index 63% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/cell_renderer.tsx rename to src/plugins/discover/public/components/discover_grid/virtual_columns/logs/cell_renderer.tsx index eefb165167f4a..d7f5b2fc63170 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/cell_renderer.tsx +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/cell_renderer.tsx @@ -1,20 +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. + * 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 React from 'react'; import type { DataGridCellValueElementProps } from '@kbn/unified-data-table'; -import { LogsExplorerDiscoverServices } from '../../controller'; -import { VirtualColumnServiceProvider } from '../../hooks/use_virtual_column_services'; -import { CONTENT_FIELD, RESOURCE_FIELD } from '../../../common/constants'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { VirtualColumnServiceProvider } from '../../../../application/main/hooks/grid_customisations/use_virtual_column_services'; +import { CONTENT_FIELD, RESOURCE_FIELD } from '../../../../../common/data_types/logs/constants'; import { Content } from './content'; import { Resource } from './resource'; export const renderCell = - (type: string, { data }: { data: LogsExplorerDiscoverServices['data'] }) => + (type: string, { data }: { data: DataPublicPluginStart }) => (props: DataGridCellValueElementProps) => { const { dataView } = props; const virtualColumnServices = { diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column.tsx similarity index 76% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column.tsx rename to src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column.tsx index 9e1ad91079ace..b168478dd9388 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column.tsx +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column.tsx @@ -1,14 +1,15 @@ /* * 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. + * 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 React from 'react'; import { CustomGridColumnProps } from '@kbn/unified-data-table'; +import { CONTENT_FIELD, RESOURCE_FIELD } from '../../../../../common/data_types/logs/constants'; import { ContentColumnTooltip } from './column_tooltips/content_column_tooltip'; -import { CONTENT_FIELD, RESOURCE_FIELD } from '../../../common/constants'; import { ResourceColumnTooltip } from './column_tooltips/resource_column_tooltip'; export const renderColumn = diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/content_column_tooltip.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/content_column_tooltip.tsx similarity index 80% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/content_column_tooltip.tsx rename to src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/content_column_tooltip.tsx index df33f1f1beff3..948085f2d6e02 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/content_column_tooltip.tsx +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/content_column_tooltip.tsx @@ -1,8 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { EuiText, useEuiTheme } from '@elastic/eui'; @@ -13,10 +14,10 @@ import { contentHeaderTooltipParagraph1, contentHeaderTooltipParagraph2, contentLabel, -} from '../../common/translations'; +} from '../../../../data_types/logs/translations'; +import * as constants from '../../../../../../common/data_types/logs/constants'; import { TooltipButton } from './tooltip_button'; import { FieldWithToken } from './field_with_token'; -import * as constants from '../../../../common/constants'; export const ContentColumnTooltip = ({ column, headerRowHeight }: CustomGridColumnProps) => { const { euiTheme } = useEuiTheme(); diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/field_with_token.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/field_with_token.tsx new file mode 100644 index 0000000000000..abe5ed875e767 --- /dev/null +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/field_with_token.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 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 { EuiFlexGroup, EuiFlexItem, EuiText, EuiToken } from '@elastic/eui'; +import React from 'react'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; + +const spacingXsCss = css` + margin-bottom: ${euiThemeVars.euiSizeXS}; +`; + +export const FieldWithToken = ({ + field, + iconType = 'tokenKeyword', +}: { + field: string; + iconType?: string; +}) => { + return ( +
+ + + + + + + {field} + + + +
+ ); +}; diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/hover_popover.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/hover_popover.tsx new file mode 100644 index 0000000000000..100fb8b31a348 --- /dev/null +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/hover_popover.tsx @@ -0,0 +1,59 @@ +/* + * 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 React, { useEffect, useRef, useState } from 'react'; +import { EuiPopover, EuiPopoverTitle } from '@elastic/eui'; + +export const HoverPopover = ({ + children, + button, + title, +}: { + children: React.ReactChild; + button: React.ReactElement; + title: string; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const leaveTimer = useRef(null); + + const clearTimer = () => { + if (leaveTimer.current) { + clearTimeout(leaveTimer.current); + } + }; + + const onMouseEnter = () => { + clearTimer(); + setIsPopoverOpen(true); + }; + + const onMouseLeave = () => { + leaveTimer.current = setTimeout(() => setIsPopoverOpen(false), 100); + }; + + useEffect(() => { + return () => { + clearTimer(); + }; + }, []); + + return ( +
+ + {title} + {children} + +
+ ); +}; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/resource_column_tooltip.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/resource_column_tooltip.tsx similarity index 77% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/resource_column_tooltip.tsx rename to src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/resource_column_tooltip.tsx index 64a64156cbaea..1ab50b08dcb1b 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/column_tooltips/resource_column_tooltip.tsx +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/resource_column_tooltip.tsx @@ -1,8 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 React from 'react'; @@ -10,9 +11,12 @@ import { css } from '@emotion/react'; import { EuiText } from '@elastic/eui'; import type { CustomGridColumnProps } from '@kbn/unified-data-table'; import { euiThemeVars } from '@kbn/ui-theme'; -import { resourceHeaderTooltipParagraph, resourceLabel } from '../../common/translations'; +import { + resourceHeaderTooltipParagraph, + resourceLabel, +} from '../../../../data_types/logs/translations'; +import * as constants from '../../../../../../common/data_types/logs/constants'; import { TooltipButton } from './tooltip_button'; -import * as constants from '../../../../common/constants'; import { FieldWithToken } from './field_with_token'; const spacingCSS = css` diff --git a/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/tooltip_button.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/tooltip_button.tsx new file mode 100644 index 0000000000000..3f7d616be3014 --- /dev/null +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/column_tooltips/tooltip_button.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 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 React, { useEffect, useRef, useState, useCallback, useMemo } from 'react'; +import { EuiIcon } from '@elastic/eui'; +import ColumnHeaderTruncateContainer from '@kbn/unified-data-table/src/components/column_header_truncate_container'; + +import { EuiPopover, EuiPopoverTitle } from '@elastic/eui'; + +export const TooltipButton = ({ + children, + popoverTitle, + displayText, + headerRowHeight, + iconType = 'questionInCircle', +}: { + children: React.ReactChild; + popoverTitle: string; + displayText?: string; + headerRowHeight?: number; + iconType?: string; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const leaveTimer = useRef(null); + + const clearTimer = useMemo( + () => () => { + if (leaveTimer.current) { + clearTimeout(leaveTimer.current); + } + }, + [] + ); + + const onMouseEnter = useCallback(() => { + clearTimer(); + setIsPopoverOpen(true); + }, [clearTimer]); + + const onMouseLeave = useCallback(() => { + leaveTimer.current = setTimeout(() => setIsPopoverOpen(false), 100); + }, []); + + useEffect(() => { + return () => { + clearTimer(); + }; + }, [clearTimer]); + + return ( + + {displayText}{' '} + + } + isOpen={isPopoverOpen} + anchorPosition="upCenter" + panelPaddingSize="s" + ownFocus={false} + > + {popoverTitle} + {children} + + + ); +}; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/content.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/content.tsx similarity index 86% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/content.tsx rename to src/plugins/discover/public/components/discover_grid/virtual_columns/logs/content.tsx index b13cacec025f3..4be45b3d7d96f 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/content.tsx +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/content.tsx @@ -1,8 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 React, { useMemo } from 'react'; @@ -18,8 +19,8 @@ import { import { i18n } from '@kbn/i18n'; import type { DataTableRecord } from '@kbn/discover-utils/src/types'; import { dynamic } from '@kbn/shared-ux-utility'; -import { LogLevel } from '../common/log_level'; -import * as constants from '../../../common/constants'; +import * as constants from '../../../../../common/data_types/logs/constants'; +import { LogLevel } from '../../../data_types/logs/log_level'; const SourceDocument = dynamic( () => import('@kbn/unified-data-table/src/components/source_document') @@ -38,10 +39,8 @@ const LogMessage = ({ field, value }: { field?: string; value: string }) => { const renderFieldPrefix = field && field !== constants.MESSAGE_FIELD; return ( - {renderFieldPrefix && ( - {field} - )} - + {renderFieldPrefix && {field}} + {value} @@ -59,7 +58,7 @@ const SourcePopoverContent = ({ }) => { const closeButton = ( )} diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/resource.tsx b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/resource.tsx similarity index 82% rename from x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/resource.tsx rename to src/plugins/discover/public/components/discover_grid/virtual_columns/logs/resource.tsx index 7cecdeee5ce40..84f95579af7b9 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/components/virtual_columns/resource.tsx +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/resource.tsx @@ -1,18 +1,19 @@ /* * 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. + * 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 React from 'react'; import type { DataGridCellValueElementProps } from '@kbn/unified-data-table'; import { AgentName } from '@kbn/elastic-agent-utils'; import { dynamic } from '@kbn/shared-ux-utility'; -import { ChipWithPopover } from '../common/popover_chip'; -import * as constants from '../../../common/constants'; -import { getUnformattedResourceFields } from '../../utils/resource'; -import { LogDocument } from '../../../common/document'; +import { LogDocument } from '@kbn/discover-utils/src'; +import * as constants from '../../../../../common/data_types/logs/constants'; +import { getUnformattedResourceFields } from './utils/resource'; +import { ChipWithPopover } from '../../../data_types/logs/popover_chip'; const AgentIcon = dynamic(() => import('@kbn/custom-icons/src/components/agent_icon')); diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/utils/resource.ts b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/utils/resource.ts similarity index 80% rename from x-pack/plugins/observability_solution/logs_explorer/public/utils/resource.ts rename to src/plugins/discover/public/components/discover_grid/virtual_columns/logs/utils/resource.ts index 1b1f3dd078008..7f6d3b5ed7dae 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/utils/resource.ts +++ b/src/plugins/discover/public/components/discover_grid/virtual_columns/logs/utils/resource.ts @@ -1,13 +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; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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 { LogDocument, ResourceFields } from '../../common/document'; -import * as constants from '../../common/constants'; -import { getFieldFromDoc } from './get_field_from_flattened_doc'; +import { getFieldFromDoc, LogDocument, ResourceFields } from '@kbn/discover-utils/src'; +import * as constants from '../../../../../../common/data_types/logs/constants'; export const getUnformattedResourceFields = (doc: LogDocument): ResourceFields => { const serviceName = getFieldFromDoc(doc, constants.SERVICE_NAME_FIELD); diff --git a/src/plugins/discover/public/customizations/customization_types/data_table_customisation.ts b/src/plugins/discover/public/customizations/customization_types/data_table_customisation.ts index 2ed371fd2109e..3e6e510488fbd 100644 --- a/src/plugins/discover/public/customizations/customization_types/data_table_customisation.ts +++ b/src/plugins/discover/public/customizations/customization_types/data_table_customisation.ts @@ -6,15 +6,10 @@ * Side Public License, v 1. */ -import { - CustomCellRenderer, - CustomControlColumnConfiguration, - CustomGridColumnsConfiguration, -} from '@kbn/unified-data-table'; +import { CustomControlColumnConfiguration } from '@kbn/unified-data-table'; export interface DataTableCustomization { id: 'data_table'; - customCellRenderer?: CustomCellRenderer; - customGridColumnsConfiguration?: CustomGridColumnsConfiguration; + logsEnabled: boolean; // TODO / NOTE: Just temporary until Discover's data type contextual awareness lands. customControlColumnsConfiguration?: CustomControlColumnConfiguration; } diff --git a/src/plugins/discover/public/customizations/customization_types/field_list_customisation.ts b/src/plugins/discover/public/customizations/customization_types/field_list_customisation.ts index 706022112e3fe..a43126a1aaaa3 100644 --- a/src/plugins/discover/public/customizations/customization_types/field_list_customisation.ts +++ b/src/plugins/discover/public/customizations/customization_types/field_list_customisation.ts @@ -6,12 +6,7 @@ * Side Public License, v 1. */ -import type { DataViewField } from '@kbn/data-views-plugin/common'; -import { FieldsGroup } from '@kbn/unified-field-list/src/types'; - export interface FieldListCustomization { id: 'field_list'; - additionalFieldGroups?: { - smartFields?: FieldsGroup['fields']; - }; + logsFieldsEnabled: boolean; // TODO / NOTE: Just temporary until Discover's data type contextual awareness lands. } diff --git a/src/plugins/discover/tsconfig.json b/src/plugins/discover/tsconfig.json index 8ea9e844d10a9..bf4c06f1c5dbe 100644 --- a/src/plugins/discover/tsconfig.json +++ b/src/plugins/discover/tsconfig.json @@ -84,9 +84,13 @@ "@kbn/shared-ux-markdown", "@kbn/data-view-utils", "@kbn/presentation-publishing", + "@kbn/elastic-agent-utils", + "@kbn/custom-icons", "@kbn/observability-ai-assistant-plugin", "@kbn/data-visualizer-plugin", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/custom-icons", + "@kbn/observability-ai-assistant-plugin" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_cell_renderer.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_cell_renderer.tsx deleted file mode 100644 index 765d910ede954..0000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_cell_renderer.tsx +++ /dev/null @@ -1,17 +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 { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { CONTENT_FIELD, RESOURCE_FIELD } from '../../common/constants'; -import { renderCell } from '../components/virtual_columns/cell_renderer'; - -export const createCustomCellRenderer = ({ data }: { data: DataPublicPluginStart }) => { - return { - [CONTENT_FIELD]: renderCell(CONTENT_FIELD, { data }), - [RESOURCE_FIELD]: renderCell(RESOURCE_FIELD, { data }), - }; -}; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_column.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_column.tsx deleted file mode 100644 index 1e5cba9693eb5..0000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_column.tsx +++ /dev/null @@ -1,14 +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 { CONTENT_FIELD, RESOURCE_FIELD } from '../../common/constants'; -import { renderColumn } from '../components/virtual_columns/column'; - -export const createCustomGridColumnsConfiguration = () => ({ - [CONTENT_FIELD]: renderColumn(CONTENT_FIELD), - [RESOURCE_FIELD]: renderColumn(RESOURCE_FIELD), -}); diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_control_column.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_control_column.tsx index 7fe8c0ae7aa7f..9bb71a3055a68 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_control_column.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_control_column.tsx @@ -15,6 +15,7 @@ import { import { EuiButtonIcon, EuiDataGridCellValueElementProps, EuiToolTip } from '@elastic/eui'; import type { DataTableRecord } from '@kbn/discover-utils/src/types'; import { useActor } from '@xstate/react'; +import { LogDocument } from '@kbn/discover-utils/src'; import { LogsExplorerControllerStateService } from '../state_machines/logs_explorer_controller'; import { degradedDocButtonLabelWhenNotPresent, @@ -24,7 +25,6 @@ import { } from '../components/common/translations'; import * as constants from '../../common/constants'; import { getStacktraceFields } from '../utils/get_stack_trace'; -import { LogDocument } from '../../common/document'; import { ActionsColumnTooltip } from '../components/virtual_columns/column_tooltips/actions_column_tooltip'; const ConnectedDegradedDocs = ({ diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_field_list.ts b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_field_list.ts deleted file mode 100644 index 93a414c39367e..0000000000000 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/custom_field_list.ts +++ /dev/null @@ -1,24 +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 { DataViewField } from '@kbn/data-views-plugin/common'; -import * as constants from '../../common/constants'; - -export const smartFields = [ - new DataViewField({ - name: constants.RESOURCE_FIELD, - type: 'smart_field', - searchable: false, - aggregatable: false, - }), - new DataViewField({ - name: constants.CONTENT_FIELD, - type: 'smart_field', - searchable: false, - aggregatable: false, - }), -]; diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx index 7fcc9d6a1cb9d..42a3838311a98 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx +++ b/x-pack/plugins/observability_solution/logs_explorer/public/customizations/logs_explorer_profile.tsx @@ -15,9 +15,6 @@ import type { LogsExplorerController } from '../controller'; import type { LogsExplorerStartDeps } from '../types'; import { useKibanaContextForPluginProvider } from '../utils/use_kibana'; import { createCustomSearchBar } from './custom_search_bar'; -import { createCustomCellRenderer } from './custom_cell_renderer'; -import { createCustomGridColumnsConfiguration } from './custom_column'; -import { smartFields } from './custom_field_list'; import { createCustomUnifiedHistogram } from './custom_unified_histogram'; const LazyCustomDataSourceFilters = dynamic(() => import('./custom_data_source_filters')); @@ -83,8 +80,7 @@ export const createLogsExplorerProfileCustomizations = customizations.set({ id: 'data_table', - customCellRenderer: createCustomCellRenderer({ data }), - customGridColumnsConfiguration: createCustomGridColumnsConfiguration(), + logsEnabled: true, customControlColumnsConfiguration: await import('./custom_control_column').then((module) => module.createCustomControlColumnsConfiguration(service) ), @@ -92,9 +88,7 @@ export const createLogsExplorerProfileCustomizations = customizations.set({ id: 'field_list', - additionalFieldGroups: { - smartFields, - }, + logsFieldsEnabled: true, }); // Fix bug where filtering on histogram does not work diff --git a/x-pack/plugins/observability_solution/logs_explorer/public/utils/get_stack_trace.ts b/x-pack/plugins/observability_solution/logs_explorer/public/utils/get_stack_trace.ts index 58eb44a7744c9..ac264af2732aa 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/public/utils/get_stack_trace.ts +++ b/x-pack/plugins/observability_solution/logs_explorer/public/utils/get_stack_trace.ts @@ -5,9 +5,8 @@ * 2.0. */ -import { LogDocument, StackTraceFields } from '../../common/document'; +import { getFieldFromDoc, LogDocument, StackTraceFields } from '@kbn/discover-utils/src'; import * as constants from '../../common/constants'; -import { getFieldFromDoc } from './get_field_from_flattened_doc'; export const getStacktraceFields = (doc: LogDocument): StackTraceFields => { const errorStackTrace = getFieldFromDoc(doc, constants.ERROR_STACK_TRACE); diff --git a/x-pack/plugins/observability_solution/logs_explorer/tsconfig.json b/x-pack/plugins/observability_solution/logs_explorer/tsconfig.json index cb04be5ccf518..b30605fd567fd 100644 --- a/x-pack/plugins/observability_solution/logs_explorer/tsconfig.json +++ b/x-pack/plugins/observability_solution/logs_explorer/tsconfig.json @@ -19,13 +19,11 @@ "@kbn/core-ui-settings-browser", "@kbn/core-ui-settings-common", "@kbn/core", - "@kbn/custom-icons", "@kbn/data-plugin", "@kbn/data-views-plugin", "@kbn/deeplinks-observability", "@kbn/discover-plugin", "@kbn/discover-utils", - "@kbn/elastic-agent-utils", "@kbn/embeddable-plugin", "@kbn/es-query", "@kbn/field-formats-plugin", diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 2cc9d3cb58a3f..ae41414e3671a 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -24100,7 +24100,6 @@ "xpack.logsExplorer.dataTable.header.popover.resource": "Ressource", "xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "Les champs fournissant des informations sur la source du document, comme :", "xpack.logsExplorer.flyoutDetail.title": "Détails du log", - "xpack.logsExplorer.grid.closePopover": "Fermer la fenêtre contextuelle", "xpack.logsExplorer.popoverAction.closePopover": "Fermer la fenêtre contextuelle", "xpack.logsExplorer.popoverAction.copyValue": "Copier la valeur", "xpack.logsExplorer.popoverAction.filterFor": "Filtrer sur", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 7cb20be1fd0e7..385bd1856c51b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24075,7 +24075,6 @@ "xpack.logsExplorer.dataTable.header.popover.resource": "リソース", "xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "次のようなドキュメントのソースに関する情報を提供するフィールド:", "xpack.logsExplorer.flyoutDetail.title": "ログの詳細", - "xpack.logsExplorer.grid.closePopover": "ポップオーバーを閉じる", "xpack.logsExplorer.popoverAction.closePopover": "ポップオーバーを閉じる", "xpack.logsExplorer.popoverAction.copyValue": "値をコピー", "xpack.logsExplorer.popoverAction.filterFor": "フィルター", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 396ca22ee5058..5064879a9a604 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24108,7 +24108,6 @@ "xpack.logsExplorer.dataTable.header.popover.resource": "资源", "xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "提供有关文档来源信息的字段,例如:", "xpack.logsExplorer.flyoutDetail.title": "日志详情", - "xpack.logsExplorer.grid.closePopover": "关闭弹出框", "xpack.logsExplorer.popoverAction.closePopover": "关闭弹出框", "xpack.logsExplorer.popoverAction.copyValue": "复制值", "xpack.logsExplorer.popoverAction.filterFor": "筛留", diff --git a/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts b/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts index d61ce9e734eba..1c64c85534d66 100644 --- a/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts +++ b/x-pack/test/functional/apps/observability_logs_explorer/columns_selection.ts @@ -130,9 +130,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(cellValue.includes('error.message')).to.be(false); expect(cellValue.includes('event.original')).to.be(false); - const cellAttribute = await cellElement.findByTestSubject( - 'logsExplorerCellDescriptionList' - ); + const cellAttribute = await cellElement.findByTestSubject('discoverCellDescriptionList'); expect(cellAttribute).not.to.be.empty(); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts index beb1adc25e70c..d32f3aff208b0 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_logs_explorer/columns_selection.ts @@ -132,9 +132,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(cellValue.includes('error.message')).to.be(false); expect(cellValue.includes('event.original')).to.be(false); - const cellAttribute = await cellElement.findByTestSubject( - 'logsExplorerCellDescriptionList' - ); + const cellAttribute = await cellElement.findByTestSubject('discoverCellDescriptionList'); expect(cellAttribute).not.to.be.empty(); }); }); From a4579a7e78a49a7515102fe71d90394108cc59cd Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Thu, 16 May 2024 15:56:20 +0100 Subject: [PATCH 02/71] [ObsUx] [Infra] Change container details view with asset details view (#180436) Part of https://github.com/elastic/kibana/issues/179844 ### In this PR - From Inventory, open asset details page view for Containers - Show overview tab with CPU and Memory KPIs and metric charts - Metadata tab with old fields, more metadata fields will be shown in follow-up PR - Added links to container metrics documentation, currently there are no docs for K8s metrics just for docker containers #### How to test - The feature is under a FF, on inventory page go to settings and enable `Container view` - In containers inventory, select a container and click on 'Docker container metrics' link (there's an [issue](https://github.com/elastic/kibana/issues/180806) to reword this links as K8s containers are also shown) - Container details page should be shown with overview and metadata tabs - On overview tab KPIs for CPU and Memory and Metrics section with CPU and Memory charts should be displayed image --- .../src/lib/infra/docker_container.ts | 39 ++++ .../src/lib/infra/index.ts | 12 +- .../infra/{container.ts => k8s_container.ts} | 16 +- .../src/lib/infra/pod.ts | 4 +- .../src/scenarios/infra_docker_containers.ts | 41 ++++ .../src/scenarios/infra_k8s_containers.ts | 41 ++++ .../settings/setting_ids/index.ts | 2 + .../server/collectors/management/schema.ts | 4 + .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 + .../asset_details_tabs.tsx | 175 +++++++++++------- .../public/common/visualizations/constants.ts | 1 + .../components/asset_details/charts/chart.tsx | 4 +- .../asset_details/charts/docker_charts.tsx | 87 +++++++++ .../asset_details/charts/host_charts.tsx | 12 +- .../components/asset_details/charts/index.tsx | 3 +- .../charts/kubernetes_charts.tsx | 148 ++++++++++----- .../components/asset_details/charts/types.ts | 22 +++ .../components/kpis/container_kpi_charts.tsx | 136 ++++++++++++++ .../components/kpis/host_kpi_charts.tsx | 2 +- .../components/asset_details/constants.ts | 4 +- .../use_container_metrics_charts.test.ts | 114 ++++++++++++ .../hooks/use_container_metrics_charts.ts | 163 ++++++++++++++++ ...est.ts => use_host_metrics_charts.test.ts} | 8 +- ...s_charts.ts => use_host_metrics_charts.ts} | 15 +- .../tabs/metrics/host_metrics.tsx | 6 +- .../tabs/overview/kpis/kpi_grid.tsx | 38 ++-- .../overview/metrics/container_metrics.tsx | 42 +++++ .../tabs/overview/metrics/host_metrics.tsx | 4 +- .../tabs/overview/metrics/metrics.tsx | 5 +- .../asset_details/tabs/overview/overview.tsx | 26 ++- .../tabs/overview/section_titles.tsx | 15 +- .../components/asset_details/translations.ts | 15 ++ .../public/components/asset_details/types.ts | 4 +- .../container_metrics_explanation_content.tsx | 54 ++++++ .../host_details_flyout/flyout_wrapper.tsx | 4 +- .../hosts/components/kpis/kpi_charts.tsx | 4 +- .../components/tabs/logs/logs_tab_content.tsx | 4 +- .../hosts/components/tabs/metrics/chart.tsx | 4 +- .../metrics/hosts/hooks/use_hosts_table.tsx | 4 +- .../waffle/asset_details_flyout.tsx | 4 +- .../waffle/waffle_inventory_switcher.tsx | 8 +- .../metric_detail/asset_detail_page.tsx | 4 +- .../pages/metrics/metric_detail/index.tsx | 7 +- .../settings/features_configuration_panel.tsx | 7 + .../source_configuration_settings.tsx | 2 + .../infra/public/utils/filters/build.ts | 2 +- .../utils/filters/create_alerts_es_query.ts | 4 +- .../inventory_models/container/index.ts | 2 +- .../container/metrics/charts/cpu.ts | 79 ++++++++ .../container/metrics/charts/index.ts | 16 ++ .../container/metrics/charts/memory.ts | 79 ++++++++ .../container/metrics/formulas/cpu.ts | 23 +++ .../container/metrics/formulas/index.ts | 18 ++ .../container/metrics/formulas/memory.ts | 22 +++ .../container/metrics/index.ts | 19 +- .../host/metrics/charts/cpu.ts | 11 +- .../host/metrics/charts/disk.ts | 15 +- .../host/metrics/charts/memory.ts | 5 +- .../host/metrics/charts/network.ts | 5 +- .../host/metrics/formulas/cpu.ts | 27 ++- .../host/metrics/formulas/disk.ts | 43 ++--- .../host/metrics/formulas/memory.ts | 9 +- .../shared/charts/constants.ts | 132 +++++++++++++ .../observability/common/index.ts | 1 + .../observability/common/ui_settings_keys.ts | 2 + .../observability/public/index.ts | 1 + .../observability/server/ui_settings.ts | 15 ++ .../translations/translations/fr-FR.json | 23 --- .../translations/translations/ja-JP.json | 23 --- .../translations/translations/zh-CN.json | 23 --- .../apis/asset_manager/tests/containers.ts | 99 +++++++++- x-pack/test/common/services/index.ts | 2 + .../infra_synthtrace_kibana_client.ts | 37 ++++ .../utils/synthtrace/infra_es_client.ts | 17 ++ .../test/functional/apps/infra/constants.ts | 6 +- x-pack/test/functional/apps/infra/helpers.ts | 25 ++- .../test/functional/apps/infra/home_page.ts | 45 ++++- .../functional/apps/infra/node_details.ts | 95 +++++++++- .../functional/page_objects/asset_details.ts | 13 ++ .../page_objects/infra_home_page.ts | 4 +- 81 files changed, 1889 insertions(+), 374 deletions(-) create mode 100644 packages/kbn-apm-synthtrace-client/src/lib/infra/docker_container.ts rename packages/kbn-apm-synthtrace-client/src/lib/infra/{container.ts => k8s_container.ts} (62%) create mode 100644 packages/kbn-apm-synthtrace/src/scenarios/infra_docker_containers.ts create mode 100644 packages/kbn-apm-synthtrace/src/scenarios/infra_k8s_containers.ts create mode 100644 x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/docker_charts.tsx create mode 100644 x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/types.ts create mode 100644 x-pack/plugins/observability_solution/infra/public/components/asset_details/components/kpis/container_kpi_charts.tsx create mode 100644 x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_container_metrics_charts.test.ts create mode 100644 x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_container_metrics_charts.ts rename x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/{use_metrics_charts.test.ts => use_host_metrics_charts.test.ts} (97%) rename x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/{use_metrics_charts.ts => use_host_metrics_charts.ts} (91%) create mode 100644 x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/container_metrics.tsx create mode 100644 x-pack/plugins/observability_solution/infra/public/components/lens/metric_explanation/container_metrics_explanation_content.tsx create mode 100644 x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/cpu.ts create mode 100644 x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/index.ts create mode 100644 x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/memory.ts create mode 100644 x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/cpu.ts create mode 100644 x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/index.ts create mode 100644 x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/memory.ts create mode 100644 x-pack/test/common/services/infra_synthtrace_kibana_client.ts create mode 100644 x-pack/test/common/utils/synthtrace/infra_es_client.ts diff --git a/packages/kbn-apm-synthtrace-client/src/lib/infra/docker_container.ts b/packages/kbn-apm-synthtrace-client/src/lib/infra/docker_container.ts new file mode 100644 index 0000000000000..03f72ee61bc06 --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/infra/docker_container.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/* eslint-disable max-classes-per-file */ +import { Entity, Fields } from '../entity'; +import { Serializable } from '../serializable'; + +interface DockerContainerDocument extends Fields { + 'container.id': string; + 'metricset.name'?: string; +} + +export class DockerContainer extends Entity { + metrics() { + return new DockerContainerMetrics({ + ...this.fields, + 'docker.cpu.total.pct': 25, + 'docker.memory.usage.pct': 20, + }); + } +} + +export interface DockerContainerMetricsDocument extends DockerContainerDocument { + 'docker.cpu.total.pct': number; + 'docker.memory.usage.pct': number; +} + +class DockerContainerMetrics extends Serializable {} + +export function dockerContainer(id: string): DockerContainer { + return new DockerContainer({ + 'container.id': id, + }); +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts b/packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts index 961225670e27b..fb81b595c7e2f 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts @@ -6,14 +6,20 @@ * Side Public License, v 1. */ -import { container, ContainerMetricsDocument } from './container'; +import { dockerContainer, DockerContainerMetricsDocument } from './docker_container'; import { host, HostMetricsDocument } from './host'; +import { k8sContainer, K8sContainerMetricsDocument } from './k8s_container'; import { pod, PodMetricsDocument } from './pod'; -export type InfraDocument = HostMetricsDocument | PodMetricsDocument | ContainerMetricsDocument; +export type InfraDocument = + | HostMetricsDocument + | PodMetricsDocument + | DockerContainerMetricsDocument + | K8sContainerMetricsDocument; export const infra = { host, pod, - container, + dockerContainer, + k8sContainer, }; diff --git a/packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts b/packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts similarity index 62% rename from packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts rename to packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts index d9bef38c2fdb5..00d4f0a998960 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts @@ -10,30 +10,32 @@ import { Entity, Fields } from '../entity'; import { Serializable } from '../serializable'; -interface ContainerDocument extends Fields { +interface K8sContainerDocument extends Fields { 'container.id': string; 'kubernetes.pod.uid': string; 'kubernetes.node.name': string; 'metricset.name'?: string; } -export class Container extends Entity { +export class K8sContainer extends Entity { metrics() { - return new ContainerMetrics({ + return new K8sContainerMetrics({ ...this.fields, 'kubernetes.container.cpu.usage.limit.pct': 46, + 'kubernetes.container.memory.usage.limit.pct': 30, }); } } -export interface ContainerMetricsDocument extends ContainerDocument { +export interface K8sContainerMetricsDocument extends K8sContainerDocument { 'kubernetes.container.cpu.usage.limit.pct': number; + 'kubernetes.container.memory.usage.limit.pct': number; } -class ContainerMetrics extends Serializable {} +class K8sContainerMetrics extends Serializable {} -export function container(id: string, uid: string, nodeName: string) { - return new Container({ +export function k8sContainer(id: string, uid: string, nodeName: string): K8sContainer { + return new K8sContainer({ 'container.id': id, 'kubernetes.pod.uid': uid, 'kubernetes.node.name': nodeName, diff --git a/packages/kbn-apm-synthtrace-client/src/lib/infra/pod.ts b/packages/kbn-apm-synthtrace-client/src/lib/infra/pod.ts index 9f5c999184176..35ebe94ba6ee1 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/infra/pod.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/infra/pod.ts @@ -9,7 +9,7 @@ /* eslint-disable max-classes-per-file */ import { Entity, Fields } from '../entity'; import { Serializable } from '../serializable'; -import { container } from './container'; +import { k8sContainer } from './k8s_container'; interface PodDocument extends Fields { 'kubernetes.pod.uid': string; @@ -26,7 +26,7 @@ export class Pod extends Entity { } container(id: string) { - return container(id, this.fields['kubernetes.pod.uid'], this.fields['kubernetes.node.name']); + return k8sContainer(id, this.fields['kubernetes.pod.uid'], this.fields['kubernetes.node.name']); } } diff --git a/packages/kbn-apm-synthtrace/src/scenarios/infra_docker_containers.ts b/packages/kbn-apm-synthtrace/src/scenarios/infra_docker_containers.ts new file mode 100644 index 0000000000000..c020d2de83db9 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/scenarios/infra_docker_containers.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { InfraDocument, infra } from '@kbn/apm-synthtrace-client'; + +import { Scenario } from '../cli/scenario'; +import { withClient } from '../lib/utils/with_client'; + +const scenario: Scenario = async (runOptions) => { + return { + generate: ({ range, clients: { infraEsClient } }) => { + const { numContainers = 5 } = runOptions.scenarioOpts || {}; + const { logger } = runOptions; + + const CONTAINERS = Array(numContainers) + .fill(0) + .map((_, idx) => infra.dockerContainer(`container-${idx}`)); + + const containers = range + .interval('30s') + .rate(1) + .generator((timestamp) => + CONTAINERS.flatMap((container) => [container.metrics().timestamp(timestamp)]) + ); + + return [ + withClient( + infraEsClient, + logger.perf('generating_infra_docker_containers', () => containers) + ), + ]; + }, + }; +}; + +export default scenario; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/infra_k8s_containers.ts b/packages/kbn-apm-synthtrace/src/scenarios/infra_k8s_containers.ts new file mode 100644 index 0000000000000..39d2b36b1a03f --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/scenarios/infra_k8s_containers.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { InfraDocument, infra } from '@kbn/apm-synthtrace-client'; + +import { Scenario } from '../cli/scenario'; +import { withClient } from '../lib/utils/with_client'; + +const scenario: Scenario = async (runOptions) => { + return { + generate: ({ range, clients: { infraEsClient } }) => { + const { numContainers = 5 } = runOptions.scenarioOpts || {}; + const { logger } = runOptions; + + const CONTAINERS = Array(numContainers) + .fill(0) + .map((_, idx) => infra.k8sContainer(`container-${idx}`, `pod-${idx}`, `node-${idx}`)); + + const containers = range + .interval('30s') + .rate(1) + .generator((timestamp) => + CONTAINERS.flatMap((container) => [container.metrics().timestamp(timestamp)]) + ); + + return [ + withClient( + infraEsClient, + logger.perf('generating_infra_containers', () => containers) + ), + ]; + }, + }; +}; + +export default scenario; diff --git a/packages/kbn-management/settings/setting_ids/index.ts b/packages/kbn-management/settings/setting_ids/index.ts index 93646978aa1a6..4fd4015baeb2c 100644 --- a/packages/kbn-management/settings/setting_ids/index.ts +++ b/packages/kbn-management/settings/setting_ids/index.ts @@ -123,6 +123,8 @@ export const OBSERVABILITY_ENABLE_COMPARISON_BY_DEFAULT_ID = 'observability:enableComparisonByDefault'; export const OBSERVABILITY_ENABLE_INFRASTRUCTURE_HOSTS_VIEW_ID = 'observability:enableInfrastructureHostsView'; +export const OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID = + 'observability:enableContainerAssetView'; export const OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID = 'observability:enableInfrastructureAssetCustomDashboards'; export const OBSERVABILITY_ENABLE_INSPECT_ES_QUERIES_ID = 'observability:enableInspectEsQueries'; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index fc4786dd5ca3f..9c4e025dd92b5 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -627,6 +627,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, + 'observability:enableInfrastructureContainerAssetView': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'observability:enableInfrastructureProfilingIntegration': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index bc1c315edae29..e3b84245ce3f0 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -46,6 +46,7 @@ export interface UsageStats { 'observability:apmAWSLambdaPriceFactor': string; 'observability:apmAWSLambdaRequestCostPerMillion': number; 'observability:enableInfrastructureHostsView': boolean; + 'observability:enableInfrastructureContainerAssetView': boolean; 'observability:enableInfrastructureProfilingIntegration': boolean; 'observability:enableInfrastructureAssetCustomDashboards': boolean; 'observability:apmAgentExplorerView': boolean; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 717e5ebabbe1b..673361881b2ff 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -10404,6 +10404,12 @@ "description": "Non-default value of setting." } }, + "observability:enableInfrastructureContainerAssetView":{ + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "observability:enableInfrastructureProfilingIntegration": { "type": "boolean", "_meta": { diff --git a/x-pack/plugins/observability_solution/infra/public/common/asset_details_config/asset_details_tabs.tsx b/x-pack/plugins/observability_solution/infra/public/common/asset_details_config/asset_details_tabs.tsx index d43ab65832271..1383e1c49915e 100644 --- a/x-pack/plugins/observability_solution/infra/public/common/asset_details_config/asset_details_tabs.tsx +++ b/x-pack/plugins/observability_solution/infra/public/common/asset_details_config/asset_details_tabs.tsx @@ -10,76 +10,107 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { ContentTabIds, type Tab } from '../../components/asset_details/types'; -export const commonFlyoutTabs: Tab[] = [ - { - id: ContentTabIds.OVERVIEW, - name: i18n.translate('xpack.infra.assetDetails.tabs.overview', { - defaultMessage: 'Overview', - }), - }, - { - id: ContentTabIds.METADATA, - name: i18n.translate('xpack.infra.assetDetails.tabs.metadata', { - defaultMessage: 'Metadata', - }), - }, - { - id: ContentTabIds.METRICS, - name: i18n.translate('xpack.infra.assetDetails.tabs.metrics', { - defaultMessage: 'Metrics', - }), - }, - { - id: ContentTabIds.PROCESSES, - name: i18n.translate('xpack.infra.assetDetails.tabs.processes', { - defaultMessage: 'Processes', - }), - }, - { - id: ContentTabIds.PROFILING, - name: i18n.translate('xpack.infra.assetDetails.tabs.profiling', { - defaultMessage: 'Universal Profiling', - }), - }, - { - id: ContentTabIds.LOGS, - name: i18n.translate('xpack.infra.assetDetails.tabs.logs', { - defaultMessage: 'Logs', - }), - }, - { - id: ContentTabIds.ANOMALIES, - name: i18n.translate('xpack.infra.assetDetails.tabs.anomalies', { - defaultMessage: 'Anomalies', - }), - }, - { - id: ContentTabIds.OSQUERY, - name: i18n.translate('xpack.infra.assetDetails.tabs.osquery', { - defaultMessage: 'Osquery', - }), - }, - { - id: ContentTabIds.DASHBOARDS, - name: i18n.translate('xpack.infra.infra.nodeDetails.tabs.dashboards', { - defaultMessage: 'Dashboards', - }), - append: ( - - ), - }, +const overviewTab: Tab = { + id: ContentTabIds.OVERVIEW, + name: i18n.translate('xpack.infra.nodeDetails.tabs.overview.title', { + defaultMessage: 'Overview', + }), +}; + +const metadataTab: Tab = { + id: ContentTabIds.METADATA, + name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', { + defaultMessage: 'Metadata', + }), +}; + +const metricsTab: Tab = { + id: ContentTabIds.METRICS, + name: i18n.translate('xpack.infra.nodeDetails.tabs.metrics.title', { + defaultMessage: 'Metrics', + }), +}; + +const processesTab: Tab = { + id: ContentTabIds.PROCESSES, + name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.processes', { + defaultMessage: 'Processes', + }), +}; + +const profilingTab: Tab = { + id: ContentTabIds.PROFILING, + name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.profiling', { + defaultMessage: 'Universal Profiling', + }), +}; + +const logsTab: Tab = { + id: ContentTabIds.LOGS, + name: i18n.translate('xpack.infra.nodeDetails.tabs.logs.title', { + defaultMessage: 'Logs', + }), +}; + +const anomaliesTab: Tab = { + id: ContentTabIds.ANOMALIES, + name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', { + defaultMessage: 'Anomalies', + }), +}; + +const osqueryTab: Tab = { + id: ContentTabIds.OSQUERY, + name: i18n.translate('xpack.infra.nodeDetails.tabs.osquery', { + defaultMessage: 'Osquery', + }), +}; + +const dashboardsTab: Tab = { + id: ContentTabIds.DASHBOARDS, + name: i18n.translate('xpack.infra.infra.nodeDetails.tabs.dashboards', { + defaultMessage: 'Dashboards', + }), + append: ( + + ), +}; + +export const hostDetailsTabs: Tab[] = [ + overviewTab, + metadataTab, + metricsTab, + processesTab, + profilingTab, + logsTab, + anomaliesTab, + osqueryTab, + dashboardsTab, ]; +// Profiling and Logs tab would be added in next iteration +export const containerDetailsTabs: Tab[] = [overviewTab, metadataTab]; + +export const getAssetDetailsTabs = (type: string): Tab[] => { + switch (type) { + case 'host': + return hostDetailsTabs; + case 'container': + return containerDetailsTabs; + default: + return []; + } +}; diff --git a/x-pack/plugins/observability_solution/infra/public/common/visualizations/constants.ts b/x-pack/plugins/observability_solution/infra/public/common/visualizations/constants.ts index 76b2eb7c3d70f..959ff1145f7d4 100644 --- a/x-pack/plugins/observability_solution/infra/public/common/visualizations/constants.ts +++ b/x-pack/plugins/observability_solution/infra/public/common/visualizations/constants.ts @@ -7,6 +7,7 @@ export const HOST_METRICS_DOC_HREF = 'https://ela.st/docs-infra-host-metrics'; export const HOST_METRICS_DOTTED_LINES_DOC_HREF = 'https://ela.st/docs-infra-why-dotted'; +export const CONTAINER_METRICS_DOC_HREF = 'https://ela.st/docs-infra-docker-container-metrics'; export const KPI_CHART_HEIGHT = 150; export const METRIC_CHART_HEIGHT = 300; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx index a1edbfa327a4b..f1ccf0ef31b2b 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx @@ -10,7 +10,7 @@ import type { LensConfig, LensDataviewDataset } from '@kbn/lens-embeddable-utils import type { TimeRange } from '@kbn/es-query'; import { useDataView } from '../../../hooks/use_data_view'; import { METRIC_CHART_HEIGHT } from '../../../common/visualizations/constants'; -import { buildCombinedHostsFilter } from '../../../utils/filters/build'; +import { buildCombinedAssetFilter } from '../../../utils/filters/build'; import { type BrushEndArgs, LensChart, type OnFilterEvent, LensChartProps } from '../../lens'; import { useDatePickerContext } from '../hooks/use_date_picker'; import { extractRangeFromChartFilterEvent } from './chart_utils'; @@ -31,7 +31,7 @@ export const Chart = ({ id, queryField, overrides, dateRange, assetId, ...props const filters = useMemo(() => { return [ - buildCombinedHostsFilter({ + buildCombinedAssetFilter({ field: queryField, values: [assetId], dataView, diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/docker_charts.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/docker_charts.tsx new file mode 100644 index 0000000000000..795a66b5a2e80 --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/docker_charts.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css, cx } from '@emotion/css'; +import { EuiText, EuiLink } from '@elastic/eui'; +import { useDockerContainerPageViewMetricsCharts } from '../hooks/use_container_metrics_charts'; +import { Section } from '../components/section'; +import { ChartsGrid } from '../charts_grid/charts_grid'; +import { Chart } from './chart'; +import { TitleWithTooltip } from '../components/section_title'; +import { CONTAINER_METRIC_GROUP_TITLES } from '../translations'; +import { CONTAINER_METRICS_DOC_HREF } from '../../../common/visualizations/constants'; +import { MetricsChartsFields, ContainerMetricTypes } from './types'; + +interface Props extends MetricsChartsFields { + metric: ContainerMetricTypes; +} + +const FRAGMENT_BASE = 'key-metrics'; + +export const DockerCharts = React.forwardRef( + ({ assetId, dataView, dateRange, metric }, ref) => { + const { charts } = useDockerContainerPageViewMetricsCharts({ + metric, + metricsDataViewId: dataView?.id, + }); + return ( +
+ + + + ), + }} + /> + + } + /> + } + data-test-subj={`infraAssetDetailsDockerChartsSection${metric}`} + id="dockerContainerCharts" + ref={ref} + > + + {charts.map((chart) => ( + + ))} + +
+ ); + } +); diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/host_charts.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/host_charts.tsx index 79d225cd02882..d8d60deb81efb 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/host_charts.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/host_charts.tsx @@ -5,8 +5,6 @@ * 2.0. */ import React from 'react'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import type { TimeRange } from '@kbn/es-query'; import { EuiText, EuiLink, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common'; @@ -16,16 +14,12 @@ import { HOST_METRIC_GROUP_TITLES } from '../translations'; import { Section } from '../components/section'; import { ChartsGrid } from '../charts_grid/charts_grid'; import { Chart } from './chart'; -import { type HostMetricTypes, useHostCharts } from '../hooks/use_metrics_charts'; +import { useHostCharts } from '../hooks/use_host_metrics_charts'; import { TitleWithTooltip } from '../components/section_title'; +import { MetricsChartsFields, HostMetricTypes } from './types'; -interface Props { - assetId: string; - dateRange: TimeRange; - dataView?: DataView; - overview?: boolean; +interface Props extends MetricsChartsFields { metric: Exclude; - onShowAll?: (metric: string) => void; } const FRAGMENT_BASE = 'key-metrics'; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/index.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/index.tsx index 180a5558e29bd..c242cccdf952a 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/index.tsx @@ -6,4 +6,5 @@ */ export { HostCharts } from './host_charts'; -export { KubernetesCharts } from './kubernetes_charts'; +export { KubernetesNodeCharts, KubernetesContainerCharts } from './kubernetes_charts'; +export { DockerCharts } from './docker_charts'; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/kubernetes_charts.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/kubernetes_charts.tsx index 8791b351bf7e7..b386b1e9d8cbc 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/kubernetes_charts.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/kubernetes_charts.tsx @@ -5,66 +5,124 @@ * 2.0. */ import React from 'react'; -import { EuiButtonEmpty } from '@elastic/eui'; +import { EuiButtonEmpty, EuiText, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { TimeRange } from '@kbn/es-query'; -import type { DataView } from '@kbn/data-views-plugin/public'; +import { css, cx } from '@emotion/css'; import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common'; -import { useKubernetesCharts } from '../hooks/use_metrics_charts'; +import { useKubernetesCharts } from '../hooks/use_host_metrics_charts'; import { Section } from '../components/section'; -import { SectionTitle } from '../components/section_title'; -import { HOST_METRIC_GROUP_TITLES } from '../translations'; +import { SectionTitle, TitleWithTooltip } from '../components/section_title'; +import { CONTAINER_METRIC_GROUP_TITLES, HOST_METRIC_GROUP_TITLES } from '../translations'; import { INTEGRATIONS } from '../constants'; import { ChartsGrid } from '../charts_grid/charts_grid'; import { Chart } from './chart'; import { useIntegrationCheck } from '../hooks/use_integration_check'; +import { useK8sContainerPageViewMetricsCharts } from '../hooks/use_container_metrics_charts'; +import { CONTAINER_METRICS_DOC_HREF } from '../../../common/visualizations/constants'; +import { ContainerMetricTypes, MetricsChartsFields } from './types'; -interface Props { - assetId: string; - dateRange: TimeRange; - dataView?: DataView; - overview?: boolean; - onShowAll?: (metric: string) => void; -} +const FRAGMENT_BASE = 'key-metrics'; -export const KubernetesCharts = React.forwardRef< - HTMLDivElement, - Props & { onShowAll?: (metric: string) => void } ->(({ assetId, dataView, dateRange, onShowAll, overview }, ref) => { - const { charts } = useKubernetesCharts({ - dataViewId: dataView?.id, - options: { overview }, - }); +export const KubernetesNodeCharts = React.forwardRef( + ({ assetId, dataView, dateRange, onShowAll, overview }, ref) => { + const { charts } = useKubernetesCharts({ + dataViewId: dataView?.id, + options: { overview }, + }); + + const hasIntegration = useIntegrationCheck({ dependsOn: INTEGRATIONS.kubernetesNode }); - const hasIntegration = useIntegrationCheck({ dependsOn: INTEGRATIONS.kubernetes }); + if (!hasIntegration) { + return null; + } - if (!hasIntegration) { - return null; + return ( +
} + data-test-subj="infraAssetDetailsKubernetesChartsSection" + id="kubernetes" + ref={ref} + extraAction={ + onShowAll ? ( + onShowAll('kubernetes')} + size="xs" + flush="both" + iconSide="right" + iconType="sortRight" + > + + + ) : null + } + > + + {charts.map((chart) => ( + + ))} + +
+ ); } +); +export const KubernetesContainerCharts = React.forwardRef< + HTMLDivElement, + MetricsChartsFields & { metric: ContainerMetricTypes } +>(({ assetId, dataView, dateRange, metric }, ref) => { + const { charts } = useK8sContainerPageViewMetricsCharts({ + metric, + metricsDataViewId: dataView?.id, + }); return (
} - data-test-subj="infraAssetDetailsKubernetesChartsSection" - id="kubernetes" - ref={ref} - extraAction={ - onShowAll ? ( - onShowAll('kubernetes')} - size="xs" - flush="both" - iconSide="right" - iconType="sortRight" - > - - - ) : null + title={ + + + + + ), + }} + /> + + } + /> } + data-test-subj="infraAssetDetailsK8ContainerChartsSection" + id="k8sContainerCharts" + ref={ref} > {charts.map((chart) => ( @@ -73,7 +131,7 @@ export const KubernetesCharts = React.forwardRef< {...chart} assetId={assetId} dateRange={dateRange} - queryField={findInventoryFields('host').id} + queryField={findInventoryFields('container').id} /> ))} diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/types.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/types.ts new file mode 100644 index 0000000000000..a01a5c7f7ced2 --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DataView } from '@kbn/data-views-plugin/public'; +import type { TimeRange } from '@kbn/es-query'; + +export type HostMetricTypes = 'cpu' | 'memory' | 'network' | 'disk' | 'log' | 'kpi'; +export type KubernetesContainerMetrics = 'cpu' | 'memory'; +export type DockerContainerMetrics = 'cpu' | 'memory' | 'network' | 'disk'; +export type ContainerMetricTypes = KubernetesContainerMetrics | DockerContainerMetrics; + +export interface MetricsChartsFields { + assetId: string; + dateRange: TimeRange; + dataView?: DataView; + overview?: boolean; + onShowAll?: (metric: string) => void; +} diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/kpis/container_kpi_charts.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/kpis/container_kpi_charts.tsx new file mode 100644 index 0000000000000..8e4a67526f72c --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/kpis/container_kpi_charts.tsx @@ -0,0 +1,136 @@ +/* + * 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 { EuiFlexItem, useEuiTheme } from '@elastic/eui'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { Kpi } from './kpi'; +import { + useK8sContainerKpiCharts, + useDockerContainerKpiCharts, +} from '../../hooks/use_container_metrics_charts'; +import { useIntegrationCheck } from '../../hooks/use_integration_check'; +import { INTEGRATIONS } from '../../constants'; + +export interface ContainerKpiChartsProps { + dataView?: DataView; + dateRange: TimeRange; + query?: Query; + filters?: Filter[]; + searchSessionId?: string; + options?: { + getSubtitle?: (formulaValue: string) => string; + }; + loading?: boolean; +} + +export const ContainerKpiCharts = ({ + dateRange, + dataView, + filters, + options, + query, + searchSessionId, + loading = false, +}: ContainerKpiChartsProps) => { + const isK8Container = useIntegrationCheck({ dependsOn: INTEGRATIONS.kubernetesContainer }); + + return isK8Container ? ( + + ) : ( + + ); +}; + +const DockerKpiCharts = ({ + dateRange, + dataView, + filters, + options, + query, + searchSessionId, + loading = false, +}: ContainerKpiChartsProps) => { + const { euiTheme } = useEuiTheme(); + const charts = useDockerContainerKpiCharts({ + dataViewId: dataView?.id, + options: { + getSubtitle: options?.getSubtitle, + seriesColor: euiTheme.colors.lightestShade, + }, + }); + + return ( + <> + {charts.map((chartProps, index) => ( + + + + ))} + + ); +}; + +const KubernetesKpiCharts = ({ + dateRange, + dataView, + filters, + options, + query, + searchSessionId, + loading = false, +}: ContainerKpiChartsProps) => { + const { euiTheme } = useEuiTheme(); + const charts = useK8sContainerKpiCharts({ + dataViewId: dataView?.id, + options: { + getSubtitle: options?.getSubtitle, + seriesColor: euiTheme.colors.lightestShade, + }, + }); + + return ( + <> + {charts.map((chartProps, index) => ( + + + + ))} + + ); +}; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/kpis/host_kpi_charts.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/kpis/host_kpi_charts.tsx index e73f5a6cd0656..fa11d67df90e9 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/kpis/host_kpi_charts.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/kpis/host_kpi_charts.tsx @@ -10,7 +10,7 @@ import { EuiFlexItem, useEuiTheme } from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { Filter, Query, TimeRange } from '@kbn/es-query'; import { Kpi } from './kpi'; -import { useHostKpiCharts } from '../../hooks/use_metrics_charts'; +import { useHostKpiCharts } from '../../hooks/use_host_metrics_charts'; export interface HostKpiChartsProps { dataView?: DataView; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/constants.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/constants.ts index 4c66628324c50..81a82ecb30875 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/constants.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/constants.ts @@ -15,5 +15,7 @@ export const APM_HOST_FILTER_FIELD = 'host.hostname'; export const ASSET_DETAILS_URL_STATE_KEY = 'assetDetails'; export const INTEGRATIONS = { - [INTEGRATION_NAME.kubernetes]: 'kubernetes.node', + [INTEGRATION_NAME.kubernetesNode]: 'kubernetes.node', + [INTEGRATION_NAME.kubernetesContainer]: 'kubernetes.container', + [INTEGRATION_NAME.docker]: 'docker', }; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_container_metrics_charts.test.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_container_metrics_charts.test.ts new file mode 100644 index 0000000000000..f35c7ca63b552 --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_container_metrics_charts.test.ts @@ -0,0 +1,114 @@ +/* + * 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 { renderHook } from '@testing-library/react-hooks'; +import { ContainerMetricTypes } from '../charts/types'; +import { + useK8sContainerPageViewMetricsCharts, + useDockerContainerPageViewMetricsCharts, + useDockerContainerKpiCharts, + useK8sContainerKpiCharts, +} from './use_container_metrics_charts'; + +const metricsDataViewId = 'metricsDataViewId'; +const getContainerChartsExpectedOrder = (metric: ContainerMetricTypes): string[] => { + switch (metric) { + case 'cpu': + return ['cpuUsage']; + case 'memory': + return ['memoryUsage']; + default: + return []; + } +}; + +const getK8sContainerChartsExpectedOrder = (metric: ContainerMetricTypes): string[] => { + switch (metric) { + case 'cpu': + return ['k8sCpuUsage']; + case 'memory': + return ['k8sMemoryUsage']; + default: + return []; + } +}; + +describe('useDockerContainerCharts', () => { + describe.each<[ContainerMetricTypes]>([['cpu'], ['memory']])('%s', (item) => { + test.each<[ContainerMetricTypes]>([[item]])( + 'should return an array of charts with correct order for metric "%s"', + async (metric) => { + const expectedOrder = getContainerChartsExpectedOrder(metric); + + const { result, waitForNextUpdate } = renderHook(() => + useDockerContainerPageViewMetricsCharts({ metricsDataViewId, metric }) + ); + await waitForNextUpdate(); + + const { charts } = result.current; + + expect(charts).toHaveLength(expectedOrder.length); + + charts.forEach((chart, index) => { + expect(chart).toHaveProperty('id', expectedOrder[index]); + }); + } + ); + }); +}); + +describe('useDockerKPIMetricsCharts', () => { + it('should return an array of charts with correct order', async () => { + const expectedOrder = ['cpuUsage', 'memoryUsage']; + const { result, waitForNextUpdate } = renderHook(() => + useDockerContainerKpiCharts({ dataViewId: metricsDataViewId }) + ); + await waitForNextUpdate(); + expect(result.current).toHaveLength(expectedOrder.length); + result.current.forEach((chart, index) => { + expect(chart).toHaveProperty('id', expectedOrder[index]); + }); + }); +}); + +describe('useK8sContainerCharts', () => { + describe.each<[ContainerMetricTypes]>([['cpu'], ['memory']])('%s', (item) => { + test.each<[ContainerMetricTypes]>([[item]])( + 'should return an array of charts with correct order for metric "%s"', + async (metric) => { + const expectedOrder = getK8sContainerChartsExpectedOrder(metric); + + const { result, waitForNextUpdate } = renderHook(() => + useK8sContainerPageViewMetricsCharts({ metricsDataViewId, metric }) + ); + await waitForNextUpdate(); + + const { charts } = result.current; + + expect(charts).toHaveLength(expectedOrder.length); + + charts.forEach((chart, index) => { + expect(chart).toHaveProperty('id', expectedOrder[index]); + }); + } + ); + }); +}); + +describe('useK8sContainerKPIMetricsCharts', () => { + it('should return an array of charts with correct order', async () => { + const expectedOrder = ['k8sCpuUsage', 'k8sMemoryUsage']; + const { result, waitForNextUpdate } = renderHook(() => + useK8sContainerKpiCharts({ dataViewId: metricsDataViewId }) + ); + await waitForNextUpdate(); + expect(result.current).toHaveLength(expectedOrder.length); + result.current.forEach((chart, index) => { + expect(chart).toHaveProperty('id', expectedOrder[index]); + }); + }); +}); diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_container_metrics_charts.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_container_metrics_charts.ts new file mode 100644 index 0000000000000..08710378643d5 --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_container_metrics_charts.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 { i18n } from '@kbn/i18n'; +import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; +import useAsync from 'react-use/lib/useAsync'; +import { ContainerMetricTypes } from '../charts/types'; + +const getSubtitleFromFormula = (value: string) => + value.startsWith('max') + ? i18n.translate('xpack.infra.containerViewPage.kpi.subtitle.max', { defaultMessage: 'Max' }) + : i18n.translate('xpack.infra.assetDetails.kpi.subtitle.average', { + defaultMessage: 'Average', + }); + +export const useDockerContainerPageViewMetricsCharts = ({ + metric, + metricsDataViewId, +}: { + metric: ContainerMetricTypes; + metricsDataViewId?: string; +}) => { + const { value: charts = [], error } = useAsync(async () => { + const containerCharts = await getDockerContainerCharts(metric); + + return containerCharts.map((chart) => { + return { + ...chart, + ...(metricsDataViewId && { + dataset: { + index: metricsDataViewId, + }, + }), + }; + }); + }, [metricsDataViewId]); + + return { charts, error }; +}; + +const getDockerContainerCharts = async (metric: ContainerMetricTypes) => { + const model = findInventoryModel('container'); + const { cpu, memory } = await model.metrics.getCharts(); + + switch (metric) { + case 'cpu': + return [cpu.xy.dockerContainerCpuUsage]; + case 'memory': + return [memory.xy.dockerContainerMemoryUsage]; + default: + return []; + } +}; + +export const useK8sContainerPageViewMetricsCharts = ({ + metric, + metricsDataViewId, +}: { + metric: ContainerMetricTypes; + metricsDataViewId?: string; +}) => { + const { value: charts = [], error } = useAsync(async () => { + const containerK8sCharts = await getK8sContainerCharts(metric); + + return containerK8sCharts.map((chart) => { + return { + ...chart, + ...(metricsDataViewId && { + dataset: { + index: metricsDataViewId, + }, + }), + }; + }); + }, [metricsDataViewId]); + + return { charts, error }; +}; + +const getK8sContainerCharts = async (metric: ContainerMetricTypes) => { + const model = findInventoryModel('container'); + const { cpu, memory } = await model.metrics.getCharts(); + + switch (metric) { + case 'cpu': + return [cpu.xy.k8sContainerCpuUsage]; + case 'memory': + return [memory.xy.k8sContainerMemoryUsage]; + default: + return []; + } +}; + +export const useDockerContainerKpiCharts = ({ + dataViewId, + options, +}: { + dataViewId?: string; + options?: { seriesColor: string; getSubtitle?: (formulaValue: string) => string }; +}) => { + const { value: charts = [] } = useAsync(async () => { + const model = findInventoryModel('container'); + const { cpu, memory } = await model.metrics.getCharts(); + + return [cpu.metric.dockerContainerCpuUsage, memory.metric.dockerContainerMemoryUsage].map( + (chart) => ({ + ...chart, + seriesColor: options?.seriesColor, + decimals: 1, + subtitle: getSubtitle(options, chart), + ...(dataViewId && { + dataset: { + index: dataViewId, + }, + }), + }) + ); + }, [dataViewId, options?.seriesColor, options?.getSubtitle]); + + return charts; +}; + +export const useK8sContainerKpiCharts = ({ + dataViewId, + options, +}: { + dataViewId?: string; + options?: { seriesColor: string; getSubtitle?: (formulaValue: string) => string }; +}) => { + const { value: charts = [] } = useAsync(async () => { + const model = findInventoryModel('container'); + const { cpu, memory } = await model.metrics.getCharts(); + + return [cpu.metric.k8sContainerCpuUsage, memory.metric.k8sContainerMemoryUsage].map( + (chart) => ({ + ...chart, + seriesColor: options?.seriesColor, + decimals: 1, + subtitle: getSubtitle(options, chart), + ...(dataViewId && { + dataset: { + index: dataViewId, + }, + }), + }) + ); + }, [dataViewId, options?.seriesColor, options?.getSubtitle]); + + return charts; +}; + +function getSubtitle( + options: { getSubtitle?: ((formulaValue: string) => string) | undefined } | undefined, + chart: { value: string } +) { + return options?.getSubtitle + ? options?.getSubtitle(chart.value) + : getSubtitleFromFormula(chart.value); +} diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.test.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_host_metrics_charts.test.ts similarity index 97% rename from x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.test.ts rename to x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_host_metrics_charts.test.ts index 1e0573e187074..c4e3815f5e0f8 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_host_metrics_charts.test.ts @@ -6,12 +6,8 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { - useHostKpiCharts, - useHostCharts, - useKubernetesCharts, - type HostMetricTypes, -} from './use_metrics_charts'; +import { HostMetricTypes } from '../charts/types'; +import { useHostKpiCharts, useHostCharts, useKubernetesCharts } from './use_host_metrics_charts'; const dataViewId = 'metricsDataViewId'; const getHostChartsExpectedOrder = (metric: HostMetricTypes, overview: boolean): string[] => { diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_host_metrics_charts.ts similarity index 91% rename from x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.ts rename to x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_host_metrics_charts.ts index 5450e86029670..6d5381caf6187 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_host_metrics_charts.ts @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; import useAsync from 'react-use/lib/useAsync'; +import { HostMetricTypes } from '../charts/types'; -export type HostMetricTypes = 'cpu' | 'memory' | 'network' | 'disk' | 'log' | 'kpi'; interface UseChartsOptions { overview?: boolean; } @@ -101,9 +101,7 @@ export const useHostKpiCharts = ({ ...chart, seriesColor: options?.seriesColor, decimals: 1, - subtitle: options?.getSubtitle - ? options?.getSubtitle(chart.value) - : getSubtitleFromFormula(chart.value), + subtitle: getSubtitle(options, chart), ...(dataViewId && { dataset: { index: dataViewId, @@ -151,3 +149,12 @@ const getHostsCharts = async ({ return []; } }; + +function getSubtitle( + options: { getSubtitle?: ((formulaValue: string) => string) | undefined } | undefined, + chart: { value: string } +) { + return options?.getSubtitle + ? options?.getSubtitle(chart.value) + : getSubtitleFromFormula(chart.value); +} diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/host_metrics.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/host_metrics.tsx index 3246d452399cb..9d08a628aec5f 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/host_metrics.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/host_metrics.tsx @@ -6,13 +6,13 @@ */ import React, { useRef } from 'react'; -import { HostMetricTypes } from '../../hooks/use_metrics_charts'; import { useDatePickerContext } from '../../hooks/use_date_picker'; import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; import { useDataViewsContext } from '../../hooks/use_data_views'; import { useIntersectingState } from '../../hooks/use_intersecting_state'; import { MetricsTemplate } from './metrics_template'; -import { HostCharts, KubernetesCharts } from '../../charts'; +import { HostCharts, KubernetesNodeCharts } from '../../charts'; +import { HostMetricTypes } from '../../charts/types'; const METRIC_TYPES: Array> = [ 'cpu', @@ -41,7 +41,7 @@ export const HostMetrics = () => { metric={metric} /> ))} - { +export const KPIGrid = ({ assetId, assetType, dataView, dateRange }: Props) => { const { searchSessionId } = useLoadingStateContext(); const filters = useMemo(() => { return [ - buildCombinedHostsFilter({ - field: findInventoryFields('host').id, + buildCombinedAssetFilter({ + field: findInventoryFields(assetType).id, values: [assetId], dataView, }), ]; - }, [dataView, assetId]); + }, [dataView, assetId, assetType]); return ( - + {assetType === 'host' ? ( + + ) : ( + + )} ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/container_metrics.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/container_metrics.tsx new file mode 100644 index 0000000000000..f14619005eadb --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/container_metrics.tsx @@ -0,0 +1,42 @@ +/* + * 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, EuiFlexGrid } from '@elastic/eui'; +import type { TimeRange } from '@kbn/es-query'; +import type { DataView } from '@kbn/data-views-plugin/public'; +import { DockerCharts } from '../../../charts/docker_charts'; +import { INTEGRATIONS } from '../../../constants'; +import { useIntegrationCheck } from '../../../hooks/use_integration_check'; +import { KubernetesContainerCharts } from '../../../charts/kubernetes_charts'; + +interface Props { + assetId: string; + dateRange: TimeRange; + dataView?: DataView; +} + +export const ContainerMetrics = (props: Props) => { + const isK8sContainer = useIntegrationCheck({ dependsOn: INTEGRATIONS.kubernetesContainer }); + + return ( + + + {isK8sContainer ? ( + <> + + + + ) : ( + <> + + + + )} + + + ); +}; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/host_metrics.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/host_metrics.tsx index e7fe8bf795b47..6ac962570ebc6 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/host_metrics.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/host_metrics.tsx @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexGrid } from '@elastic/eui'; import type { TimeRange } from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/public'; import { useTabSwitcherContext } from '../../../hooks/use_tab_switcher'; -import { HostCharts, KubernetesCharts } from '../../../charts'; +import { HostCharts, KubernetesNodeCharts } from '../../../charts'; import { ContentTabIds } from '../../../types'; interface Props { @@ -33,7 +33,7 @@ export const HostMetrics = (props: Props) => { - + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics.tsx index b5cdac6bd4a74..f3ad7da1b99f5 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics.tsx @@ -9,6 +9,7 @@ import type { TimeRange } from '@kbn/es-query'; import type { DataView } from '@kbn/data-views-plugin/public'; import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { HostMetrics } from './host_metrics'; +import { ContainerMetrics } from './container_metrics'; import { Section } from '../../../components/section'; import { MetricsSectionTitle } from '../section_titles'; @@ -24,6 +25,8 @@ export const MetricsContent = ({ assetType, ...props }: Props) => { switch (assetType) { case 'host': return ; + case 'container': + return ; default: return null; } @@ -31,7 +34,7 @@ export const MetricsContent = ({ assetType, ...props }: Props) => { return (
} + title={} data-test-subj="infraAssetDetailsMetricsCollapsible" id="metrics" collapsible diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/overview.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/overview.tsx index c67347a765d69..99723fce587ba 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/overview.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/overview.tsx @@ -48,21 +48,29 @@ export const Overview = () => { return ( - - - - - {fetchMetadataError && !metadataLoading ? : metadataSummarySection} - - - - + {asset.type === 'host' ? : null} + + + + {fetchMetadataError && !metadataLoading ? : metadataSummarySection} + {asset.type === 'host' ? ( + + + + + ) : null} {asset.type === 'host' ? ( diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_titles.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_titles.tsx index c330944ffbcdc..d7a718939056a 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_titles.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_titles.tsx @@ -6,12 +6,14 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; +import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { HostMetricsExplanationContent } from '../../../lens'; import { TitleWithTooltip } from '../../components/section_title'; import { AlertsTooltipContent } from '../../components/alerts_tooltip_content'; import { ServicesTooltipContent } from '../../components/services_tooltip_content'; +import { ContainerMetricsExplanationContent } from '../../../lens/metric_explanation/container_metrics_explanation_content'; -export const MetricsSectionTitle = () => { +export const MetricsSectionTitle = ({ assetType }: { assetType: InventoryItemType }) => { return ( { })} data-test-subj="infraAssetDetailsMetricsTitle" tooltipTestSubj="infraAssetDetailsMetricsPopoverButton" - tooltipContent={} + tooltipContent={getTooltipContent(assetType)} /> ); }; @@ -47,3 +49,12 @@ export const ServicesSectionTitle = () => ( tooltipContent={} /> ); + +function getTooltipContent(assetType: InventoryItemType) { + switch (assetType) { + case 'host': + return ; + default: + return ; + } +} diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/translations.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/translations.ts index 1313e655f6b6f..0f313dc445d69 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/translations.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/translations.ts @@ -31,3 +31,18 @@ export const HOST_METRIC_GROUP_TITLES = { defaultMessage: 'Kubernetes', }), }; + +export const CONTAINER_METRIC_GROUP_TITLES = { + cpu: i18n.translate('xpack.infra.metricsGroup.containerCpu', { + defaultMessage: 'CPU', + }), + memory: i18n.translate('xpack.infra.metricsGroup.containerMemory', { + defaultMessage: 'Memory', + }), + network: i18n.translate('xpack.infra.metricsGroup.containerNetwork', { + defaultMessage: 'Network', + }), + disk: i18n.translate('xpack.infra.metricsGroup.containerDisk', { + defaultMessage: 'Disk', + }), +}; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts index 0299bd3582c8b..e51a7b53bda7d 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts @@ -98,5 +98,7 @@ export interface RouteState { export type DataViewOrigin = 'logs' | 'metrics'; export enum INTEGRATION_NAME { - kubernetes = 'kubernetes', + kubernetesNode = 'kubernetesNode', + kubernetesContainer = 'kubernetesContainer', + docker = 'docker', } diff --git a/x-pack/plugins/observability_solution/infra/public/components/lens/metric_explanation/container_metrics_explanation_content.tsx b/x-pack/plugins/observability_solution/infra/public/components/lens/metric_explanation/container_metrics_explanation_content.tsx new file mode 100644 index 0000000000000..637db2e6388ef --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/components/lens/metric_explanation/container_metrics_explanation_content.tsx @@ -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 React from 'react'; +import { EuiText, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + CONTAINER_METRICS_DOC_HREF, + HOST_METRICS_DOTTED_LINES_DOC_HREF, +} from '../../../common/visualizations/constants'; + +export const ContainerMetricsExplanationContent = () => { + return ( + +

+ +

+

+ + + {i18n.translate('xpack.infra.containerViewPage.tooltip.whatAreTheseMetricsLink', { + defaultMessage: 'What are these metrics?', + })} + + +

+

+ + + {i18n.translate('xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines', { + defaultMessage: 'Why am I seeing dotted lines?', + })} + + +

+
+ ); +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx index 6177518b3a8fe..d7e36d950d4ca 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx @@ -10,7 +10,7 @@ import { useSourceContext } from '../../../../../containers/metrics_source'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import type { HostNodeRow } from '../../hooks/use_hosts_table'; import { AssetDetails } from '../../../../../components/asset_details'; -import { commonFlyoutTabs } from '../../../../../common/asset_details_config/asset_details_tabs'; +import { hostDetailsTabs } from '../../../../../common/asset_details_config/asset_details_tabs'; export interface Props { node: HostNodeRow; @@ -32,7 +32,7 @@ export const FlyoutWrapper = ({ node: { name }, closeFlyout }: Props) => { showActionsColumn: true, }, }} - tabs={commonFlyoutTabs} + tabs={hostDetailsTabs} links={['nodeDetails']} renderMode={{ mode: 'flyout', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx index 9c43581df8a9d..9fd8f40fa08c7 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { HostKpiCharts } from '../../../../../components/asset_details'; -import { buildCombinedHostsFilter } from '../../../../../utils/filters/build'; +import { buildCombinedAssetFilter } from '../../../../../utils/filters/build'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import { useHostsViewContext } from '../../hooks/use_hosts_view'; import { useHostCountContext } from '../../hooks/use_host_count'; @@ -26,7 +26,7 @@ export const KpiCharts = () => { const filters = shouldUseSearchCriteria ? [...searchCriteria.filters, ...(searchCriteria.panelFilters ?? [])] : [ - buildCombinedHostsFilter({ + buildCombinedAssetFilter({ field: 'host.name', values: hostNodes.map((p) => p.name), dataView, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx index c977c1fde37a9..27344ccd1f108 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/logs/logs_tab_content.tsx @@ -16,7 +16,7 @@ import { useUnifiedSearchContext } from '../../../hooks/use_unified_search'; import { useLogsSearchUrlState } from '../../../hooks/use_logs_search_url_state'; import { LogsLinkToStream } from './logs_link_to_stream'; import { LogsSearchBar } from './logs_search_bar'; -import { buildCombinedHostsFilter } from '../../../../../../utils/filters/build'; +import { buildCombinedAssetFilter } from '../../../../../../utils/filters/build'; import { useLogViewReference } from '../../../../../../hooks/use_log_view_reference'; export const LogsTabContent = () => { @@ -27,7 +27,7 @@ export const LogsTabContent = () => { const hostsFilterQuery = useMemo( () => - buildCombinedHostsFilter({ + buildCombinedAssetFilter({ field: 'host.name', values: hostNodes.map((p) => p.name), }), diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/chart.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/chart.tsx index 88846924c15e2..79a4b610dd7a0 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/chart.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/chart.tsx @@ -11,7 +11,7 @@ import { METRIC_CHART_HEIGHT } from '../../../../../../common/visualizations/con import { LensChart } from '../../../../../../components/lens'; import { useUnifiedSearchContext } from '../../../hooks/use_unified_search'; import { useHostsViewContext } from '../../../hooks/use_hosts_view'; -import { buildCombinedHostsFilter } from '../../../../../../utils/filters/build'; +import { buildCombinedAssetFilter } from '../../../../../../utils/filters/build'; import { useHostsTableContext } from '../../../hooks/use_hosts_table'; import { useAfterLoadedState } from '../../../hooks/use_after_loaded_state'; @@ -40,7 +40,7 @@ export const Chart = ({ id, ...chartProps }: ChartProps) => { return shouldUseSearchCriteria ? [...searchCriteria.filters, ...(searchCriteria.panelFilters ?? [])] : [ - buildCombinedHostsFilter({ + buildCombinedAssetFilter({ field: 'host.name', values: currentPage.map((p) => p.name), dataView, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx index e5bd61b9b1e19..0cf70b4b4182b 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -29,7 +29,7 @@ import { useMetricsDataViewContext } from './use_metrics_data_view'; import { ColumnHeader } from '../components/table/column_header'; import { TABLE_COLUMN_LABEL, TABLE_CONTENT_LABEL } from '../translations'; import { METRICS_TOOLTIP } from '../../../../common/visualizations'; -import { buildCombinedHostsFilter } from '../../../../utils/filters/build'; +import { buildCombinedAssetFilter } from '../../../../utils/filters/build'; /** * Columns and items types @@ -151,7 +151,7 @@ export const useHostsTable = () => { return []; } const selectedHostNames = selectedItems.map(({ name }) => name); - const newFilter = buildCombinedHostsFilter({ + const newFilter = buildCombinedAssetFilter({ field: 'host.name', values: selectedHostNames, dataView, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx index 31b16ca70f26d..af50a277b593b 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx @@ -12,7 +12,7 @@ import type { InfraWaffleMapOptions } from '../../../../../lib/lib'; import { ContentTabIds } from '../../../../../components/asset_details/types'; import { AssetDetails } from '../../../../../components/asset_details'; import { useSourceContext } from '../../../../../containers/metrics_source'; -import { commonFlyoutTabs } from '../../../../../common/asset_details_config/asset_details_tabs'; +import { hostDetailsTabs } from '../../../../../common/asset_details_config/asset_details_tabs'; interface Props { assetName: string; @@ -25,7 +25,7 @@ interface Props { } const flyoutTabs = [ - ...commonFlyoutTabs, + ...hostDetailsTabs, { id: ContentTabIds.LINK_TO_APM, name: i18n.translate('xpack.infra.assetDetails.tabs.linkToApm', { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx index 29b3978573377..d7b88870b6450 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx @@ -57,7 +57,7 @@ export const WaffleInventorySwitcher: React.FC = () => { ); const goToHost = useCallback(() => goToNodeType('host'), [goToNodeType]); const goToK8 = useCallback(() => goToNodeType('pod'), [goToNodeType]); - const goToDocker = useCallback(() => goToNodeType('container'), [goToNodeType]); + const goToContainer = useCallback(() => goToNodeType('container'), [goToNodeType]); const goToAwsEC2 = useCallback(() => goToNodeType('awsEC2'), [goToNodeType]); const goToAwsS3 = useCallback(() => goToNodeType('awsS3'), [goToNodeType]); const goToAwsRDS = useCallback(() => goToNodeType('awsRDS'), [goToNodeType]); @@ -79,9 +79,9 @@ export const WaffleInventorySwitcher: React.FC = () => { onClick: goToK8, }, { - 'data-test-subj': 'goToDocker', + 'data-test-subj': 'goToContainer', name: getDisplayNameForType('container'), - onClick: goToDocker, + onClick: goToContainer, }, { name: 'AWS', @@ -117,7 +117,7 @@ export const WaffleInventorySwitcher: React.FC = () => { ], }, ] as EuiContextMenuPanelDescriptor[], - [goToAwsEC2, goToAwsRDS, goToAwsS3, goToAwsSQS, goToDocker, goToHost, goToK8] + [goToAwsEC2, goToAwsRDS, goToAwsS3, goToAwsSQS, goToContainer, goToHost, goToK8] ); const selectedText = useMemo(() => { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx index f0733ca291b37..31cbe1baae31b 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx @@ -14,7 +14,7 @@ import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useSourceContext } from '../../../containers/metrics_source'; import { AssetDetails } from '../../../components/asset_details'; import { MetricsPageTemplate } from '../page_template'; -import { commonFlyoutTabs } from '../../../common/asset_details_config/asset_details_tabs'; +import { getAssetDetailsTabs } from '../../../common/asset_details_config/asset_details_tabs'; export const AssetDetailPage = () => { const { isLoading, loadSourceFailureMessage, loadSource, source } = useSourceContext(); @@ -43,7 +43,7 @@ export const AssetDetailPage = () => { { params: { type: nodeType }, } = useRouteMatch<{ type: InventoryItemType; node: string }>(); + const isContainerAssetViewEnabled = useUiSetting(enableInfrastructureContainerAssetView); + + const showContainerAssetDetailPage = nodeType === 'container' && isContainerAssetViewEnabled; return ( - {nodeType === 'host' ? ( + {nodeType === 'host' || showContainerAssetDetailPage ? ( ) : ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/features_configuration_panel.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/features_configuration_panel.tsx index a8f8569014c7d..f0d7e9d487277 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/features_configuration_panel.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/features_configuration_panel.tsx @@ -13,6 +13,7 @@ import React from 'react'; import { enableInfrastructureHostsView, enableInfrastructureProfilingIntegration, + enableInfrastructureContainerAssetView, } from '@kbn/observability-plugin/common'; import { useEditableSettings } from '@kbn/observability-shared-plugin/public'; import { withSuspense } from '@kbn/shared-ux-utility'; @@ -83,6 +84,12 @@ export function FeaturesConfigurationPanel({ unsavedChange={unsavedChanges[enableInfrastructureProfilingIntegration]} /> )} + ); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx index cbfe09df53b2d..004a0b9e9bb55 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx @@ -14,6 +14,7 @@ import { useEditableSettings, } from '@kbn/observability-shared-plugin/public'; import { + enableInfrastructureContainerAssetView, enableInfrastructureHostsView, enableInfrastructureProfilingIntegration, } from '@kbn/observability-plugin/common'; @@ -90,6 +91,7 @@ export const SourceConfigurationSettings = ({ const infraUiSettings = useEditableSettings([ enableInfrastructureHostsView, enableInfrastructureProfilingIntegration, + enableInfrastructureContainerAssetView, ]); const resetAllUnsavedChanges = useCallback(() => { diff --git a/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts b/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts index b52804390bc98..0aee9c00814c6 100644 --- a/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts +++ b/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts @@ -16,7 +16,7 @@ import type { DataView } from '@kbn/data-views-plugin/common'; import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common'; import type { InfraCustomDashboardAssetType } from '../../../common/custom_dashboards'; -export const buildCombinedHostsFilter = ({ +export const buildCombinedAssetFilter = ({ field, values, dataView, diff --git a/x-pack/plugins/observability_solution/infra/public/utils/filters/create_alerts_es_query.ts b/x-pack/plugins/observability_solution/infra/public/utils/filters/create_alerts_es_query.ts index a0922e794df71..e80ac2cdf9ac0 100644 --- a/x-pack/plugins/observability_solution/infra/public/utils/filters/create_alerts_es_query.ts +++ b/x-pack/plugins/observability_solution/infra/public/utils/filters/create_alerts_es_query.ts @@ -9,7 +9,7 @@ import { ALERT_TIME_RANGE } from '@kbn/rule-data-utils'; import { BoolQuery, buildEsQuery, Filter, type TimeRange } from '@kbn/es-query'; import type { AlertStatus } from '@kbn/observability-plugin/common/typings'; import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common'; -import { buildCombinedHostsFilter } from './build'; +import { buildCombinedAssetFilter } from './build'; import { ALERT_STATUS_QUERY } from '../../components/shared/alerts/constants'; export interface AlertsEsQuery { @@ -28,7 +28,7 @@ export const createAlertsEsQuery = ({ const alertStatusFilter = createAlertStatusFilter(status); const dateFilter = createDateFilter(dateRange); - const hostsFilter = buildCombinedHostsFilter({ + const hostsFilter = buildCombinedAssetFilter({ field: findInventoryFields('host').id, values: assetIds, }); diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/index.ts index e50d020d1d4f7..a113c6fd1802c 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/index.ts @@ -11,7 +11,7 @@ import { InventoryModel } from '../types'; export { containerSnapshotMetricTypes } from './metrics'; -export const container: InventoryModel = { +export const container: InventoryModel = { id: 'container', displayName: i18n.translate('xpack.metricsData.inventoryModel.container.displayName', { defaultMessage: 'Docker Containers', diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/cpu.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/cpu.ts new file mode 100644 index 0000000000000..36c5ddaf91acd --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/cpu.ts @@ -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 { + CPU_USAGE_LABEL, + DEFAULT_XY_FITTING_FUNCTION, + DEFAULT_XY_HIDDEN_AXIS_TITLE, + DEFAULT_XY_HIDDEN_LEGEND, + DEFAULT_XY_YBOUNDS, +} from '../../../shared/charts/constants'; +import { LensConfigWithId } from '../../../types'; +import { formulas } from '../formulas'; + +const dockerContainerCpuUsageXY: LensConfigWithId = { + id: 'cpuUsage', + chartType: 'xy', + title: CPU_USAGE_LABEL, + layers: [ + { + seriesType: 'line', + type: 'series', + xAxis: '@timestamp', + yAxis: [formulas.dockerContainerCpuUsage], + }, + ], + ...DEFAULT_XY_FITTING_FUNCTION, + ...DEFAULT_XY_HIDDEN_LEGEND, + ...DEFAULT_XY_HIDDEN_AXIS_TITLE, + ...DEFAULT_XY_YBOUNDS, +}; + +const k8sContainerCpuUsageXY: LensConfigWithId = { + id: 'k8sCpuUsage', + chartType: 'xy', + title: CPU_USAGE_LABEL, + layers: [ + { + seriesType: 'line', + type: 'series', + xAxis: '@timestamp', + yAxis: [formulas.k8sContainerCpuUsage], + }, + ], + ...DEFAULT_XY_FITTING_FUNCTION, + ...DEFAULT_XY_HIDDEN_LEGEND, + ...DEFAULT_XY_HIDDEN_AXIS_TITLE, + ...DEFAULT_XY_YBOUNDS, +}; + +const dockerContainerCpuUsageMetric: LensConfigWithId = { + id: 'cpuUsage', + chartType: 'metric', + title: CPU_USAGE_LABEL, + trendLine: true, + ...formulas.dockerContainerCpuUsage, +}; + +const containerK8sCpuUsageMetric: LensConfigWithId = { + id: 'k8sCpuUsage', + chartType: 'metric', + title: CPU_USAGE_LABEL, + trendLine: true, + ...formulas.k8sContainerCpuUsage, +}; + +export const cpu = { + xy: { + dockerContainerCpuUsage: dockerContainerCpuUsageXY, + k8sContainerCpuUsage: k8sContainerCpuUsageXY, + }, + metric: { + dockerContainerCpuUsage: dockerContainerCpuUsageMetric, + k8sContainerCpuUsage: containerK8sCpuUsageMetric, + }, +} as const; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/index.ts new file mode 100644 index 0000000000000..6a83e00c9c5c8 --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { cpu } from './cpu'; +import { memory } from './memory'; + +export const charts = { + cpu, + memory, +} as const; + +export type ContainerCharts = typeof charts; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/memory.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/memory.ts new file mode 100644 index 0000000000000..45ce080d7f448 --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/charts/memory.ts @@ -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 { LensConfigWithId } from '../../../types'; +import { + DEFAULT_XY_FITTING_FUNCTION, + DEFAULT_XY_HIDDEN_AXIS_TITLE, + DEFAULT_XY_HIDDEN_LEGEND, + DEFAULT_XY_YBOUNDS, + MEMORY_USAGE_LABEL, +} from '../../../shared/charts/constants'; +import { formulas } from '../formulas'; + +const dockerContainerMemoryUsageXY: LensConfigWithId = { + id: 'memoryUsage', + chartType: 'xy', + title: MEMORY_USAGE_LABEL, + layers: [ + { + seriesType: 'line', + type: 'series', + xAxis: '@timestamp', + yAxis: [formulas.dockerContainerMemoryUsage], + }, + ], + ...DEFAULT_XY_FITTING_FUNCTION, + ...DEFAULT_XY_HIDDEN_LEGEND, + ...DEFAULT_XY_YBOUNDS, + ...DEFAULT_XY_HIDDEN_AXIS_TITLE, +}; + +const k8sContainerMemoryUsageXY: LensConfigWithId = { + id: 'k8sMemoryUsage', + chartType: 'xy', + title: MEMORY_USAGE_LABEL, + layers: [ + { + seriesType: 'line', + type: 'series', + xAxis: '@timestamp', + yAxis: [formulas.k8sContainerMemoryUsage], + }, + ], + ...DEFAULT_XY_FITTING_FUNCTION, + ...DEFAULT_XY_HIDDEN_LEGEND, + ...DEFAULT_XY_YBOUNDS, + ...DEFAULT_XY_HIDDEN_AXIS_TITLE, +}; + +const dockerContainerMemoryUsageMetric: LensConfigWithId = { + id: 'memoryUsage', + chartType: 'metric', + title: MEMORY_USAGE_LABEL, + trendLine: true, + ...formulas.dockerContainerMemoryUsage, +}; + +const k8sContainerMemoryUsageMetric: LensConfigWithId = { + id: 'k8sMemoryUsage', + chartType: 'metric', + title: MEMORY_USAGE_LABEL, + trendLine: true, + ...formulas.k8sContainerMemoryUsage, +}; + +export const memory = { + xy: { + dockerContainerMemoryUsage: dockerContainerMemoryUsageXY, + k8sContainerMemoryUsage: k8sContainerMemoryUsageXY, + }, + metric: { + dockerContainerMemoryUsage: dockerContainerMemoryUsageMetric, + k8sContainerMemoryUsage: k8sContainerMemoryUsageMetric, + }, +}; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/cpu.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/cpu.ts new file mode 100644 index 0000000000000..283f139d31a7a --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/cpu.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LensBaseLayer } from '@kbn/lens-embeddable-utils/config_builder'; +import { CPU_USAGE_LABEL } from '../../../shared/charts/constants'; + +export const dockerContainerCpuUsage: LensBaseLayer = { + label: CPU_USAGE_LABEL, + value: 'average(docker.cpu.total.pct)', + format: 'percent', + decimals: 1, +}; + +export const k8sContainerCpuUsage: LensBaseLayer = { + label: CPU_USAGE_LABEL, + value: 'average(kubernetes.container.cpu.usage.limit.pct)', + format: 'percent', + decimals: 1, +}; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/index.ts new file mode 100644 index 0000000000000..2f5e4f7975f7a --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/index.ts @@ -0,0 +1,18 @@ +/* + * 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 { dockerContainerCpuUsage, k8sContainerCpuUsage } from './cpu'; +import { dockerContainerMemoryUsage, k8sContainerMemoryUsage } from './memory'; + +export const formulas = { + dockerContainerCpuUsage, + dockerContainerMemoryUsage, + k8sContainerCpuUsage, + k8sContainerMemoryUsage, +} as const; + +export type ContainerFormulas = typeof formulas; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/memory.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/memory.ts new file mode 100644 index 0000000000000..827f06e4fdb0d --- /dev/null +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/formulas/memory.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { LensBaseLayer } from '@kbn/lens-embeddable-utils/config_builder'; +import { MEMORY_USAGE_LABEL } from '../../../shared/charts/constants'; + +export const dockerContainerMemoryUsage: LensBaseLayer = { + label: MEMORY_USAGE_LABEL, + value: 'average(docker.memory.usage.pct)', + format: 'percent', + decimals: 1, +}; + +export const k8sContainerMemoryUsage: LensBaseLayer = { + label: MEMORY_USAGE_LABEL, + value: 'average(kubernetes.container.memory.usage.limit.pct)', + format: 'percent', + decimals: 1, +}; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/index.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/index.ts index eb0678890ad3a..faa848192fd46 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/index.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/index.ts @@ -5,22 +5,23 @@ * 2.0. */ -import { InventoryMetrics } from '../../types'; +import { InventoryMetricsWithCharts } from '../../types'; import { cpu } from './snapshot/cpu'; import { memory } from './snapshot/memory'; import { rx } from './snapshot/rx'; import { tx } from './snapshot/tx'; - -import { containerOverview } from './tsvb/container_overview'; -import { containerCpuUsage } from './tsvb/container_cpu_usage'; import { containerCpuKernel } from './tsvb/container_cpu_kernel'; +import { containerCpuUsage } from './tsvb/container_cpu_usage'; import { containerDiskIOOps } from './tsvb/container_diskio_ops'; import { containerDiskIOBytes } from './tsvb/container_disk_io_bytes'; -import { containerMemory } from './tsvb/container_memory'; -import { containerNetworkTraffic } from './tsvb/container_network_traffic'; -import { containerK8sOverview } from './tsvb/container_k8s_overview'; import { containerK8sCpuUsage } from './tsvb/container_k8s_cpu_usage'; import { containerK8sMemoryUsage } from './tsvb/container_k8s_memory_usage'; +import { containerK8sOverview } from './tsvb/container_k8s_overview'; +import { containerMemory } from './tsvb/container_memory'; +import { containerNetworkTraffic } from './tsvb/container_network_traffic'; +import { containerOverview } from './tsvb/container_overview'; +import type { ContainerFormulas } from './formulas'; +import { ContainerCharts } from './charts'; const containerSnapshotMetrics = { cpu, memory, rx, tx }; @@ -28,7 +29,7 @@ export const containerSnapshotMetricTypes = Object.keys(containerSnapshotMetrics keyof typeof containerSnapshotMetrics >; -export const metrics: InventoryMetrics = { +export const metrics: InventoryMetricsWithCharts = { tsvb: { containerOverview, containerCpuUsage, @@ -42,6 +43,8 @@ export const metrics: InventoryMetrics = { containerK8sMemoryUsage, }, snapshot: containerSnapshotMetrics, + getFormulas: async () => await import('./formulas').then(({ formulas }) => formulas), + getCharts: async () => await import('./charts').then(({ charts }) => charts), defaultSnapshot: 'cpu', defaultTimeRangeInSeconds: 3600, // 1 hour }; diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/cpu.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/cpu.ts index a12637d6c9a2c..bfa9c1f9dec60 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/cpu.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/cpu.ts @@ -5,8 +5,9 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import { + CPU_USAGE_LABEL, + LOAD_LABEL, DEFAULT_XY_FITTING_FUNCTION, DEFAULT_XY_HIDDEN_AXIS_TITLE, DEFAULT_XY_HIDDEN_LEGEND, @@ -19,9 +20,7 @@ import { formulas } from '../formulas'; const cpuUsageBreakdown: LensConfigWithId = { id: 'cpuUsageBreakdown', chartType: 'xy', - title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.cpuUsage', { - defaultMessage: 'CPU Usage', - }), + title: CPU_USAGE_LABEL, layers: [ { seriesType: 'area', @@ -47,9 +46,7 @@ const cpuUsageBreakdown: LensConfigWithId = { const loadBreakdown: LensConfigWithId = { id: 'loadBreakdown', chartType: 'xy', - title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.load', { - defaultMessage: 'Load', - }), + title: LOAD_LABEL, layers: [ { seriesType: 'area', diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/disk.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/disk.ts index a73c9f274b40b..112de73066518 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/disk.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/disk.ts @@ -14,14 +14,15 @@ import { DEFAULT_XY_HIDDEN_LEGEND, DEFAULT_XY_LEGEND, DEFAULT_XY_YBOUNDS, + DISK_IOPS_LABEL, + DISK_THROUGHPUT_LABEL, + DISK_USAGE_BY_MOUNT_POINT_LABEL, } from '../../../shared/charts/constants'; const diskIOReadWrite: LensConfigWithId = { id: 'diskIOReadWrite', chartType: 'xy', - title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskIOPS', { - defaultMessage: 'Disk IOPS', - }), + title: DISK_IOPS_LABEL, layers: [ { seriesType: 'area', @@ -51,9 +52,7 @@ const diskIOReadWrite: LensConfigWithId = { const diskUsageByMountPoint: LensConfigWithId = { id: 'diskUsageByMountPoint', chartType: 'xy', - title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskUsageByMountingPoint', { - defaultMessage: 'Disk Usage by Mount Point', - }), + title: DISK_USAGE_BY_MOUNT_POINT_LABEL, layers: [ { seriesType: 'area', @@ -86,9 +85,7 @@ const diskUsageByMountPoint: LensConfigWithId = { const diskThroughputReadWrite: LensConfigWithId = { id: 'diskThroughputReadWrite', chartType: 'xy', - title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskThroughput', { - defaultMessage: 'Disk Throughput', - }), + title: DISK_THROUGHPUT_LABEL, layers: [ { seriesType: 'area', diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/memory.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/memory.ts index 91e5fcff000e8..8113848810fe8 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/memory.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/memory.ts @@ -14,14 +14,13 @@ import { DEFAULT_XY_HIDDEN_LEGEND, DEFAULT_XY_LEGEND, DEFAULT_XY_YBOUNDS, + MEMORY_USAGE_LABEL, } from '../../../shared/charts/constants'; const memoryUsageBreakdown: LensConfigWithId = { id: 'memoryUsageBreakdown', chartType: 'xy', - title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.memoryUsage', { - defaultMessage: 'Memory Usage', - }), + title: MEMORY_USAGE_LABEL, layers: [ { seriesType: 'area', diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/network.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/network.ts index e9a4ea0351779..d94dd48db8370 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/network.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/charts/network.ts @@ -13,14 +13,13 @@ import { DEFAULT_XY_HIDDEN_AXIS_TITLE, DEFAULT_XY_HIDDEN_LEGEND, DEFAULT_XY_LEGEND, + NETWORK_LABEL, } from '../../../shared/charts/constants'; const rxTx: LensConfigWithId = { id: 'rxTx', chartType: 'xy', - title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.network', { - defaultMessage: 'Network', - }), + title: NETWORK_LABEL, layers: [ { seriesType: 'area', diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu.ts index 10256dcb1cbf5..fa75dc071666c 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/cpu.ts @@ -7,6 +7,13 @@ import { i18n } from '@kbn/i18n'; import type { LensBaseLayer } from '@kbn/lens-embeddable-utils/config_builder'; +import { + CPU_USAGE_LABEL, + LOAD_15M_LABEL, + LOAD_1M_LABEL, + LOAD_5M_LABEL, + NORMALIZED_LOAD_LABEL, +} from '../../../shared/charts/constants'; export const cpuUsageIowait: LensBaseLayer = { label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage.iowaitLabel', { @@ -72,45 +79,35 @@ export const cpuUsageUser: LensBaseLayer = { }; export const cpuUsage: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.cpuUsage', { - defaultMessage: 'CPU Usage', - }), + label: CPU_USAGE_LABEL, value: '(average(system.cpu.user.pct) + average(system.cpu.system.pct)) / max(system.cpu.cores)', format: 'percent', decimals: 0, }; export const load1m: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.load1m', { - defaultMessage: 'Load (1m)', - }), + label: LOAD_1M_LABEL, value: 'average(system.load.1)', format: 'number', decimals: 1, }; export const load5m: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.load5m', { - defaultMessage: 'Load (5m)', - }), + label: LOAD_5M_LABEL, value: 'average(system.load.5)', format: 'number', decimals: 1, }; export const load15m: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.load15m', { - defaultMessage: 'Load (15m)', - }), + label: LOAD_15M_LABEL, value: 'average(system.load.15)', format: 'number', decimals: 1, }; export const normalizedLoad1m: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.normalizedLoad1m', { - defaultMessage: 'Normalized Load', - }), + label: NORMALIZED_LOAD_LABEL, value: 'average(system.load.1) / max(system.load.cores)', format: 'percent', decimals: 0, diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/disk.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/disk.ts index 6d6f644e19ace..9eae6986a6065 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/disk.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/disk.ts @@ -5,13 +5,20 @@ * 2.0. */ -import { i18n } from '@kbn/i18n'; import type { LensBaseLayer } from '@kbn/lens-embeddable-utils/config_builder'; +import { + DISK_READ_IOPS_LABEL, + DISK_READ_THROUGHPUT_LABEL, + DISK_SPACE_AVAILABILITY_LABEL, + DISK_SPACE_AVAILABLE_LABEL, + DISK_USAGE_AVERAGE_LABEL, + DISK_USAGE_LABEL, + DISK_WRITE_IOPS_LABEL, + DISK_WRITE_THROUGHPUT_LABEL, +} from '../../../shared/charts/constants'; export const diskIORead: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskIORead', { - defaultMessage: 'Disk Read IOPS', - }), + label: DISK_READ_IOPS_LABEL, value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')", format: 'number', decimals: 0, @@ -19,9 +26,7 @@ export const diskIORead: LensBaseLayer = { }; export const diskReadThroughput: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskReadThroughput', { - defaultMessage: 'Disk Read Throughput', - }), + label: DISK_READ_THROUGHPUT_LABEL, value: "counter_rate(max(system.diskio.read.bytes), kql='system.diskio.read.bytes: *')", format: 'bytes', decimals: 1, @@ -29,45 +34,35 @@ export const diskReadThroughput: LensBaseLayer = { }; export const diskSpaceAvailable: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskSpaceAvailable', { - defaultMessage: 'Disk Space Available', - }), + label: DISK_SPACE_AVAILABLE_LABEL, value: 'average(system.filesystem.free)', format: 'bytes', decimals: 0, }; export const diskSpaceAvailability: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskSpaceAvailability', { - defaultMessage: 'Disk Space Availability', - }), + label: DISK_SPACE_AVAILABILITY_LABEL, value: '1 - average(system.filesystem.used.pct)', format: 'percent', decimals: 0, }; export const diskUsage: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskUsage', { - defaultMessage: 'Disk Usage', - }), + label: DISK_USAGE_LABEL, value: 'max(system.filesystem.used.pct)', format: 'percent', decimals: 0, }; export const diskUsageAverage: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskUsageAverage', { - defaultMessage: 'Disk Usage Average', - }), + label: DISK_USAGE_AVERAGE_LABEL, value: 'average(system.filesystem.used.pct)', format: 'percent', decimals: 0, }; export const diskIOWrite: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskIOWrite', { - defaultMessage: 'Disk Write IOPS', - }), + label: DISK_WRITE_IOPS_LABEL, value: "counter_rate(max(system.diskio.write.count), kql='system.diskio.write.count: *')", format: 'number', decimals: 0, @@ -75,9 +70,7 @@ export const diskIOWrite: LensBaseLayer = { }; export const diskWriteThroughput: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.diskWriteThroughput', { - defaultMessage: 'Disk Write Throughput', - }), + label: DISK_WRITE_THROUGHPUT_LABEL, value: "counter_rate(max(system.diskio.write.bytes), kql='system.diskio.write.bytes: *')", format: 'bytes', decimals: 1, diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/memory.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/memory.ts index 7b3afb4894e12..9c1d285d1b879 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/memory.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/host/metrics/formulas/memory.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import type { LensBaseLayer } from '@kbn/lens-embeddable-utils/config_builder'; +import { MEMORY_FREE_LABEL, MEMORY_USAGE_LABEL } from '../../../shared/charts/constants'; export const memoryCache: LensBaseLayer = { label: i18n.translate('xpack.metricsData.assetDetails.formulas.metric.label.cache', { @@ -18,9 +19,7 @@ export const memoryCache: LensBaseLayer = { }; export const memoryFree: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.memoryFree', { - defaultMessage: 'Memory Free', - }), + label: MEMORY_FREE_LABEL, value: 'max(system.memory.total) - average(system.memory.actual.used.bytes)', format: 'bytes', decimals: 1, @@ -36,9 +35,7 @@ export const memoryFreeExcludingCache: LensBaseLayer = { }; export const memoryUsage: LensBaseLayer = { - label: i18n.translate('xpack.metricsData.assetDetails.formulas.memoryUsage', { - defaultMessage: 'Memory Usage', - }), + label: MEMORY_USAGE_LABEL, value: 'average(system.memory.actual.used.pct)', format: 'percent', diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/shared/charts/constants.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/shared/charts/constants.ts index c835b1239769b..6d6d22c116f43 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/shared/charts/constants.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/shared/charts/constants.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { i18n } from '@kbn/i18n'; import type { LensXYConfigBase } from '@kbn/lens-embeddable-utils/config_builder'; export const DEFAULT_XY_FITTING_FUNCTION: Pick = { @@ -38,3 +39,134 @@ export const DEFAULT_XY_HIDDEN_AXIS_TITLE: Pick = { }), schema: schema.boolean(), }, + [enableInfrastructureContainerAssetView]: { + category: [observabilityFeatureId], + name: i18n.translate('xpack.observability.enableInfrastructureContainerAssetView', { + defaultMessage: 'Container view', + }), + value: false, + description: i18n.translate( + 'xpack.observability.enableInfrastructureContainerAssetViewDescription', + { + defaultMessage: 'Enable the Container asset view in the Infrastructure app.', + } + ), + schema: schema.boolean(), + }, [enableInfrastructureProfilingIntegration]: { category: [observabilityFeatureId], name: i18n.translate('xpack.observability.enableInfrastructureProfilingIntegration', { diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ae41414e3671a..526bee1ed8169 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -21088,10 +21088,8 @@ "xpack.infra.assetDetails.tabs.metadata.seeLess": "Afficher moins", "xpack.infra.assetDetails.tabs.metadata.seeMore": "+{count} de plus", "xpack.infra.assetDetails.tabs.metadata": "Métadonnées", - "xpack.infra.assetDetails.tabs.osquery": "Osquery", "xpack.infra.assetDetails.tabs.overview": "Aperçu", "xpack.infra.assetDetails.tabs.processes": "Processus", - "xpack.infra.assetDetails.tabs.profiling": "Universal Profiling", "xpack.infra.homePage.toolbar.showingLastOneMinuteDataText": "Dernières {duration} de données pour l'heure sélectionnée", "xpack.infra.hostsViewPage.errorOnCreateOrLoadDataview": "Une erreur s'est produite lors de la création d'une vue de données : {metricAlias}. Essayez de recharger la page.", "xpack.infra.hostsViewPage.kpi.subtitle.average.limit": "Moyenne (de {limit} hôtes)", @@ -44918,7 +44916,6 @@ "xpack.features.savedQueryManagementFeatureName": "Gestion des requêtes enregistrées", "xpack.features.savedQueryManagementTooltip": "Si \"All\" (Tout) est défini, les requêtes enregistrées peuvent être gérées grâce à Kibana dans toutes les applications compatibles. Si \"None\" est défini, les privilèges relatifs aux requêtes enregistrées sont fixés indépendamment pour chaque application.", "xpack.features.visualizeFeatureName": "Bibliothèque Visualize", - "xpack.metricsData.assetDetails.formulas.cpuUsage": "Utilisation CPU", "xpack.metricsData.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", "xpack.metricsData.assetDetails.formulas.cpuUsage.irqLabel": "irq", "xpack.metricsData.assetDetails.formulas.cpuUsage.niceLabel": "nice", @@ -44926,45 +44923,25 @@ "xpack.metricsData.assetDetails.formulas.cpuUsage.stealLabel": "steal", "xpack.metricsData.assetDetails.formulas.cpuUsage.systemLabel": "system", "xpack.metricsData.assetDetails.formulas.cpuUsage.userLabel": "utilisateur", - "xpack.metricsData.assetDetails.formulas.diskIORead": "Entrées et sorties par seconde en lecture sur le disque", - "xpack.metricsData.assetDetails.formulas.diskIOWrite": "Entrées et sorties par seconde en écriture sur le disque", - "xpack.metricsData.assetDetails.formulas.diskReadThroughput": "Rendement de lecture du disque", - "xpack.metricsData.assetDetails.formulas.diskSpaceAvailability": "Disponibilité de l'espace disque", - "xpack.metricsData.assetDetails.formulas.diskSpaceAvailable": "Espace disque disponible", - "xpack.metricsData.assetDetails.formulas.diskUsage": "Utilisation du disque", - "xpack.metricsData.assetDetails.formulas.diskWriteThroughput": "Rendement d’écriture du disque", "xpack.metricsData.assetDetails.formulas.hostCount.hostsLabel": "Hôtes", "xpack.metricsData.assetDetails.formulas.kubernetes.capacity": "Capacité", "xpack.metricsData.assetDetails.formulas.kubernetes.used": "Utilisé", - "xpack.metricsData.assetDetails.formulas.load15m": "Charge (15 min)", - "xpack.metricsData.assetDetails.formulas.load1m": "Charge (1 min)", - "xpack.metricsData.assetDetails.formulas.load5m": "Charge (5 min)", "xpack.metricsData.assetDetails.formulas.logRate": "Taux de log", - "xpack.metricsData.assetDetails.formulas.memoryFree": "Sans mémoire", - "xpack.metricsData.assetDetails.formulas.memoryUsage": "Utilisation mémoire", "xpack.metricsData.assetDetails.formulas.metric.label.cache": "cache", "xpack.metricsData.assetDetails.formulas.metric.label.free": "gratuit", "xpack.metricsData.assetDetails.formulas.metric.label.used": "utilisé", - "xpack.metricsData.assetDetails.formulas.normalizedLoad1m": "Charge normalisée", "xpack.metricsData.assetDetails.formulas.rx": "Réseau entrant (RX)", "xpack.metricsData.assetDetails.formulas.tx": "Réseau sortant (TX)", - "xpack.metricsData.assetDetails.metricsCharts.cpuUsage": "Utilisation CPU", - "xpack.metricsData.assetDetails.metricsCharts.diskIOPS": "Entrées et sorties par seconde (IOPS) sur le disque", - "xpack.metricsData.assetDetails.metricsCharts.diskThroughput": "Rendement du disque", "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.used": "Utilisé", - "xpack.metricsData.assetDetails.metricsCharts.diskUsageByMountingPoint": "Utilisation du disque par point de montage", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "Capacité CPU du nœud", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeDiskCapacity": "Capacité du disque du nœud", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeMemoryCapacity": "Capacité de mémoire du nœud", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodePodCapacity": "Capacité de pod du nœud", - "xpack.metricsData.assetDetails.metricsCharts.load": "Charge", - "xpack.metricsData.assetDetails.metricsCharts.memoryUsage": "Utilisation mémoire", "xpack.metricsData.assetDetails.metricsCharts.metric.label.cache": "Cache", "xpack.metricsData.assetDetails.metricsCharts.metric.label.free": "Gratuit", "xpack.metricsData.assetDetails.metricsCharts.metric.label.read": "Lire", "xpack.metricsData.assetDetails.metricsCharts.metric.label.used": "Utilisé", "xpack.metricsData.assetDetails.metricsCharts.metric.label.write": "Écrire", - "xpack.metricsData.assetDetails.metricsCharts.network": "Réseau", "xpack.metricsData.assetDetails.metricsCharts.network.label.rx": "Entrant (RX)", "xpack.metricsData.assetDetails.metricsCharts.network.label.tx": "Sortant (TX)", "xpack.metricsData.hostsPage.goToMetricsSettings": "Vérifier les paramètres", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 385bd1856c51b..e7c5c729dcd78 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -21228,10 +21228,8 @@ "xpack.infra.assetDetails.tabs.metadata.seeLess": "簡易表示", "xpack.infra.assetDetails.tabs.metadata.seeMore": "他 {count} 件", "xpack.infra.assetDetails.tabs.metadata": "メタデータ", - "xpack.infra.assetDetails.tabs.osquery": "Osquery", "xpack.infra.assetDetails.tabs.overview": "概要", "xpack.infra.assetDetails.tabs.processes": "プロセス", - "xpack.infra.assetDetails.tabs.profiling": "ユニバーサルプロファイリング", "xpack.infra.assetDetails.tooltip.activeAlertsExplanation": "アクティブアラート", "xpack.infra.bottomDrawer.kubernetesDashboardsLink": "Kubernetesダッシュボード", "xpack.infra.chartSection.missingMetricDataBody": "このチャートはデータが欠けています。", @@ -44888,7 +44886,6 @@ "xpack.features.savedQueryManagementFeatureName": "保存されたクエリ管理", "xpack.features.savedQueryManagementTooltip": "[すべて]に設定すると、保存されたクエリは、クエリをサポートするすべてのアプリケーションのKibana全体で管理できます。[なし]に設定すると、保存されたクエリ権限は各アプリケーションで独自に決定されます。", "xpack.features.visualizeFeatureName": "Visualizeライブラリ", - "xpack.metricsData.assetDetails.formulas.cpuUsage": "CPU使用状況", "xpack.metricsData.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", "xpack.metricsData.assetDetails.formulas.cpuUsage.irqLabel": "irq", "xpack.metricsData.assetDetails.formulas.cpuUsage.niceLabel": "nice", @@ -44896,45 +44893,25 @@ "xpack.metricsData.assetDetails.formulas.cpuUsage.stealLabel": "steal", "xpack.metricsData.assetDetails.formulas.cpuUsage.systemLabel": "システム", "xpack.metricsData.assetDetails.formulas.cpuUsage.userLabel": "ユーザー", - "xpack.metricsData.assetDetails.formulas.diskIORead": "ディスク読み取りIOPS", - "xpack.metricsData.assetDetails.formulas.diskIOWrite": "ディスク書き込みIOPS", - "xpack.metricsData.assetDetails.formulas.diskReadThroughput": "ディスク読み取りスループット", - "xpack.metricsData.assetDetails.formulas.diskSpaceAvailability": "空きディスク容量", - "xpack.metricsData.assetDetails.formulas.diskSpaceAvailable": "空きディスク容量", - "xpack.metricsData.assetDetails.formulas.diskUsage": "ディスク使用量", - "xpack.metricsData.assetDetails.formulas.diskWriteThroughput": "ディスク書き込みスループット", "xpack.metricsData.assetDetails.formulas.hostCount.hostsLabel": "ホスト", "xpack.metricsData.assetDetails.formulas.kubernetes.capacity": "容量", "xpack.metricsData.assetDetails.formulas.kubernetes.used": "使用中", - "xpack.metricsData.assetDetails.formulas.load15m": "読み込み(15m)", - "xpack.metricsData.assetDetails.formulas.load1m": "読み込み(1m)", - "xpack.metricsData.assetDetails.formulas.load5m": "読み込み(5m)", "xpack.metricsData.assetDetails.formulas.logRate": "ログレート", - "xpack.metricsData.assetDetails.formulas.memoryFree": "空きメモリー", - "xpack.metricsData.assetDetails.formulas.memoryUsage": "メモリー使用状況", "xpack.metricsData.assetDetails.formulas.metric.label.cache": "キャッシュ", "xpack.metricsData.assetDetails.formulas.metric.label.free": "空き", "xpack.metricsData.assetDetails.formulas.metric.label.used": "使用中", - "xpack.metricsData.assetDetails.formulas.normalizedLoad1m": "正規化された負荷", "xpack.metricsData.assetDetails.formulas.rx": "ネットワーク受信(RX)", "xpack.metricsData.assetDetails.formulas.tx": "ネットワーク送信(TX)", - "xpack.metricsData.assetDetails.metricsCharts.cpuUsage": "CPU使用状況", - "xpack.metricsData.assetDetails.metricsCharts.diskIOPS": "Disk IOPS", - "xpack.metricsData.assetDetails.metricsCharts.diskThroughput": "Disk Throughput", "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.used": "使用中", - "xpack.metricsData.assetDetails.metricsCharts.diskUsageByMountingPoint": "マウントポイント別ディスク使用量", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "ノード CPU 処理能力", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeDiskCapacity": "ノードディスク容量", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeMemoryCapacity": "ノードメモリー容量", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodePodCapacity": "ノードポッド容量", - "xpack.metricsData.assetDetails.metricsCharts.load": "読み込み", - "xpack.metricsData.assetDetails.metricsCharts.memoryUsage": "メモリー使用状況", "xpack.metricsData.assetDetails.metricsCharts.metric.label.cache": "キャッシュ", "xpack.metricsData.assetDetails.metricsCharts.metric.label.free": "空き", "xpack.metricsData.assetDetails.metricsCharts.metric.label.read": "読み取り", "xpack.metricsData.assetDetails.metricsCharts.metric.label.used": "使用中", "xpack.metricsData.assetDetails.metricsCharts.metric.label.write": "書き込み", - "xpack.metricsData.assetDetails.metricsCharts.network": "ネットワーク", "xpack.metricsData.assetDetails.metricsCharts.network.label.rx": "受信(RX)", "xpack.metricsData.assetDetails.metricsCharts.network.label.tx": "送信(TX)", "xpack.metricsData.hostsPage.goToMetricsSettings": "設定を確認", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5064879a9a604..b33c76617db9e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -21094,10 +21094,8 @@ "xpack.infra.assetDetails.tabs.metadata.seeLess": "显示更少", "xpack.infra.assetDetails.tabs.metadata.seeMore": "另外 {count} 个", "xpack.infra.assetDetails.tabs.metadata": "元数据", - "xpack.infra.assetDetails.tabs.osquery": "Osquery", "xpack.infra.assetDetails.tabs.overview": "概览", "xpack.infra.assetDetails.tabs.processes": "进程", - "xpack.infra.assetDetails.tabs.profiling": "Universal Profiling", "xpack.infra.homePage.toolbar.showingLastOneMinuteDataText": "选定时间过去 {duration}的数据", "xpack.infra.hostsViewPage.errorOnCreateOrLoadDataview": "尝试创建数据视图时出错:{metricAlias}。尝试重新加载该页面。", "xpack.infra.hostsViewPage.kpi.subtitle.average.limit": "平均值(属于 {limit} 台主机)", @@ -44936,7 +44934,6 @@ "xpack.features.savedQueryManagementFeatureName": "已保存查询管理", "xpack.features.savedQueryManagementTooltip": "如果设置为“全部”,可以在支持已保存查询的所有应用程序中管理整个 Kibana 中的已保存查询。如果设置为“无”,将由每个应用程序单独确定已保存查询权限。", "xpack.features.visualizeFeatureName": "Visualize 库", - "xpack.metricsData.assetDetails.formulas.cpuUsage": "CPU 使用率", "xpack.metricsData.assetDetails.formulas.cpuUsage.iowaitLabel": "iowait", "xpack.metricsData.assetDetails.formulas.cpuUsage.irqLabel": "irq", "xpack.metricsData.assetDetails.formulas.cpuUsage.niceLabel": "nice", @@ -44944,45 +44941,25 @@ "xpack.metricsData.assetDetails.formulas.cpuUsage.stealLabel": "steal", "xpack.metricsData.assetDetails.formulas.cpuUsage.systemLabel": "system", "xpack.metricsData.assetDetails.formulas.cpuUsage.userLabel": "用户", - "xpack.metricsData.assetDetails.formulas.diskIORead": "磁盘读取 IOPS", - "xpack.metricsData.assetDetails.formulas.diskIOWrite": "磁盘写入 IOPS", - "xpack.metricsData.assetDetails.formulas.diskReadThroughput": "磁盘读取吞吐量", - "xpack.metricsData.assetDetails.formulas.diskSpaceAvailability": "磁盘空间可用性", - "xpack.metricsData.assetDetails.formulas.diskSpaceAvailable": "可用磁盘空间", - "xpack.metricsData.assetDetails.formulas.diskUsage": "磁盘使用率", - "xpack.metricsData.assetDetails.formulas.diskWriteThroughput": "磁盘写入吞吐量", "xpack.metricsData.assetDetails.formulas.hostCount.hostsLabel": "主机", "xpack.metricsData.assetDetails.formulas.kubernetes.capacity": "容量", "xpack.metricsData.assetDetails.formulas.kubernetes.used": "已使用", - "xpack.metricsData.assetDetails.formulas.load15m": "负载(15 分钟)", - "xpack.metricsData.assetDetails.formulas.load1m": "负载(1 分钟)", - "xpack.metricsData.assetDetails.formulas.load5m": "负载(5 分钟)", "xpack.metricsData.assetDetails.formulas.logRate": "日志速率", - "xpack.metricsData.assetDetails.formulas.memoryFree": "可用内存", - "xpack.metricsData.assetDetails.formulas.memoryUsage": "内存利用率", "xpack.metricsData.assetDetails.formulas.metric.label.cache": "缓存", "xpack.metricsData.assetDetails.formulas.metric.label.free": "可用", "xpack.metricsData.assetDetails.formulas.metric.label.used": "已使用", - "xpack.metricsData.assetDetails.formulas.normalizedLoad1m": "标准化负载", "xpack.metricsData.assetDetails.formulas.rx": "网络入站数据 (RX)", "xpack.metricsData.assetDetails.formulas.tx": "网络出站数据 (TX)", - "xpack.metricsData.assetDetails.metricsCharts.cpuUsage": "CPU 使用率", - "xpack.metricsData.assetDetails.metricsCharts.diskIOPS": "磁盘 IOPS", - "xpack.metricsData.assetDetails.metricsCharts.diskThroughput": "磁盘吞吐量", "xpack.metricsData.assetDetails.metricsCharts.diskUsage.label.used": "已使用", - "xpack.metricsData.assetDetails.metricsCharts.diskUsageByMountingPoint": "磁盘使用率(按装载点)", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeCpuCapacity": "节点 CPU 容量", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeDiskCapacity": "节点磁盘容量", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodeMemoryCapacity": "节点内存容量", "xpack.metricsData.assetDetails.metricsCharts.kubernetes.nodePodCapacity": "节点 Pod 容量", - "xpack.metricsData.assetDetails.metricsCharts.load": "加载", - "xpack.metricsData.assetDetails.metricsCharts.memoryUsage": "内存利用率", "xpack.metricsData.assetDetails.metricsCharts.metric.label.cache": "缓存", "xpack.metricsData.assetDetails.metricsCharts.metric.label.free": "可用", "xpack.metricsData.assetDetails.metricsCharts.metric.label.read": "读取", "xpack.metricsData.assetDetails.metricsCharts.metric.label.used": "已使用", "xpack.metricsData.assetDetails.metricsCharts.metric.label.write": "写入", - "xpack.metricsData.assetDetails.metricsCharts.network": "网络", "xpack.metricsData.assetDetails.metricsCharts.network.label.rx": "入站 (RX)", "xpack.metricsData.assetDetails.metricsCharts.network.label.tx": "出站 (TX)", "xpack.metricsData.hostsPage.goToMetricsSettings": "检查设置", diff --git a/x-pack/test/api_integration/apis/asset_manager/tests/containers.ts b/x-pack/test/api_integration/apis/asset_manager/tests/containers.ts index d71f7a1c7f951..ea1b7120238b0 100644 --- a/x-pack/test/api_integration/apis/asset_manager/tests/containers.ts +++ b/x-pack/test/api_integration/apis/asset_manager/tests/containers.ts @@ -23,8 +23,8 @@ export default function ({ getService }: FtrProviderContext) { await synthtrace.clean(); }); - it('should return container assets', async () => { - await synthtrace.index(generateContainersData({ from, to, count: 5 })); + it('should return docker container assets', async () => { + await synthtrace.index(generateDockerContainersData({ from, to, count: 5 })); const response = await supertest .get(routePaths.GET_CONTAINERS) @@ -38,8 +38,8 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body.containers.length).to.equal(5); }); - it('should return a specific container asset by EAN', async () => { - await synthtrace.index(generateContainersData({ from, to, count: 5 })); + it('should return a specific docker container asset by EAN', async () => { + await synthtrace.index(generateDockerContainersData({ from, to, count: 5 })); const testEan = 'container:container-id-1'; const response = await supertest @@ -56,8 +56,68 @@ export default function ({ getService }: FtrProviderContext) { expect(response.body.containers[0]['asset.ean']).to.equal(testEan); }); - it('should return a filtered list of container assets by ID wildcard pattern', async () => { - await synthtrace.index(generateContainersData({ from, to, count: 15 })); + it('should return a filtered list of docker container assets by ID wildcard pattern', async () => { + await synthtrace.index(generateDockerContainersData({ from, to, count: 15 })); + const testIdPattern = '*id-1*'; + + const response = await supertest + .get(routePaths.GET_CONTAINERS) + .query({ + from, + to, + stringFilters: JSON.stringify({ id: testIdPattern }), + }) + .expect(200); + + expect(response.body).to.have.property('containers'); + expect(response.body.containers.length).to.equal(6); + + const ids = response.body.containers.map((result: Asset) => result['asset.id']); + + expect(ids).to.eql([ + 'container-id-1', + 'container-id-10', + 'container-id-11', + 'container-id-12', + 'container-id-13', + 'container-id-14', + ]); + }); + + it('should return k8s container assets', async () => { + await synthtrace.index(generateK8sContainersData({ from, to, count: 5 })); + const response = await supertest + .get(routePaths.GET_CONTAINERS) + .query({ + from, + to, + }) + .expect(200); + + expect(response.body).to.have.property('containers'); + expect(response.body.containers.length).to.equal(5); + }); + + it('should return a specific k8s container asset by EAN', async () => { + await synthtrace.index(generateK8sContainersData({ from, to, count: 5 })); + const testEan = 'container:container-id-1'; + + const response = await supertest + .get(routePaths.GET_CONTAINERS) + .query({ + from, + to, + stringFilters: JSON.stringify({ ean: testEan }), + }) + .expect(200); + + expect(response.body).to.have.property('containers'); + expect(response.body.containers.length).to.equal(1); + expect(response.body.containers[0]['asset.ean']).to.equal(testEan); + }); + + it('should return a filtered list of k8s container assets by ID wildcard pattern', async () => { + await synthtrace.index(generateK8sContainersData({ from, to, count: 15 })); const testIdPattern = '*id-1*'; const response = await supertest @@ -86,7 +146,30 @@ export default function ({ getService }: FtrProviderContext) { }); } -function generateContainersData({ +function generateDockerContainersData({ + from, + to, + count = 1, +}: { + from: string; + to: string; + count: number; +}) { + const range = timerange(from, to); + + const containers = Array(count) + .fill(0) + .map((_, idx) => infra.dockerContainer(`container-id-${idx}`)); + + return range + .interval('1m') + .rate(1) + .generator((timestamp) => + containers.map((container) => container.metrics().timestamp(timestamp)) + ); +} + +function generateK8sContainersData({ from, to, count = 1, @@ -100,7 +183,7 @@ function generateContainersData({ const containers = Array(count) .fill(0) .map((_, idx) => - infra.container(`container-id-${idx}`, `container-uid-${idx + 1000}`, `node-name-${idx}`) + infra.k8sContainer(`container-id-${idx}`, `container-uid-${idx + 1000}`, `node-name-${idx}`) ); return range diff --git a/x-pack/test/common/services/index.ts b/x-pack/test/common/services/index.ts index a613fb8899c6c..b5045fd94f8e5 100644 --- a/x-pack/test/common/services/index.ts +++ b/x-pack/test/common/services/index.ts @@ -11,6 +11,7 @@ import { InfraLogViewsServiceProvider } from './infra_log_views'; import { SpacesServiceProvider } from './spaces'; import { BsearchSecureService } from './bsearch_secure'; import { ApmSynthtraceKibanaClientProvider } from './apm_synthtrace_kibana_client'; +import { InfraSynthtraceKibanaClientProvider } from './infra_synthtrace_kibana_client'; export const services = { ...kibanaCommonServices, @@ -19,4 +20,5 @@ export const services = { spaces: SpacesServiceProvider, secureBsearch: BsearchSecureService, apmSynthtraceKibanaClient: ApmSynthtraceKibanaClientProvider, + infraSynthtraceKibanaClient: InfraSynthtraceKibanaClientProvider, }; diff --git a/x-pack/test/common/services/infra_synthtrace_kibana_client.ts b/x-pack/test/common/services/infra_synthtrace_kibana_client.ts new file mode 100644 index 0000000000000..5a8a3c4e06578 --- /dev/null +++ b/x-pack/test/common/services/infra_synthtrace_kibana_client.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import url from 'url'; +import { kbnTestConfig } from '@kbn/test'; +import { InfraSynthtraceKibanaClient, createLogger, LogLevel } from '@kbn/apm-synthtrace'; + +const getKibanaServerUrlWithAuth = () => { + const kibanaServerUrl = url.format(kbnTestConfig.getUrlParts() as url.UrlObject); + const kibanaServerUrlWithAuth = url + .format({ + ...url.parse(kibanaServerUrl), + auth: `elastic:${kbnTestConfig.getUrlParts().password}`, + }) + .slice(0, -1); + return kibanaServerUrlWithAuth; +}; + +export function InfraSynthtraceKibanaClientProvider() { + const kibanaServerUrlWithAuth = getKibanaServerUrlWithAuth(); + const target = kibanaServerUrlWithAuth; + const logger = createLogger(LogLevel.debug); + const username = 'elastic'; + const password = kbnTestConfig.getUrlParts().password || 'changeme'; + const kibanaClient = new InfraSynthtraceKibanaClient({ + target, + logger, + username, + password, + }); + + return kibanaClient; +} diff --git a/x-pack/test/common/utils/synthtrace/infra_es_client.ts b/x-pack/test/common/utils/synthtrace/infra_es_client.ts new file mode 100644 index 0000000000000..7e39942a9a46c --- /dev/null +++ b/x-pack/test/common/utils/synthtrace/infra_es_client.ts @@ -0,0 +1,17 @@ +/* + * 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 { Client } from '@elastic/elasticsearch'; +import { InfraSynthtraceEsClient, createLogger, LogLevel } from '@kbn/apm-synthtrace'; + +export async function getInfraSynthtraceEsClient(client: Client) { + return new InfraSynthtraceEsClient({ + client, + logger: createLogger(LogLevel.info), + refreshAfterIndex: true, + }); +} diff --git a/x-pack/test/functional/apps/infra/constants.ts b/x-pack/test/functional/apps/infra/constants.ts index fcead1cf9dc26..4c1d0d29a6ca4 100644 --- a/x-pack/test/functional/apps/infra/constants.ts +++ b/x-pack/test/functional/apps/infra/constants.ts @@ -51,7 +51,11 @@ export const ML_JOB_IDS = [ export const HOSTS_LINK_LOCAL_STORAGE_KEY = 'inventoryUI:hostsLinkClicked'; export const INVENTORY_PATH = 'metrics/inventory'; -export const NODE_DETAILS_PATH = 'detail/host'; +export const NODE_DETAILS_PATH = 'detail'; export const HOSTS_VIEW_PATH = 'metrics/hosts'; export const DATE_PICKER_FORMAT = 'MMM D, YYYY @ HH:mm:ss.SSS'; + +export const DATE_WITH_DOCKER_DATA_FROM = '2023-03-28T18:20:00.000Z'; +export const DATE_WITH_DOCKER_DATA_TO = '2023-03-28T18:21:00.000Z'; +export const DATE_WITH_DOCKER_DATA = '03/28/2023 6:20:59 PM'; diff --git a/x-pack/test/functional/apps/infra/helpers.ts b/x-pack/test/functional/apps/infra/helpers.ts index 51356acadf146..2ddabf314390f 100644 --- a/x-pack/test/functional/apps/infra/helpers.ts +++ b/x-pack/test/functional/apps/infra/helpers.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import { apm, timerange, infra } from '@kbn/apm-synthtrace-client'; const SERVICE_PREFIX = 'service'; // generates traces, metrics for services @@ -46,3 +46,26 @@ export function generateAddServicesToExistingHost({ ) ); } + +export function generateDockerContainersData({ + from, + to, + count = 1, +}: { + from: string; + to: string; + count?: number; +}) { + const range = timerange(from, to); + + const containers = Array(count) + .fill(0) + .map((_, idx) => infra.dockerContainer(`container-id-${idx}`)); + + return range + .interval('30s') + .rate(1) + .generator((timestamp) => + containers.flatMap((container) => container.metrics().timestamp(timestamp)) + ); +} diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts index df22e6ac8ccba..5e0dd1d36d50f 100644 --- a/x-pack/test/functional/apps/infra/home_page.ts +++ b/x-pack/test/functional/apps/infra/home_page.ts @@ -8,17 +8,25 @@ import expect from '@kbn/expect'; import { parse } from 'url'; import { KUBERNETES_TOUR_STORAGE_KEY } from '@kbn/infra-plugin/public/pages/metrics/inventory_view/components/kubernetes_tour'; +import { InfraSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { FtrProviderContext } from '../../ftr_provider_context'; import { DATES, INVENTORY_PATH } from './constants'; +import { generateDockerContainersData } from './helpers'; +import { getInfraSynthtraceEsClient } from '../../../common/utils/synthtrace/infra_es_client'; const DATE_WITH_DATA = DATES.metricsAndLogs.hosts.withData; const DATE_WITHOUT_DATA = DATES.metricsAndLogs.hosts.withoutData; const DATE_WITH_POD_WITH_DATA = DATES.metricsAndLogs.pods.withData; +const DATE_WITH_DOCKER_DATA_FROM = '2023-03-28T18:20:00.000Z'; +const DATE_WITH_DOCKER_DATA_TO = '2023-03-28T18:21:00.000Z'; +const DATE_WITH_DOCKER_DATA = '03/28/2023 6:20:00 PM'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); const browser = getService('browser'); const retry = getService('retry'); + const esClient = getService('es'); + const infraSynthtraceKibanaClient = getService('infraSynthtraceKibanaClient'); const pageObjects = getPageObjects([ 'common', 'header', @@ -355,7 +363,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await returnTo(INVENTORY_PATH); }); - it('Should redirect to Node Details page', async () => { + it('Should redirect to Pod Details page', async () => { await pageObjects.infraHome.goToPods(); await pageObjects.infraHome.goToTime(DATE_WITH_POD_WITH_DATA); await pageObjects.infraHome.clickOnFirstNode(); @@ -370,6 +378,41 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await returnTo(INVENTORY_PATH); }); + + describe('Redirect to Container Details page', () => { + let synthEsClient: InfraSynthtraceEsClient; + before(async () => { + const version = await infraSynthtraceKibanaClient.fetchLatestSystemPackageVersion(); + await infraSynthtraceKibanaClient.installSystemPackage(version); + synthEsClient = await getInfraSynthtraceEsClient(esClient); + await synthEsClient.index( + generateDockerContainersData({ + from: DATE_WITH_DOCKER_DATA_FROM, + to: DATE_WITH_DOCKER_DATA_TO, + count: 5, + }) + ); + }); + + after(async () => { + return await synthEsClient.clean(); + }); + it('Should redirect to Container Details page', async () => { + await pageObjects.infraHome.goToContainer(); + await pageObjects.infraHome.goToTime(DATE_WITH_DOCKER_DATA); + await pageObjects.infraHome.clickOnFirstNode(); + await pageObjects.infraHome.clickOnGoToNodeDetails(); + + await retry.try(async () => { + const documentTitle = await browser.getTitle(); + expect(documentTitle).to.contain( + 'container-id-4 - Inventory - Infrastructure - Observability - Elastic' + ); + }); + + await returnTo(INVENTORY_PATH); + }); + }); }); }); diff --git a/x-pack/test/functional/apps/infra/node_details.ts b/x-pack/test/functional/apps/infra/node_details.ts index 88a8dfe9b2aa2..47792ec4bb2ba 100644 --- a/x-pack/test/functional/apps/infra/node_details.ts +++ b/x-pack/test/functional/apps/infra/node_details.ts @@ -7,14 +7,26 @@ import moment from 'moment'; import expect from '@kbn/expect'; -import { enableInfrastructureProfilingIntegration } from '@kbn/observability-plugin/common'; +import { InfraSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import { + enableInfrastructureContainerAssetView, + enableInfrastructureProfilingIntegration, +} from '@kbn/observability-plugin/common'; import { ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, ALERT_STATUS_UNTRACKED, } from '@kbn/rule-data-utils'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { DATES, NODE_DETAILS_PATH, DATE_PICKER_FORMAT } from './constants'; +import { + DATES, + NODE_DETAILS_PATH, + DATE_PICKER_FORMAT, + DATE_WITH_DOCKER_DATA_FROM, + DATE_WITH_DOCKER_DATA_TO, +} from './constants'; +import { getInfraSynthtraceEsClient } from '../../../common/utils/synthtrace/infra_es_client'; +import { generateDockerContainersData } from './helpers'; const START_HOST_ALERTS_DATE = moment.utc(DATES.metricsAndLogs.hosts.min); const END_HOST_ALERTS_DATE = moment.utc(DATES.metricsAndLogs.hosts.max); @@ -32,6 +44,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const browser = getService('browser'); const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); + const infraSynthtraceKibanaClient = getService('infraSynthtraceKibanaClient'); + const esClient = getService('es'); const retry = getService('retry'); const testSubjects = getService('testSubjects'); const pageObjects = getPageObjects([ @@ -50,10 +64,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { return queryParams.toString(); }; - const navigateToNodeDetails = async (assetId: string, assetName: string) => { + const navigateToNodeDetails = async (assetId: string, assetName: string, assetType: string) => { await pageObjects.common.navigateToUrlWithBrowserHistory( 'infraOps', - `/${NODE_DETAILS_PATH}/${assetId}`, + `/${NODE_DETAILS_PATH}/${assetType}/${assetId}`, getNodeDetailsUrl(assetName), { insertTimestamp: false, @@ -79,6 +93,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.header.waitUntilLoadingHasFinished(); }; + const setInfrastructureContainerAssetViewUiSetting = async (value: boolean = true) => { + await kibanaServer.uiSettings.update({ [enableInfrastructureContainerAssetView]: value }); + await browser.refresh(); + await pageObjects.header.waitUntilLoadingHasFinished(); + }; + describe('Node Details', () => { describe('#With Asset Details', () => { before(async () => { @@ -90,7 +110,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ]); await browser.setWindowSize(1600, 1200); - await navigateToNodeDetails('Jennys-MBP.fritz.box', 'Jennys-MBP.fritz.box'); + await navigateToNodeDetails('Jennys-MBP.fritz.box', 'Jennys-MBP.fritz.box', 'host'); await pageObjects.header.waitUntilLoadingHasFinished(); }); @@ -102,7 +122,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ]); }); - describe('#Date picker', () => { + describe('#Date picker: host', () => { before(async () => { await pageObjects.assetDetails.clickOverviewTab(); @@ -247,7 +267,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const ALL_ALERTS = ACTIVE_ALERTS + RECOVERED_ALERTS; const COLUMNS = 11; before(async () => { - await navigateToNodeDetails('demo-stack-apache-01', 'demo-stack-apache-01'); + await navigateToNodeDetails('demo-stack-apache-01', 'demo-stack-apache-01', 'host'); await pageObjects.header.waitUntilLoadingHasFinished(); await pageObjects.timePicker.setAbsoluteRange( @@ -259,7 +279,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); after(async () => { - await navigateToNodeDetails('Jennys-MBP.fritz.box', 'Jennys-MBP.fritz.box'); + await navigateToNodeDetails('Jennys-MBP.fritz.box', 'Jennys-MBP.fritz.box', 'host'); await pageObjects.header.waitUntilLoadingHasFinished(); await pageObjects.timePicker.setAbsoluteRange( @@ -482,7 +502,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Host with alerts and no processes', () => { before(async () => { - await navigateToNodeDetails('demo-stack-mysql-01', 'demo-stack-mysql-01'); + await navigateToNodeDetails('demo-stack-mysql-01', 'demo-stack-mysql-01', 'host'); await pageObjects.timePicker.setAbsoluteRange( START_HOST_ALERTS_DATE.format(DATE_PICKER_FORMAT), END_HOST_ALERTS_DATE.format(DATE_PICKER_FORMAT) @@ -516,7 +536,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('#With Kubernetes section', () => { before(async () => { - await navigateToNodeDetails('demo-stack-kubernetes-01', 'demo-stack-kubernetes-01'); + await navigateToNodeDetails( + 'demo-stack-kubernetes-01', + 'demo-stack-kubernetes-01', + 'host' + ); await pageObjects.header.waitUntilLoadingHasFinished(); }); @@ -597,6 +621,57 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); }); + + describe('#Asset Type: container', () => { + let synthEsClient: InfraSynthtraceEsClient; + before(async () => { + const version = await infraSynthtraceKibanaClient.fetchLatestSystemPackageVersion(); + await infraSynthtraceKibanaClient.installSystemPackage(version); + synthEsClient = await getInfraSynthtraceEsClient(esClient); + await synthEsClient.index( + generateDockerContainersData({ + from: DATE_WITH_DOCKER_DATA_FROM, + to: DATE_WITH_DOCKER_DATA_TO, + count: 1, + }) + ); + }); + + after(async () => { + await synthEsClient.clean(); + }); + + describe('when container asset view is disabled', () => { + it('should show old view of container details', async () => { + await setInfrastructureContainerAssetViewUiSetting(false); + await navigateToNodeDetails('container-id-0', 'container-id-0', 'container'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.find('metricsEmptyViewState'); + }); + }); + + describe('when container asset view is enabled', () => { + it('should show asset container details page', async () => { + await setInfrastructureContainerAssetViewUiSetting(true); + await navigateToNodeDetails('container-id-0', 'container-id-0', 'container'); + await pageObjects.header.waitUntilLoadingHasFinished(); + + await pageObjects.assetDetails.getOverviewTab(); + }); + + [ + { metric: 'cpu', chartsCount: 1 }, + { metric: 'memory', chartsCount: 1 }, + ].forEach(({ metric, chartsCount }) => { + it(`should render ${chartsCount} ${metric} chart(s) in the Metrics section`, async () => { + const charts = await pageObjects.assetDetails.getOverviewTabDockerMetricCharts( + metric + ); + expect(charts.length).to.equal(chartsCount); + }); + }); + }); + }); }); }); }; diff --git a/x-pack/test/functional/page_objects/asset_details.ts b/x-pack/test/functional/page_objects/asset_details.ts index bbb5ebead305b..32efd9f315ad2 100644 --- a/x-pack/test/functional/page_objects/asset_details.ts +++ b/x-pack/test/functional/page_objects/asset_details.ts @@ -30,6 +30,10 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) { return testSubjects.click('infraAssetDetailsOverviewTab'); }, + async getOverviewTab() { + return testSubjects.find('infraAssetDetailsOverviewTab'); + }, + async getAssetDetailsKPITileValue(type: string) { const element = await testSubjects.find(`infraAssetDetailsKPI${type}`); const div = await element.findByClassName('echMetricText__value'); @@ -103,6 +107,15 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) { return section.findAllByCssSelector('[data-test-subj*="infraAssetDetailsMetricChart"]'); }, + async getOverviewTabDockerMetricCharts(metric: string) { + const container = await testSubjects.find('infraAssetDetailsOverviewTabContent'); + const section = await container.findByTestSubject( + `infraAssetDetailsDockerChartsSection${metric}` + ); + + return section.findAllByCssSelector('[data-test-subj*="infraAssetDetailsMetricChart"]'); + }, + async getOverviewTabKubernetesMetricCharts() { const container = await testSubjects.find('infraAssetDetailsOverviewTabContent'); const section = await container.findByTestSubject(`infraAssetDetailsKubernetesChartsSection`); diff --git a/x-pack/test/functional/page_objects/infra_home_page.ts b/x-pack/test/functional/page_objects/infra_home_page.ts index 68df8c69784a9..f112e76ee809a 100644 --- a/x-pack/test/functional/page_objects/infra_home_page.ts +++ b/x-pack/test/functional/page_objects/infra_home_page.ts @@ -212,10 +212,10 @@ export function InfraHomePageProvider({ getService, getPageObjects }: FtrProvide return testSubjects.click('goToPods'); }, - async goToDocker() { + async goToContainer() { await testSubjects.click('openInventorySwitcher'); await testSubjects.find('goToHost'); - return testSubjects.click('goToDocker'); + return testSubjects.click('goToContainer'); }, async goToSettings() { From e53ff4411bd3651591dec0ee74be9e1b576ce791 Mon Sep 17 00:00:00 2001 From: Brad White Date: Thu, 16 May 2024 09:13:57 -0600 Subject: [PATCH 03/71] [FIPS / CI] Fix ES ML startup issues, UUID permissions, FTR tests status, OpenSSL target. Switch to Ubuntu (#182295) Closes elastic/kibana-operations#96 [new pipeline run](https://buildkite.com/elastic/kibana-fips/builds/28) [old pipeline run](https://buildkite.com/elastic/kibana-fips/builds/24) - Fixes OpenSSL compilation so that it does not overwrite the OS version and break OS functionality. - Fixes issue where ES would not start due to ML pipe being unable to write to Vagrant synced folder. - Reenabled ML in FIPS pipeline - Fixes permission where Kibana could not write UUID file. - Fixes smoke test exit code not reporting correctly. - Fixes Buildkite annotation for failed tests [example](https://buildkite.com/elastic/kibana-fips/builds/23). - Switches the base VM image from RHEL9 to Ubuntu due to RHEL9 subscription requirements to install repo packages. - This blocked installing Chrome, causing tests using Webdriver to fail. --------- Co-authored-by: Dzmitry Lemechko --- .buildkite/scripts/steps/fips/smoke_test.sh | 4 +- .../scripts/steps/package_testing/test.sh | 6 +- packages/kbn-es/src/cluster.ts | 18 ++-- packages/kbn-es/src/cluster_exec_options.ts | 2 + .../kbn-es/src/install/install_archive.ts | 16 +++- packages/kbn-es/src/install/types.ts | 2 + .../src/integration_tests/cluster.test.ts | 22 +++-- packages/kbn-test/src/es/test_es_cluster.ts | 28 +++---- test/package/Vagrantfile | 9 +- test/package/fips.yml | 4 +- .../roles/assert_fips_enabled/tasks/main.yml | 4 +- .../roles/install_kibana_fips/tasks/main.yml | 83 ++++++++++++++----- test/package/templates/fips/nodejs.cnf | 2 +- 13 files changed, 126 insertions(+), 74 deletions(-) diff --git a/.buildkite/scripts/steps/fips/smoke_test.sh b/.buildkite/scripts/steps/fips/smoke_test.sh index 7026c7bab941c..5c70e8c2057df 100755 --- a/.buildkite/scripts/steps/fips/smoke_test.sh +++ b/.buildkite/scripts/steps/fips/smoke_test.sh @@ -36,9 +36,9 @@ for config in "${configs[@]}"; do echo "^^^ +++" if [[ "$failedConfigs" ]]; then - failedConfigs="${failedConfigs}"$'\n'"$config" + failedConfigs="${failedConfigs}"$'\n'"- ${config}" else - failedConfigs="$config" + failedConfigs="### Failed FTR Configs"$'\n'"- ${config}" fi fi done diff --git a/.buildkite/scripts/steps/package_testing/test.sh b/.buildkite/scripts/steps/package_testing/test.sh index 99750529815c8..4917932e05228 100755 --- a/.buildkite/scripts/steps/package_testing/test.sh +++ b/.buildkite/scripts/steps/package_testing/test.sh @@ -58,12 +58,16 @@ trap "echoKibanaLogs" EXIT if [[ "$TEST_PACKAGE" == "fips" ]]; then set +e vagrant ssh $TEST_PACKAGE -t -c "/home/vagrant/kibana/.buildkite/scripts/steps/fips/smoke_test.sh" + exitCode=$? + vagrant ssh $TEST_PACKAGE -t -c "cat /home/vagrant/ftr_failed_configs 2>/dev/null" >ftr_failed_configs set -e if [ -s ftr_failed_configs ]; then - buildkite-agent meta-data set "ftr-failed-configs" <./ftr_failed_configs + cat ftr_failed_configs | buildkite-agent annotate --style "error" fi + + exit $exitCode else vagrant provision "$TEST_PACKAGE" diff --git a/packages/kbn-es/src/cluster.ts b/packages/kbn-es/src/cluster.ts index e2ddf43f32dc7..65e232a9bbd48 100644 --- a/packages/kbn-es/src/cluster.ts +++ b/packages/kbn-es/src/cluster.ts @@ -89,8 +89,9 @@ export class Cluster { async installSource(options: InstallSourceOptions) { this.log.info(chalk.bold('Installing from source')); return await this.log.indent(4, async () => { - const { installPath } = await installSource({ log: this.log, ...options }); - return { installPath }; + const { installPath, disableEsTmpDir } = await installSource({ log: this.log, ...options }); + + return { installPath, disableEsTmpDir }; }); } @@ -115,12 +116,12 @@ export class Cluster { async installSnapshot(options: InstallSnapshotOptions) { this.log.info(chalk.bold('Installing from snapshot')); return await this.log.indent(4, async () => { - const { installPath } = await installSnapshot({ + const { installPath, disableEsTmpDir } = await installSnapshot({ log: this.log, ...options, }); - return { installPath }; + return { installPath, disableEsTmpDir }; }); } @@ -130,12 +131,12 @@ export class Cluster { async installArchive(archivePath: string, options?: InstallArchiveOptions) { this.log.info(chalk.bold('Installing from an archive')); return await this.log.indent(4, async () => { - const { installPath } = await installArchive(archivePath, { + const { installPath, disableEsTmpDir } = await installArchive(archivePath, { log: this.log, ...(options || {}), }); - return { installPath }; + return { installPath, disableEsTmpDir }; }); } @@ -317,6 +318,7 @@ export class Cluster { skipReadyCheck, readyTimeout, writeLogsToPath, + disableEsTmpDir, ...options } = opts; @@ -389,7 +391,9 @@ export class Cluster { this.process = execa(ES_BIN, args, { cwd: installPath, env: { - ...(installPath ? { ES_TMPDIR: path.resolve(installPath, 'ES_TMPDIR') } : {}), + ...(installPath && !disableEsTmpDir + ? { ES_TMPDIR: path.resolve(installPath, 'ES_TMPDIR') } + : {}), ...process.env, JAVA_HOME: '', // By default, we want to always unset JAVA_HOME so that the bundled JDK will be used ES_JAVA_OPTS: esJavaOpts, diff --git a/packages/kbn-es/src/cluster_exec_options.ts b/packages/kbn-es/src/cluster_exec_options.ts index 30aeabbab903a..362af62c57954 100644 --- a/packages/kbn-es/src/cluster_exec_options.ts +++ b/packages/kbn-es/src/cluster_exec_options.ts @@ -17,4 +17,6 @@ export interface EsClusterExecOptions { readyTimeout?: number; onEarlyExit?: (msg: string) => void; writeLogsToPath?: string; + /** Disable creating a temp directory, allowing ES to write to OS's /tmp directory */ + disableEsTmpDir?: boolean; } diff --git a/packages/kbn-es/src/install/install_archive.ts b/packages/kbn-es/src/install/install_archive.ts index 78e200d4d47fb..2bfef3ab7abef 100644 --- a/packages/kbn-es/src/install/install_archive.ts +++ b/packages/kbn-es/src/install/install_archive.ts @@ -40,6 +40,7 @@ export async function installArchive(archive: string, options?: InstallArchiveOp installPath = path.resolve(basePath, path.basename(archive, '.tar.gz')), log = defaultLog, esArgs = [], + disableEsTmpDir = process.env.FTR_DISABLE_ES_TMPDIR?.toLowerCase() === 'true', } = options || {}; let dest = archive; @@ -62,9 +63,16 @@ export async function installArchive(archive: string, options?: InstallArchiveOp }); log.info('extracted to %s', chalk.bold(installPath)); - const tmpdir = path.resolve(installPath, 'ES_TMPDIR'); - fs.mkdirSync(tmpdir, { recursive: true }); - log.info('created %s', chalk.bold(tmpdir)); + /** + * If we're running inside a Vagrant VM, and this is running in a synced folder, + * ES will fail to start due to ML being unable to write a pipe in the synced folder. + * Disabling allows ES to write to the OS's /tmp directory. + */ + if (!disableEsTmpDir) { + const tmpdir = path.resolve(installPath, 'ES_TMPDIR'); + fs.mkdirSync(tmpdir, { recursive: true }); + log.info('created %s', chalk.bold(tmpdir)); + } // starting in 6.3, security is disabled by default. Since we bootstrap // the keystore, we can enable security ourselves. @@ -76,7 +84,7 @@ export async function installArchive(archive: string, options?: InstallArchiveOp ...parseSettings(esArgs, { filter: SettingsFilter.SecureOnly }), ]); - return { installPath }; + return { installPath, disableEsTmpDir }; } /** diff --git a/packages/kbn-es/src/install/types.ts b/packages/kbn-es/src/install/types.ts index e4b750c0ec472..6217f5b93c7f6 100644 --- a/packages/kbn-es/src/install/types.ts +++ b/packages/kbn-es/src/install/types.ts @@ -40,4 +40,6 @@ export interface InstallArchiveOptions { installPath?: string; log?: ToolingLog; esArgs?: string[]; + /** Disable creating a temp directory, allowing ES to write to OS's /tmp directory */ + disableEsTmpDir?: boolean; } diff --git a/packages/kbn-es/src/integration_tests/cluster.test.ts b/packages/kbn-es/src/integration_tests/cluster.test.ts index e8246558732c0..0ef9803539fdd 100644 --- a/packages/kbn-es/src/integration_tests/cluster.test.ts +++ b/packages/kbn-es/src/integration_tests/cluster.test.ts @@ -179,13 +179,13 @@ describe('#downloadSnapshot()', () => { }); describe('#installSource()', () => { - test('awaits installSource() promise and returns { installPath }', async () => { + test('awaits installSource() promise and returns { installPath, disableEsTmpDir }', async () => { let resolveInstallSource: Function; installSourceMock.mockImplementationOnce( () => new Promise((resolve) => { resolveInstallSource = () => { - resolve({ installPath: 'foo' }); + resolve({ installPath: 'foo', disableEsTmpDir: false }); }; }) ); @@ -196,11 +196,12 @@ describe('#installSource()', () => { resolveInstallSource!(); await expect(ensureResolve(promise, 'installSource()')).resolves.toEqual({ installPath: 'foo', + disableEsTmpDir: false, }); }); test('passes through all options+log to installSource()', async () => { - installSourceMock.mockResolvedValue({ installPath: 'foo' }); + installSourceMock.mockResolvedValue({ installPath: 'foo', disableEsTmpDir: false }); const options: InstallSourceOptions = { sourcePath: 'bar', license: 'trial', @@ -228,13 +229,13 @@ describe('#installSource()', () => { }); describe('#installSnapshot()', () => { - test('awaits installSnapshot() promise and returns { installPath }', async () => { + test('awaits installSnapshot() promise and returns { installPath, disableEsTmpDir }', async () => { let resolveInstallSnapshot: Function; installSnapshotMock.mockImplementationOnce( () => new Promise((resolve) => { resolveInstallSnapshot = () => { - resolve({ installPath: 'foo' }); + resolve({ installPath: 'foo', disableEsTmpDir: false }); }; }) ); @@ -245,11 +246,12 @@ describe('#installSnapshot()', () => { resolveInstallSnapshot!(); await expect(ensureResolve(promise, 'installSnapshot()')).resolves.toEqual({ installPath: 'foo', + disableEsTmpDir: false, }); }); test('passes through all options+log to installSnapshot()', async () => { - installSnapshotMock.mockResolvedValue({ installPath: 'foo' }); + installSnapshotMock.mockResolvedValue({ installPath: 'foo', disableEsTmpDir: false }); const options: InstallSnapshotOptions = { version: '8.10.0', license: 'trial', @@ -278,13 +280,13 @@ describe('#installSnapshot()', () => { }); describe('#installArchive()', () => { - test('awaits installArchive() promise and returns { installPath }', async () => { + test('awaits installArchive() promise and returns { installPath, disableEsTmpDir }', async () => { let resolveInstallArchive: Function; installArchiveMock.mockImplementationOnce( () => new Promise((resolve) => { resolveInstallArchive = () => { - resolve({ installPath: 'foo' }); + resolve({ installPath: 'foo', disableEsTmpDir: false }); }; }) ); @@ -295,11 +297,12 @@ describe('#installArchive()', () => { resolveInstallArchive!(); await expect(ensureResolve(promise, 'installArchive()')).resolves.toEqual({ installPath: 'foo', + disableEsTmpDir: false, }); }); test('passes through all options+log to installArchive()', async () => { - installArchiveMock.mockResolvedValue({ installPath: 'foo' }); + installArchiveMock.mockResolvedValue({ installPath: 'foo', disableEsTmpDir: true }); const options: InstallArchiveOptions = { license: 'trial', password: 'changeme', @@ -307,6 +310,7 @@ describe('#installArchive()', () => { installPath: 'someInstallPath', esArgs: ['foo=true'], log, + disableEsTmpDir: true, }; const cluster = new Cluster({ log }); await cluster.installArchive('bar', options); diff --git a/packages/kbn-test/src/es/test_es_cluster.ts b/packages/kbn-test/src/es/test_es_cluster.ts index 12f020175fd4e..a2078a1e56e16 100644 --- a/packages/kbn-test/src/es/test_es_cluster.ts +++ b/packages/kbn-test/src/es/test_es_cluster.ts @@ -182,7 +182,6 @@ export function createTestEsCluster< } = options; const clusterName = `${CI_PARALLEL_PROCESS_PREFIX}${customClusterName}`; - const isFIPSMode = process.env.FTR_FIPS_MODE === '1'; const defaultEsArgs = [ `cluster.name=${clusterName}`, @@ -193,12 +192,7 @@ export function createTestEsCluster< : ['discovery.type=single-node']), ]; - const esArgs = assignArgs( - defaultEsArgs, - // ML has issues running in FIPS mode due to custom OpenSSL - // Remove after https://github.com/elastic/kibana-operations/issues/96 - isFIPSMode ? [...customEsArgs, 'xpack.ml.enabled=false'] : customEsArgs - ); + const esArgs = assignArgs(defaultEsArgs, customEsArgs); const config = { version: esTestConfig.getVersion(), @@ -231,22 +225,21 @@ export function createTestEsCluster< async start() { let installPath: string; + let disableEsTmpDir: boolean; // We only install once using the first node. If the cluster has // multiple nodes, they'll all share the same ESinstallation. const firstNode = this.nodes[0]; if (esFrom === 'source') { - installPath = ( - await firstNode.installSource({ - sourcePath: config.sourcePath, - license: config.license, - password: config.password, - basePath: config.basePath, - esArgs: config.esArgs, - }) - ).installPath; + ({ installPath, disableEsTmpDir } = await firstNode.installSource({ + sourcePath: config.sourcePath, + license: config.license, + password: config.password, + basePath: config.basePath, + esArgs: config.esArgs, + })); } else if (esFrom === 'snapshot') { - installPath = (await firstNode.installSnapshot(config)).installPath; + ({ installPath, disableEsTmpDir } = await firstNode.installSnapshot(config)); } else if (esFrom === 'serverless') { if (!esServerlessOptions) { throw new Error( @@ -308,6 +301,7 @@ export function createTestEsCluster< skipReadyCheck: this.nodes.length > 1 && i < this.nodes.length - 1, onEarlyExit, writeLogsToPath, + disableEsTmpDir, }); }); } diff --git a/test/package/Vagrantfile b/test/package/Vagrantfile index ea7e87d74b785..b8c24d02c8eba 100644 --- a/test/package/Vagrantfile +++ b/test/package/Vagrantfile @@ -46,14 +46,7 @@ Vagrant.configure("2") do |config| vb.memory = 4096 vb.cpus = 2 end - fips.vm.box = 'generic/rhel9' - fips.vm.provision "shell", inline: <<-SHELL - echo "export OPENSSL_MODULES=/usr/local/lib64/ossl-modules" >> /etc/profile.d/kibana-fips-env.sh - echo "export TEST_BROWSER_HEADLESS=1" >> /etc/profile.d/kibana-fips-env.sh - echo "export ES_TMPDIR=/home/vagrant/kibana/.es/tmp" >> /etc/profile.d/kibana-fips-env.sh - # Remove after https://github.com/elastic/kibana-operations/issues/96 - echo "export FTR_FIPS_MODE=1" >> /etc/profile.d/kibana-fips-env.sh - SHELL + fips.vm.box = 'ubuntu/jammy64' fips.vm.provision "ansible" do |ansible| ansible.playbook = "fips.yml" end diff --git a/test/package/fips.yml b/test/package/fips.yml index ae62b386c0cde..6682e32b0f6be 100644 --- a/test/package/fips.yml +++ b/test/package/fips.yml @@ -6,7 +6,9 @@ nvm_ver: "0.39.7" openssl_sha: "sha256:6c13d2bf38fdf31eac3ce2a347073673f5d63263398f1f69d0df4a41253e4b3e" openssl_ver: "3.0.8" + openssl_src_path: "{{ kibana_dist_path }}/openssl-{{ openssl_ver }}" + openssl_path: "{{ kibana_dist_path }}/openssl" roles: - - upgrade_yum_packages + - upgrade_apt_packages - install_kibana_fips - assert_fips_enabled diff --git a/test/package/roles/assert_fips_enabled/tasks/main.yml b/test/package/roles/assert_fips_enabled/tasks/main.yml index 936d43c49026a..74ebe283673cb 100644 --- a/test/package/roles/assert_fips_enabled/tasks/main.yml +++ b/test/package/roles/assert_fips_enabled/tasks/main.yml @@ -1,5 +1,7 @@ - name: register kibana node getFips - command: "{{ kibana_dist_path }}/node/bin/node --enable-fips --openssl-config={{ kibana_dist_path }}/config/nodejs.cnf -p 'crypto.getFips()'" + shell: + cmd: "source /home/vagrant/.profile && {{ kibana_dist_path }}/node/bin/node --enable-fips --openssl-config={{ kibana_dist_path }}/config/nodejs.cnf -p 'crypto.getFips()'" + executable: /bin/bash register: kibana_node_fips - debug: diff --git a/test/package/roles/install_kibana_fips/tasks/main.yml b/test/package/roles/install_kibana_fips/tasks/main.yml index 3bfe729f73bd1..49376106171bd 100644 --- a/test/package/roles/install_kibana_fips/tasks/main.yml +++ b/test/package/roles/install_kibana_fips/tasks/main.yml @@ -6,18 +6,37 @@ - "processor_cores" when: ansible_processor_vcpus is not defined -- name: fix /var/log permissions for kibana +- name: setup env variables + blockinfile: + path: "/home/vagrant/.profile" + block: | + export OPENSSL_MODULES=/usr/share/kibana/openssl/lib/ossl-modules + export TEST_BROWSER_HEADLESS=1 + export FTR_DISABLE_ES_TMPDIR=true + owner: vagrant + group: vagrant + mode: '0644' + +- name: add chrome apt signing key become: yes - file: - path: /var/log - state: directory - recurse: true - mode: "0777" + apt_key: + url: https://dl.google.com/linux/linux_signing_key.pub + state: present -- name: create tmp dir for ES - file: - path: "{{ kibana_src_path }}/.es/tmp" - state: directory +- name: add chrome apt repository + become: yes + apt_repository: + repo: deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main + state: present + +- name: install apt packages + become: yes + apt: + pkg: + - build-essential + - google-chrome-stable + - unzip + state: latest - name: slurp kibana node version slurp: @@ -31,7 +50,7 @@ - name: install nvm shell: chdir: "$HOME" - cmd: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v{{ nvm_ver }}/install.sh | bash + cmd: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v{{ nvm_ver }}/install.sh | PROFILE=/home/vagrant/.profile bash - name: install kibana node version shell: @@ -40,12 +59,11 @@ args: executable: /bin/bash -- name: "ensure {{ kibana_dist_path }} dir exists" +- name: "ensure {{ openssl_path }} dir exists" become: yes file: - path: "{{ kibana_dist_path }}" + path: "{{ openssl_path }}" state: directory - mode: "0777" - name: find kibana distribution find: @@ -99,35 +117,54 @@ delay: 10 get_url: url: "https://www.openssl.org/source/openssl-{{ openssl_ver }}.tar.gz" - dest: "{{ kibana_dist_path }}/openssl-{{ openssl_ver }}.tar.gz" + dest: "{{ openssl_src_path }}.tar.gz" checksum: "{{ openssl_sha }}" - name: extract OpenSSL become: yes unarchive: - src: "{{ kibana_dist_path }}/openssl-{{ openssl_ver }}.tar.gz" + src: "{{ openssl_src_path }}.tar.gz" dest: "{{ kibana_dist_path }}" remote_src: yes - name: configure OpenSSL for FIPS become: yes shell: - chdir: "{{ kibana_dist_path }}/openssl-{{ openssl_ver }}" - cmd: ./Configure enable-fips + chdir: "{{ openssl_src_path }}" + cmd: "./Configure --prefix={{ openssl_path }} --openssldir={{ openssl_path }}/ssl --libdir={{ openssl_path }}/lib enable-fips" - name: compile OpenSSL with FIPS become: yes make: - chdir: "{{ kibana_dist_path }}/openssl-{{ openssl_ver }}" + chdir: "{{ openssl_src_path }}" jobs: "{{ ansible_facts['processor_vcpus'] }}" - name: install OpenSSL with FIPS become: yes make: - chdir: "{{ kibana_dist_path }}/openssl-{{ openssl_ver }}" + chdir: "{{ openssl_src_path }}" target: install -- name: link OpenSSL package +- name: "change owner of {{ kibana_dist_path }} to vagrant" become: yes - shell: - cmd: ldconfig /usr/local/lib64/ + file: + path: "{{ kibana_dist_path }}" + owner: vagrant + group: vagrant + recurse: yes + +- name: fix /var/log permissions for kibana + become: yes + file: + path: /var/log + state: directory + recurse: true + mode: "0777" + +- name: increase vm.max_map_count for ES + become: yes + sysctl: + name: vm.max_map_count + value: '262144' + state: present + reload: yes \ No newline at end of file diff --git a/test/package/templates/fips/nodejs.cnf b/test/package/templates/fips/nodejs.cnf index bd8fece6674d7..f4f3a076975eb 100644 --- a/test/package/templates/fips/nodejs.cnf +++ b/test/package/templates/fips/nodejs.cnf @@ -9,7 +9,7 @@ ########################################################################## nodejs_conf = nodejs_init -.include /usr/local/ssl/fipsmodule.cnf +.include /usr/share/kibana/openssl/ssl/fipsmodule.cnf [nodejs_init] providers = provider_sect From 4fade3731398698acfc612a1479573c15f8f1937 Mon Sep 17 00:00:00 2001 From: elena-shostak <165678770+elena-shostak@users.noreply.github.com> Date: Thu, 16 May 2024 17:14:09 +0200 Subject: [PATCH 04/71] [Roles] Added optional role description (#183145) ## Summary 1. Added optional role description field for Save/Edit Role page. 2. Added tooltip with description for roles ComboBox that we render on the User and Role Mappings pages.
3. Updated RolesGridPage table responsive setup.
[Before] responsiveBreakpoint={false} [After] responsiveBreakpoint={true}
Before After
https://github.com/elastic/kibana/assets/165678770/7035c05b-85c6-4da0-97d3-85f6d2dbc313 ### 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] [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 - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed. [Report](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5960) - [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)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) __Fixes: https://github.com/elastic/kibana/issues/173570__ ## Release note Added optional role description field for Save/Edit Role page. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/api/role-management/get-all.asciidoc | 2 + docs/api/role-management/get.asciidoc | 1 + docs/api/role-management/put.asciidoc | 8 ++ .../role_combo_box/role_combo_box.test.tsx | 6 + .../role_combo_box/role_combo_box.tsx | 21 +++- .../roles/edit_role/edit_role_page.test.tsx | 63 ++++++++++ .../roles/edit_role/edit_role_page.tsx | 119 ++++++++++++------ .../roles/edit_role/validate_role.ts | 1 - .../roles/roles_grid/roles_grid_page.test.tsx | 22 ++++ .../roles/roles_grid/roles_grid_page.tsx | 23 +++- x-pack/test/functional/apps/security/index.ts | 1 + .../apps/security/role_description.ts | 68 ++++++++++ 12 files changed, 294 insertions(+), 41 deletions(-) create mode 100644 x-pack/test/functional/apps/security/role_description.ts diff --git a/docs/api/role-management/get-all.asciidoc b/docs/api/role-management/get-all.asciidoc index 888bf0c8a137c..56c8b2c78859b 100644 --- a/docs/api/role-management/get-all.asciidoc +++ b/docs/api/role-management/get-all.asciidoc @@ -32,6 +32,7 @@ The API returns the following: [ { "name": "my_kibana_role", + "description": "My kibana role description", "metadata" : { "version" : 1 }, @@ -55,6 +56,7 @@ The API returns the following: }, { "name": "my_admin_role", + "description": "My admin role description", "metadata" : { "version" : 1 }, diff --git a/docs/api/role-management/get.asciidoc b/docs/api/role-management/get.asciidoc index b18b2e231774a..95f944a56e150 100644 --- a/docs/api/role-management/get.asciidoc +++ b/docs/api/role-management/get.asciidoc @@ -31,6 +31,7 @@ The API returns the following: -------------------------------------------------- { "name": "my_restricted_kibana_role", + "description": "My restricted kibana role description", "metadata" : { "version" : 1 }, diff --git a/docs/api/role-management/put.asciidoc b/docs/api/role-management/put.asciidoc index ce293f75b63ae..d68f3928a4063 100644 --- a/docs/api/role-management/put.asciidoc +++ b/docs/api/role-management/put.asciidoc @@ -21,6 +21,9 @@ To use the create or update role API, you must have the `manage_security` cluste [[role-management-api-response-body]] ==== Request body +`description`:: + (Optional, string) Description for the role. + `metadata`:: (Optional, object) In the `metadata` object, keys that begin with `_` are reserved for system usage. @@ -74,6 +77,7 @@ Grant access to various features in all spaces: -------------------------------------------------- $ curl -X PUT api/security/role/my_kibana_role { + "description": "my_kibana_role_description", "metadata": { "version": 1 }, @@ -112,6 +116,7 @@ Grant dashboard-only access to only the Marketing space: -------------------------------------------------- $ curl -X PUT api/security/role/my_kibana_role { + "description": "Grants dashboard-only access to only the Marketing space.", "metadata": { "version": 1 }, @@ -138,6 +143,7 @@ Grant full access to all features in the Default space: -------------------------------------------------- $ curl -X PUT api/security/role/my_kibana_role { + "description": "Grants full access to all features in the Default space.", "metadata": { "version": 1 }, @@ -162,6 +168,7 @@ Grant different access to different spaces: -------------------------------------------------- $ curl -X PUT api/security/role/my_kibana_role { + "description": "Grants full access to discover and dashboard features in the default space. Grants read access in the marketing, and sales spaces.", "metadata": { "version": 1 }, @@ -193,6 +200,7 @@ Grant access to {kib} and {es}: -------------------------------------------------- $ curl -X PUT api/security/role/my_kibana_role { + "description": "Grants all cluster privileges and full access to index1 and index2. Grants full access to remote_index1 and remote_index2, and the monitor_enrich cluster privilege on remote_cluster1. Grants all Kibana privileges in the default space.", "metadata": { "version": 1 }, diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx index 4003ce6233bda..f841fbca7d55a 100644 --- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx +++ b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.test.tsx @@ -42,6 +42,7 @@ describe('RoleComboBox', () => { }, { name: 'deprecated_role', + description: 'Deprecated role description', elasticsearch: { cluster: [], indices: [], run_as: [] }, kibana: [], metadata: { _reserved: true, _deprecated: true }, @@ -72,6 +73,7 @@ describe('RoleComboBox', () => { "label": "custom_role", "value": Object { "deprecatedReason": undefined, + "description": undefined, "isAdmin": false, "isDeprecated": false, "isReserved": false, @@ -89,6 +91,7 @@ describe('RoleComboBox', () => { "label": "reserved_role", "value": Object { "deprecatedReason": undefined, + "description": undefined, "isAdmin": false, "isDeprecated": false, "isReserved": true, @@ -106,6 +109,7 @@ describe('RoleComboBox', () => { "label": "some_admin", "value": Object { "deprecatedReason": undefined, + "description": undefined, "isAdmin": true, "isDeprecated": false, "isReserved": true, @@ -123,6 +127,7 @@ describe('RoleComboBox', () => { "label": "some_system", "value": Object { "deprecatedReason": undefined, + "description": undefined, "isAdmin": false, "isDeprecated": false, "isReserved": true, @@ -140,6 +145,7 @@ describe('RoleComboBox', () => { "label": "deprecated_role", "value": Object { "deprecatedReason": undefined, + "description": "Deprecated role description", "isAdmin": false, "isDeprecated": true, "isReserved": true, diff --git a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx index 5e329b32c353d..c1ce607d82acf 100644 --- a/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx +++ b/x-pack/plugins/security/public/management/role_combo_box/role_combo_box.tsx @@ -6,7 +6,14 @@ */ import type { EuiComboBoxOptionOption, EuiComboBoxProps } from '@elastic/eui'; -import { EuiBadge, EuiComboBox, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + EuiBadge, + EuiComboBox, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiToolTip, +} from '@elastic/eui'; import React from 'react'; import { i18n } from '@kbn/i18n'; @@ -34,6 +41,7 @@ type Option = EuiComboBoxOptionOption<{ isSystem: boolean; isAdmin: boolean; deprecatedReason?: string; + description?: string; }>; export const RoleComboBox = (props: Props) => { @@ -57,6 +65,7 @@ export const RoleComboBox = (props: Props) => { isSystem, isAdmin, deprecatedReason: roleDefinition?.metadata?._deprecated_reason, + description: roleDefinition?.description, }, }; }; @@ -134,7 +143,15 @@ export const RoleComboBox = (props: Props) => { function renderOption(option: Option) { return ( - {option.label} + + {option.value?.description ? ( + + {option.label} + + ) : ( + {option.label} + )} + {option.value?.isDeprecated ? ( diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index 90246ccd6afe9..93d8fec9e15a0 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -541,6 +541,69 @@ describe('', () => { expectSaveFormButtons(wrapper); }); + it('can render a user defined role with description', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find('input[data-test-subj="roleFormDescriptionInput"]').prop('value')).toBe( + 'my custom role description' + ); + expect( + wrapper.find('input[data-test-subj="roleFormDescriptionInput"]').prop('disabled') + ).toBe(undefined); + expectSaveFormButtons(wrapper); + }); + + it('can render a reserved role with description', async () => { + const wrapper = mountWithIntl( + + + + ); + + await waitForRender(wrapper); + + expect(wrapper.find('[data-test-subj="roleFormDescriptionTooltip"]')).toHaveLength(1); + + expect(wrapper.find('input[data-test-subj="roleFormDescriptionInput"]').prop('value')).toBe( + 'my reserved role description' + ); + expect( + wrapper.find('input[data-test-subj="roleFormDescriptionInput"]').prop('disabled') + ).toBe(true); + }); + it('can render when creating a new role', async () => { const wrapper = mountWithIntl( diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 56fb561443e82..697b85feb9edb 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -18,6 +18,7 @@ import { EuiSpacer, EuiText, EuiTitle, + EuiToolTip, } from '@elastic/eui'; import type { ChangeEvent, FocusEvent, FunctionComponent, HTMLProps } from 'react'; import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'; @@ -211,6 +212,7 @@ function useRole( ? rolesAPIClient.getRole(roleName) : Promise.resolve({ name: '', + description: '', elasticsearch: { cluster: [], indices: [], run_as: [], remote_cluster: [] }, kibana: [], _unrecognized_applications: [], @@ -452,45 +454,82 @@ export const EditRolePage: FunctionComponent = ({ return null; }; - const getRoleName = () => { + const getRoleNameAndDescription = () => { return ( - - } - helpText={ - !isEditingExistingRole ? ( - - ) : !isRoleReserved ? ( - + + + } + helpText={ + !isEditingExistingRole ? ( + + ) : !isRoleReserved ? ( + + ) : undefined + } + {...validator.validateRoleName(role)} + {...(creatingRoleAlreadyExists + ? { error: 'A role with this name already exists.', isInvalid: true } + : {})} + > + - ) : undefined - } - {...validator.validateRoleName(role)} - {...(creatingRoleAlreadyExists - ? { error: 'A role with this name already exists.', isInvalid: true } - : {})} - > - - + + + + + } + > + {isRoleReserved || isRoleReadOnly ? ( + + + + ) : ( + + )} + + + ); }; @@ -510,6 +549,12 @@ export const EditRolePage: FunctionComponent = ({ } }; + const onDescriptionChange = (e: ChangeEvent) => + setRole({ + ...role, + description: e.target.value.trim().length ? e.target.value : undefined, + }); + const getElasticsearchPrivileges = () => { return (
@@ -787,7 +832,7 @@ export const EditRolePage: FunctionComponent = ({ )} - {getRoleName()} + {getRoleNameAndDescription()} {getElasticsearchPrivileges()} {getKibanaPrivileges()} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts index bf85f80df1fc1..a425578ed98e5 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/validate_role.ts @@ -85,7 +85,6 @@ export class RoleValidator { } return valid(); } - public validateRemoteClusterPrivileges(role: Role): RoleValidationResult { if (!this.shouldValidate) { return valid(); diff --git a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx index ed6f28ea8321f..6b666cfd378f4 100644 --- a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.test.tsx @@ -56,6 +56,12 @@ describe('', () => { elasticsearch: { cluster: [], indices: [], run_as: [] }, kibana: [{ base: [], spaces: [], feature: {} }], }, + { + name: 'test-role-with-description', + description: 'role-description', + elasticsearch: { cluster: [], indices: [], run_as: [] }, + kibana: [{ base: [], spaces: [], feature: {} }], + }, { name: 'reserved-role', elasticsearch: { cluster: [], indices: [], run_as: [] }, @@ -162,6 +168,10 @@ describe('', () => { expect(wrapper.find('a[data-test-subj="edit-role-action-disabled-role"]')).toHaveLength(1); expect(wrapper.find('a[data-test-subj="clone-role-action-disabled-role"]')).toHaveLength(1); + + expect(findTestSubject(wrapper, 'roleRowDescription-test-role-with-description')).toHaveLength( + 1 + ); }); it('hides reserved roles when instructed to', async () => { @@ -201,6 +211,12 @@ describe('', () => { elasticsearch: { cluster: [], indices: [], run_as: [] }, kibana: [{ base: [], spaces: [], feature: {} }], }, + { + name: 'test-role-with-description', + description: 'role-description', + elasticsearch: { cluster: [], indices: [], run_as: [] }, + kibana: [{ base: [], spaces: [], feature: {} }], + }, ]); findTestSubject(wrapper, 'showReservedRolesSwitch').simulate('click'); @@ -222,6 +238,12 @@ describe('', () => { elasticsearch: { cluster: [], indices: [], run_as: [] }, kibana: [{ base: [], spaces: [], feature: {} }], }, + { + name: 'test-role-with-description', + description: 'role-description', + elasticsearch: { cluster: [], indices: [], run_as: [] }, + kibana: [{ base: [], spaces: [], feature: {} }], + }, ]); }); diff --git a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx index a21d0a1e99912..bb87cc61b0f84 100644 --- a/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx +++ b/x-pack/plugins/security/public/management/roles/roles_grid/roles_grid_page.tsx @@ -17,6 +17,7 @@ import { EuiSpacer, EuiSwitch, EuiText, + EuiToolTip, } from '@elastic/eui'; import _ from 'lodash'; import React, { Component } from 'react'; @@ -183,7 +184,6 @@ export class RolesGridPage extends Component { { ); }, }, + { + field: 'description', + name: i18n.translate('xpack.security.management.roles.descriptionColumnName', { + defaultMessage: 'Role Description', + }), + sortable: true, + truncateText: { lines: 3 }, + render: (description: string, record: Role) => { + return ( + + + {description} + + + ); + }, + }, ]; if (this.props.buildFlavor !== 'serverless') { config.push({ diff --git a/x-pack/test/functional/apps/security/index.ts b/x-pack/test/functional/apps/security/index.ts index 009c270d3c2a3..a65db6d3d3cf8 100644 --- a/x-pack/test/functional/apps/security/index.ts +++ b/x-pack/test/functional/apps/security/index.ts @@ -18,5 +18,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./user_email')); loadTestFile(require.resolve('./role_mappings')); loadTestFile(require.resolve('./remote_cluster_security_roles')); + loadTestFile(require.resolve('./role_description')); }); } diff --git a/x-pack/test/functional/apps/security/role_description.ts b/x-pack/test/functional/apps/security/role_description.ts new file mode 100644 index 0000000000000..eb272dec3d0a5 --- /dev/null +++ b/x-pack/test/functional/apps/security/role_description.ts @@ -0,0 +1,68 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const security = getService('security'); + const PageObjects = getPageObjects(['security', 'settings', 'common', 'header']); + + describe('Role Description', function () { + before(async () => { + await security.testUser.setRoles(['cluster_security_manager']); + await PageObjects.security.initTests(); + await PageObjects.settings.navigateTo(); + await PageObjects.security.clickElasticsearchRoles(); + }); + + after(async () => { + // NOTE: Logout needs to happen before anything else to avoid flaky behavior + await PageObjects.security.forceLogout(); + await security.role.delete('a-role-with-description'); + await security.role.delete('a-role-without-description'); + await security.testUser.restoreDefaults(); + }); + + it('Can create role with description', async () => { + await PageObjects.security.clickCreateNewRole(); + await testSubjects.setValue('roleFormNameInput', 'a-role-with-description'); + await testSubjects.setValue('roleFormDescriptionInput', 'role description'); + await PageObjects.security.clickSaveEditRole(); + + const columnDescription = await testSubjects.getVisibleText( + 'roleRowDescription-a-role-with-description' + ); + expect(columnDescription).to.equal('role description'); + + await PageObjects.settings.clickLinkText('a-role-with-description'); + const name = await testSubjects.getAttribute('roleFormNameInput', 'value'); + const description = await testSubjects.getAttribute('roleFormDescriptionInput', 'value'); + + expect(name).to.equal('a-role-with-description'); + expect(description).to.equal('role description'); + + await PageObjects.security.clickCancelEditRole(); + }); + + it('Can create role without description', async () => { + await PageObjects.security.clickCreateNewRole(); + await testSubjects.setValue('roleFormNameInput', 'a-role-without-description'); + await PageObjects.security.clickSaveEditRole(); + + await PageObjects.settings.clickLinkText('a-role-without-description'); + const name = await testSubjects.getAttribute('roleFormNameInput', 'value'); + const description = await testSubjects.getAttribute('roleFormDescriptionInput', 'value'); + + expect(name).to.equal('a-role-without-description'); + expect(description).to.equal(''); + + await PageObjects.security.clickCancelEditRole(); + }); + }); +} From b2118427f1ebaa6f8eb2411d3fb957e31a8a24b5 Mon Sep 17 00:00:00 2001 From: Jon Date: Thu, 16 May 2024 10:37:24 -0500 Subject: [PATCH 05/71] [artifacts/container] Re-add curl (#183626) Partially reverts https://github.com/elastic/kibana/pull/183334. Some services using kibana as a base expect it to exist for running health checks. cc @jsoriano --- .../os_packages/docker_generator/templates/base/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile index 9a58281ba55b3..d9e67b8389326 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/base/Dockerfile @@ -107,7 +107,7 @@ RUN for iter in {1..10}; do \ apt-get update && \ apt-get upgrade -y && \ apt-get install -y --no-install-recommends \ - fontconfig libnss3 ca-certificates && \ + fontconfig libnss3 curl ca-certificates && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* && exit_code=0 && break || exit_code=$? && echo "apt-get error: retry $iter in 10s" && \ sleep 10; \ From 4c80f262db014fbff83073e63b19121684a95053 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 16 May 2024 08:41:31 -0700 Subject: [PATCH 06/71] [UII] Allow to reset log level for agents >= 8.15.0 (#183434) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ‼️ Should be reverted if https://github.com/elastic/elastic-agent/issues/4747 does not make 8.15.0. ## Summary Resolves #180778 This PR allows agent log level to be reset back to the level set on its policy (or if not set, simply the default agent level, see https://github.com/elastic/elastic-agent/pull/3090). To achieve this, this PR: - Allows `null` to be passed for the log level settings action, i.e.: ``` POST kbn:/api/fleet/agents//actions {"action":{"type":"SETTINGS","data":{"log_level": null}}} ``` - Enables the agent policy log level setting implemented in https://github.com/elastic/kibana/pull/180607 - Always show `Apply changes` on the agent details > Logs tab - For agents >= 8.15.0, always show `Reset to policy` on the agent details > Logs tab - Ensures both buttons are disabled if user does not have access to write to agents image image ### Caveats 1. The reported agent log level is not accurate if agent is using the level from its policy and does not have a log level set on its own level (https://github.com/elastic/elastic-agent/issues/4747), so the initial selection on the agent log level could be wrong 2. We have no way to tell where the log level came from (https://github.com/elastic/elastic-agent/issues/4748), so that's why `Apply changes` and `Reset to policy` are always shown ### Testing Use the latest `8.15.0-SNAPSHOT` for agents or fleet server to test this change ### 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 --- .../fleet/common/constants/agent_policy.ts | 17 +- .../plugins/fleet/common/openapi/bundled.json | 1 + .../plugins/fleet/common/openapi/bundled.yaml | 1 + .../components/schemas/agent_action.yaml | 1 + ..._settings.ts => agent_policy_settings.tsx} | 22 +- .../components/agent_logs/agent_logs.test.tsx | 39 ++- .../components/agent_logs/agent_logs.tsx | 5 +- .../agent_logs/select_log_level.tsx | 243 +++++++++++------- .../fleet/server/types/models/agent.ts | 14 +- .../apis/agents/actions.ts | 20 +- 10 files changed, 243 insertions(+), 120 deletions(-) rename x-pack/plugins/fleet/common/settings/{agent_policy_settings.ts => agent_policy_settings.tsx} (89%) diff --git a/x-pack/plugins/fleet/common/constants/agent_policy.ts b/x-pack/plugins/fleet/common/constants/agent_policy.ts index b2cbed6bb68e9..9f1bc458ec363 100644 --- a/x-pack/plugins/fleet/common/constants/agent_policy.ts +++ b/x-pack/plugins/fleet/common/constants/agent_policy.ts @@ -39,17 +39,10 @@ export const DEFAULT_MAX_AGENT_POLICIES_WITH_INACTIVITY_TIMEOUT = 750; export const AGENTLESS_POLICY_ID = 'agentless'; // the policy id defined here: https://github.com/elastic/project-controller/blob/main/internal/project/security/security_kibana_config.go#L86 export const AGENT_LOG_LEVELS = { - ERROR: 'error', - WARNING: 'warning', - INFO: 'info', - DEBUG: 'debug', + info: 'info', + debug: 'debug', + warning: 'warning', + error: 'error', }; -export const DEFAULT_LOG_LEVEL = AGENT_LOG_LEVELS.INFO; - -export const agentLoggingLevels = { - Info: 'info', - Debug: 'debug', - Warning: 'warning', - Error: 'error', -} as const; +export const DEFAULT_LOG_LEVEL = AGENT_LOG_LEVELS.info; diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index f9ebd7d84653a..9a86cbe6f9fd1 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -7294,6 +7294,7 @@ "properties": { "log_level": { "type": "string", + "nullable": true, "enum": [ "debug", "info", diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 3bf9514e08147..16137b6f8dd27 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -4674,6 +4674,7 @@ components: properties: log_level: type: string + nullable: true enum: - debug - info diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_action.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_action.yaml index e1726e438f82c..ae8734939c373 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_action.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_action.yaml @@ -19,6 +19,7 @@ oneOf: properties: log_level: type: string + nullable: true enum: - debug - info diff --git a/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts b/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx similarity index 89% rename from x-pack/plugins/fleet/common/settings/agent_policy_settings.ts rename to x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx index 4257f47c7c33d..4ffe46b680764 100644 --- a/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts +++ b/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx @@ -4,11 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import React from 'react'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiCode } from '@elastic/eui'; + import { z } from 'zod'; -import { agentLoggingLevels } from '../constants'; +import { AGENT_LOG_LEVELS, DEFAULT_LOG_LEVEL } from '../constants'; import type { SettingsConfig } from './types'; @@ -130,20 +133,19 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [ }, { name: 'agent.logging.level', - hidden: true, title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentLoggingLevelTitle', { defaultMessage: 'Agent logging level', }), - description: i18n.translate( - 'xpack.fleet.settings.agentPolicyAdvanced.agentLoggingLevelDescription', - { - defaultMessage: - 'Sets the log level for all the agents on the policy. The default log level is "info".', - } + description: ( + {DEFAULT_LOG_LEVEL} }} + /> ), api_field: { name: 'agent_logging_level', }, - schema: z.nativeEnum(agentLoggingLevels).default(agentLoggingLevels.Info), + schema: z.nativeEnum(AGENT_LOG_LEVELS).default(DEFAULT_LOG_LEVEL), }, ]; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.test.tsx index cf22126a7c0d4..7824b8abd2a5c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.test.tsx @@ -65,11 +65,15 @@ describe('AgentLogsUI', () => { }, } as any); }); - const renderComponent = () => { + const renderComponent = ( + opts = { + agentVersion: '8.11.0', + } + ) => { const renderer = createFleetTestRendererMock(); const agent = { id: 'agent1', - local_metadata: { elastic: { agent: { version: '8.11' } } }, + local_metadata: { elastic: { agent: { version: opts.agentVersion, log_level: 'debug' } } }, } as any; const state = { datasets: ['elastic_agent'], @@ -125,4 +129,35 @@ describe('AgentLogsUI', () => { `http://localhost:5620/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:'2023-20-04T14:00:00.340Z',to:'2023-20-04T14:20:00.340Z'))&_a=(columns:!(event.dataset,message),index:'logs-*',query:(language:kuery,query:'elastic_agent.id:agent1 and (data_stream.dataset:elastic_agent) and (log.level:info or log.level:error)'))` ); }); + + it('should show log level dropdown with correct value', () => { + mockStartServices(); + const result = renderComponent(); + const logLevelDropdown = result.getByTestId('selectAgentLogLevel'); + expect(logLevelDropdown.getElementsByTagName('option').length).toBe(4); + expect(logLevelDropdown).toHaveDisplayValue('debug'); + }); + + it('should always show apply log level changes button', () => { + mockStartServices(); + const result = renderComponent(); + const applyLogLevelBtn = result.getByTestId('applyLogLevelBtn'); + expect(applyLogLevelBtn).toBeInTheDocument(); + expect(applyLogLevelBtn).not.toHaveAttribute('disabled'); + }); + + it('should hide reset log level button for agents version < 8.15.0', () => { + mockStartServices(); + const result = renderComponent(); + const resetLogLevelBtn = result.queryByTestId('resetLogLevelBtn'); + expect(resetLogLevelBtn).not.toBeInTheDocument(); + }); + + it('should show reset log level button for agents version >= 8.15.0', () => { + mockStartServices(); + const result = renderComponent({ agentVersion: '8.15.0' }); + const resetLogLevelBtn = result.getByTestId('resetLogLevelBtn'); + expect(resetLogLevelBtn).toBeInTheDocument(); + expect(resetLogLevelBtn).not.toHaveAttribute('disabled'); + }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx index 05efb3abfdcaa..34cc206967d62 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx @@ -342,7 +342,10 @@ export const AgentLogsUI: React.FunctionComponent = memo( - + ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/select_log_level.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/select_log_level.tsx index 4c32952339347..5a38031898eb8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/select_log_level.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/select_log_level.tsx @@ -9,106 +9,175 @@ import React, { memo, useState, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiSelect, EuiFormLabel, EuiButtonEmpty, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import semverGte from 'semver/functions/gte'; import type { Agent } from '../../../../../types'; import { sendPostAgentAction, useAuthz, useStartServices } from '../../../../../hooks'; import { AGENT_LOG_LEVELS, DEFAULT_LOG_LEVEL } from '../../../../../../../../common/constants'; -const LEVEL_VALUES = Object.values(AGENT_LOG_LEVELS); +export const SelectLogLevel: React.FC<{ agent: Agent; agentPolicyLogLevel?: string }> = memo( + ({ agent, agentPolicyLogLevel = DEFAULT_LOG_LEVEL }) => { + const authz = useAuthz(); + const { notifications } = useStartServices(); + const [isSetLevelLoading, setIsSetLevelLoading] = useState(false); + const [isResetLevelLoading, setIsResetLevelLoading] = useState(false); + const canResetLogLevel = semverGte( + agent.local_metadata?.elastic?.agent?.version, + '8.15.0', + true + ); -export const SelectLogLevel: React.FC<{ agent: Agent }> = memo(({ agent }) => { - const authz = useAuthz(); - const { notifications } = useStartServices(); - const [isLoading, setIsLoading] = useState(false); - const [agentLogLevel, setAgentLogLevel] = useState( - agent.local_metadata?.elastic?.agent?.log_level ?? DEFAULT_LOG_LEVEL - ); - const [selectedLogLevel, setSelectedLogLevel] = useState(agentLogLevel); + const [agentLogLevel, setAgentLogLevel] = useState( + agent.local_metadata?.elastic?.agent?.log_level ?? DEFAULT_LOG_LEVEL + ); + const [selectedLogLevel, setSelectedLogLevel] = useState(agentLogLevel); - const onClickApply = useCallback(() => { - setIsLoading(true); - async function send() { - try { - const res = await sendPostAgentAction(agent.id, { - action: { - type: 'SETTINGS', - data: { - log_level: selectedLogLevel, + const resetLogLevel = useCallback(() => { + setIsResetLevelLoading(true); + async function send() { + try { + const res = await sendPostAgentAction(agent.id, { + action: { + type: 'SETTINGS', + data: { + log_level: null, + }, }, - }, - }); - if (res.error) { - throw res.error; + }); + if (res.error) { + throw res.error; + } + + // TODO: reset to an empty state? + setAgentLogLevel(agentPolicyLogLevel); + setSelectedLogLevel(agentPolicyLogLevel); + + notifications.toasts.addSuccess( + i18n.translate('xpack.fleet.agentLogs.resetLogLevel.successText', { + defaultMessage: `Reset agent logging level to policy`, + }) + ); + } catch (error) { + notifications.toasts.addError(error, { + title: i18n.translate('xpack.fleet.agentLogs.resetLogLevel.errorTitleText', { + defaultMessage: 'Error resetting agent logging level', + }), + }); } - setAgentLogLevel(selectedLogLevel); - notifications.toasts.addSuccess( - i18n.translate('xpack.fleet.agentLogs.selectLogLevel.successText', { - defaultMessage: `Changed agent logging level to '{logLevel}'.`, - values: { - logLevel: selectedLogLevel, + setIsResetLevelLoading(false); + } + + send(); + }, [agent.id, agentPolicyLogLevel, notifications]); + + const onClickApply = useCallback(() => { + setIsSetLevelLoading(true); + async function send() { + try { + const res = await sendPostAgentAction(agent.id, { + action: { + type: 'SETTINGS', + data: { + log_level: selectedLogLevel, + }, }, - }) - ); - } catch (error) { - notifications.toasts.addError(error, { - title: i18n.translate('xpack.fleet.agentLogs.selectLogLevel.errorTitleText', { - defaultMessage: 'Error updating agent logging level', - }), - }); + }); + if (res.error) { + throw res.error; + } + setAgentLogLevel(selectedLogLevel); + notifications.toasts.addSuccess( + i18n.translate('xpack.fleet.agentLogs.selectLogLevel.successText', { + defaultMessage: `Changed agent logging level to '{logLevel}'`, + values: { + logLevel: selectedLogLevel, + }, + }) + ); + } catch (error) { + notifications.toasts.addError(error, { + title: i18n.translate('xpack.fleet.agentLogs.selectLogLevel.errorTitleText', { + defaultMessage: 'Error updating agent logging level', + }), + }); + } + setIsSetLevelLoading(false); } - setIsLoading(false); - } - send(); - }, [notifications, selectedLogLevel, agent.id]); + send(); + }, [notifications, selectedLogLevel, agent.id]); - return ( - - - - - - - - { - setSelectedLogLevel(event.target.value); - }} - options={LEVEL_VALUES.map((level) => ({ text: level, value: level }))} - /> - - {agentLogLevel !== selectedLogLevel && ( - - - {isLoading ? ( - - ) : ( + return ( + <> + + + - )} - - - )} - - ); -}); + + + + { + setSelectedLogLevel(event.target.value); + }} + options={Object.entries(AGENT_LOG_LEVELS).map(([key, value]) => ({ + value, + text: key, + }))} + /> + + + + {isSetLevelLoading ? ( + + ) : ( + + )} + + + {canResetLogLevel && ( + + + + + + )} + + + ); + } +); diff --git a/x-pack/plugins/fleet/server/types/models/agent.ts b/x-pack/plugins/fleet/server/types/models/agent.ts index aed1632929e3a..b8fd7ed8bf82f 100644 --- a/x-pack/plugins/fleet/server/types/models/agent.ts +++ b/x-pack/plugins/fleet/server/types/models/agent.ts @@ -32,12 +32,14 @@ export const NewAgentActionSchema = schema.oneOf([ schema.object({ type: schema.oneOf([schema.literal('SETTINGS')]), data: schema.object({ - log_level: schema.oneOf([ - schema.literal('debug'), - schema.literal('info'), - schema.literal('warning'), - schema.literal('error'), - ]), + log_level: schema.nullable( + schema.oneOf([ + schema.literal('debug'), + schema.literal('info'), + schema.literal('warning'), + schema.literal('error'), + ]) + ), }), }), ]); diff --git a/x-pack/test/fleet_api_integration/apis/agents/actions.ts b/x-pack/test/fleet_api_integration/apis/agents/actions.ts index 0e592cf9281fd..57b367f8c4bd0 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/actions.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/actions.ts @@ -26,7 +26,7 @@ export default function (providerContext: FtrProviderContext) { }); describe('POST /agents/{agentId}/actions', () => { - it('should return a 200 if this a valid SETTINGS action request', async () => { + it('should return a 200 if this a SETTINGS action request with a valid log level', async () => { const { body: apiResponse } = await supertest .post(`/api/fleet/agents/agent1/actions`) .set('kbn-xsrf', 'xx') @@ -42,7 +42,23 @@ export default function (providerContext: FtrProviderContext) { expect(apiResponse.item.data).to.eql({ log_level: 'debug' }); }); - it('should return a 400 if this a invalid SETTINGS action request', async () => { + it('should return a 200 if this a SETTINGS action request with null log level', async () => { + const { body: apiResponse } = await supertest + .post(`/api/fleet/agents/agent1/actions`) + .set('kbn-xsrf', 'xx') + .send({ + action: { + type: 'SETTINGS', + data: { log_level: null }, + }, + }) + .expect(200); + + expect(apiResponse.item.type).to.eql('SETTINGS'); + expect(apiResponse.item.data).to.eql({ log_level: null }); + }); + + it('should return a 400 if this a SETTINGS action request with an invalid log level', async () => { const { body: apiResponse } = await supertest .post(`/api/fleet/agents/agent1/actions`) .set('kbn-xsrf', 'xx') From f5c2cbaf8e3cbe0f4399cc0760db91f8c9f11ec2 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 16 May 2024 18:42:04 +0300 Subject: [PATCH 07/71] fix: [Obs Alerts > Rules][KEYBOARD]: Inspect Alerts Table button should always be visible and must be visible on keyboard focus (#183513) Closes: https://github.com/elastic/observability-dev/issues/3361 ## Description The Obs Alerts have a button for opening an `Inspect Alerts table` modal that is only visible on `:hover`. This button is easily missed by keyboard navigation because it is not visible on `:focus`. My recommendation is the button should **always** be visible and must be visible when it receives focus. Screenshot attached below. ### Steps to recreate 1. Open the [Obs Alerts](https://keepserverless-qa-oblt-b4ba07.kb.eu-west-1.aws.qa.elastic.cloud/app/observability/alerts) table 2. Create an alert that's sure to fire, like a custom threshold that's very low, like 100 or 1000 documents 3. Run the alert rule from the `Rules` view 4. When the alert appears in `Alerts`, tab through to the data grid 5. Watch closely for focus to "disappear" 6. Press `Tab` again and focus should reappear 7. Now press `Shift + Tab` to make focus disappear again 8. Press `Enter` and verify the modal opens ### What was done?: 1. `&:focus-visible` styles were added to correctly handle keyboard navigation ### Screen: https://github.com/elastic/kibana/assets/20072247/c8644009-e966-4e19-920a-62cf04b741a6 --- .../components/inspect/hover_visibility_container/index.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.tsx index 95240e4141e31..e76ac557a50fb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/toolbar/components/inspect/hover_visibility_container/index.tsx @@ -28,6 +28,10 @@ const StyledDiv = euiStyled.div` pointer-events: none; opacity: 0; transition: opacity ${getOr(250, 'eui.euiAnimSpeedNormal', theme)} ease; + &:focus-visible { + pointer-events: auto; + opacity: 1; + } } ${targetClassNames.map((cn) => `&:hover .${cn}`).join(', ')} { From 81679a96aa1f5783c02749ef9497aae15241d219 Mon Sep 17 00:00:00 2001 From: Jedr Blaszyk Date: Thu, 16 May 2024 17:44:35 +0200 Subject: [PATCH 08/71] [Connectors] Use Connector API to manage connector filtering (#183148) Use[ Connector API update filtering endpoint](https://www.elastic.co/guide/en/elasticsearch/reference/master/update-connector-filtering-api.html) to manage connector filtering in Kibana. --- .../lib/update_filtering.test.ts | 99 +++++++++++++++ .../lib/update_filtering.ts | 57 ++------- .../lib/update_filtering_draft.test.ts | 116 ++++++++++++++++++ .../lib/update_filtering_draft.ts | 34 ++--- .../update_connector_filtering_api_logic.ts | 17 +-- .../sync_rules/connector_filtering_logic.tsx | 2 - .../routes/enterprise_search/connectors.ts | 40 +++--- 7 files changed, 260 insertions(+), 105 deletions(-) create mode 100644 packages/kbn-search-connectors/lib/update_filtering.test.ts create mode 100644 packages/kbn-search-connectors/lib/update_filtering_draft.test.ts diff --git a/packages/kbn-search-connectors/lib/update_filtering.test.ts b/packages/kbn-search-connectors/lib/update_filtering.test.ts new file mode 100644 index 0000000000000..284210539230b --- /dev/null +++ b/packages/kbn-search-connectors/lib/update_filtering.test.ts @@ -0,0 +1,99 @@ +/* + * 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 { ElasticsearchClient } from '@kbn/core/server'; + +import { errors } from '@elastic/elasticsearch'; + +import { updateFiltering } from './update_filtering'; +import { FilteringRule, FilteringRules, FilteringValidationState } from '../types/connectors'; + +describe('updateFiltering lib function', () => { + const mockClient = { + transport: { + request: jest.fn(), + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + jest.useFakeTimers(); + jest.setSystemTime(new Date('2024-05-25T12:00:00.000Z')); + }); + + it('should activate connector filtering draft', async () => { + const filteringRule: FilteringRule = { + updated_at: '2024-05-10T12:14:14.291Z', + created_at: '2024-05-09T14:37:56.090Z', + field: 'name', + id: 'my-rule', + order: 0, + policy: 'exclude', + rule: 'regex', + value: 'test.*', + }; + + const draftToActivate: FilteringRules = { + advanced_snippet: { + created_at: '2024-05-25T12:00:00.000Z', + updated_at: '2024-05-25T12:00:00.000Z', + value: {}, + }, + rules: [ + { + ...filteringRule, + updated_at: '2024-05-25T12:00:00.000Z', + }, + ], + validation: { + errors: [], + state: FilteringValidationState.VALID, + }, + }; + + mockClient.transport.request.mockImplementationOnce(() => ({ result: 'updated' })); + mockClient.transport.request.mockImplementationOnce(() => ({ + filtering: [{ active: draftToActivate }], + })); + + await expect( + updateFiltering(mockClient as unknown as ElasticsearchClient, 'connectorId') + ).resolves.toEqual(draftToActivate); + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/connectorId/_filtering/activate', + }); + }); + + it('should not index document if there is no connector', async () => { + mockClient.transport.request.mockImplementationOnce(() => { + return Promise.reject( + new errors.ResponseError({ + statusCode: 404, + body: { + error: { + type: `document_missing_exception`, + }, + }, + } as any) + ); + }); + await expect( + updateFiltering(mockClient as unknown as ElasticsearchClient, 'connectorId') + ).rejects.toEqual( + new errors.ResponseError({ + statusCode: 404, + body: { + error: { + type: `document_missing_exception`, + }, + }, + } as any) + ); + }); +}); diff --git a/packages/kbn-search-connectors/lib/update_filtering.ts b/packages/kbn-search-connectors/lib/update_filtering.ts index 9651828fdc313..0c02b9da899a4 100644 --- a/packages/kbn-search-connectors/lib/update_filtering.ts +++ b/packages/kbn-search-connectors/lib/update_filtering.ts @@ -6,59 +6,24 @@ * Side Public License, v 1. */ +import { Result } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { CONNECTORS_INDEX } from '..'; import { fetchConnectorById } from './fetch_connectors'; -import { - Connector, - FilteringRule, - FilteringRules, - FilteringValidationState, -} from '../types/connectors'; +import { FilteringRules } from '../types/connectors'; export const updateFiltering = async ( client: ElasticsearchClient, - connectorId: string, - { - advancedSnippet, - filteringRules, - }: { - advancedSnippet: string; - filteringRules: FilteringRule[]; - } + connectorId: string ): Promise => { - const now = new Date().toISOString(); - const parsedAdvancedSnippet: Record = advancedSnippet - ? JSON.parse(advancedSnippet) - : {}; - const parsedFilteringRules = filteringRules.map((filteringRule) => ({ - ...filteringRule, - created_at: filteringRule.created_at ? filteringRule.created_at : now, - updated_at: now, - })); - const connector = await fetchConnectorById(client, connectorId); - if (!connector) { - throw new Error(`Could not find connector with id ${connectorId}`); - } - const active: FilteringRules = { - advanced_snippet: { - created_at: connector.filtering[0].active.advanced_snippet.created_at || now, - updated_at: now, - value: parsedAdvancedSnippet, - }, - rules: parsedFilteringRules, - validation: { - errors: [], - state: FilteringValidationState.VALID, - }, - }; - - const result = await client.update({ - doc: { ...connector, filtering: [{ ...connector.filtering[0], active, draft: active }] }, - id: connectorId, - index: CONNECTORS_INDEX, + const activateDraftFilteringResult = await client.transport.request<{ result: Result }>({ + method: 'PUT', + path: `/_connector/${connectorId}/_filtering/activate`, }); - return result.result === 'updated' ? active : undefined; + if (activateDraftFilteringResult.result === 'updated') { + const connector = await fetchConnectorById(client, connectorId); + return connector?.filtering?.[0]?.active; + } + return undefined; }; diff --git a/packages/kbn-search-connectors/lib/update_filtering_draft.test.ts b/packages/kbn-search-connectors/lib/update_filtering_draft.test.ts new file mode 100644 index 0000000000000..cfbc4e9d70713 --- /dev/null +++ b/packages/kbn-search-connectors/lib/update_filtering_draft.test.ts @@ -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 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 { ElasticsearchClient } from '@kbn/core/server'; + +import { errors } from '@elastic/elasticsearch'; + +import { updateFilteringDraft } from './update_filtering_draft'; +import { FilteringRule, FilteringRules, FilteringValidationState } from '../types/connectors'; + +describe('updateFilteringDraft lib function', () => { + const mockClient = { + transport: { + request: jest.fn(), + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + jest.useFakeTimers(); + jest.setSystemTime(new Date('2024-05-25T12:00:00.000Z')); + }); + + it('should update connector filtering draft', async () => { + const filteringRule: FilteringRule = { + updated_at: '2024-05-10T12:14:14.291Z', + created_at: '2024-05-09T14:37:56.090Z', + field: 'name', + id: 'my-rule', + order: 0, + policy: 'exclude', + rule: 'regex', + value: 'test.*', + }; + + const draft: FilteringRules = { + advanced_snippet: { + created_at: '2024-05-25T12:00:00.000Z', + updated_at: '2024-05-25T12:00:00.000Z', + value: {}, + }, + rules: [ + { + ...filteringRule, + updated_at: '2024-05-25T12:00:00.000Z', + }, + ], + validation: { + errors: [], + state: FilteringValidationState.EDITED, + }, + }; + + mockClient.transport.request.mockImplementationOnce(() => ({ result: 'updated' })); + mockClient.transport.request.mockImplementationOnce(() => ({ filtering: [{ draft }] })); + + await expect( + updateFilteringDraft(mockClient as unknown as ElasticsearchClient, 'connectorId', { + advancedSnippet: '{}', + filteringRules: [filteringRule], + }) + ).resolves.toEqual(draft); + expect(mockClient.transport.request).toHaveBeenCalledWith({ + body: { + advanced_snippet: { + created_at: '2024-05-25T12:00:00.000Z', + updated_at: '2024-05-25T12:00:00.000Z', + value: {}, + }, + rules: [ + { + ...filteringRule, + updated_at: '2024-05-25T12:00:00.000Z', + }, + ], + }, + method: 'PUT', + path: '/_connector/connectorId/_filtering', + }); + }); + + it('should not index document if there is no connector', async () => { + mockClient.transport.request.mockImplementationOnce(() => { + return Promise.reject( + new errors.ResponseError({ + statusCode: 404, + body: { + error: { + type: `document_missing_exception`, + }, + }, + } as any) + ); + }); + await expect( + updateFilteringDraft(mockClient as unknown as ElasticsearchClient, 'connectorId', { + advancedSnippet: '{}', + filteringRules: [], + }) + ).rejects.toEqual( + new errors.ResponseError({ + statusCode: 404, + body: { + error: { + type: `document_missing_exception`, + }, + }, + } as any) + ); + }); +}); diff --git a/packages/kbn-search-connectors/lib/update_filtering_draft.ts b/packages/kbn-search-connectors/lib/update_filtering_draft.ts index 154f85f4becb0..6d580604004c8 100644 --- a/packages/kbn-search-connectors/lib/update_filtering_draft.ts +++ b/packages/kbn-search-connectors/lib/update_filtering_draft.ts @@ -6,16 +6,11 @@ * Side Public License, v 1. */ +import { Result } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { CONNECTORS_INDEX } from '..'; import { fetchConnectorById } from './fetch_connectors'; -import { - Connector, - FilteringRule, - FilteringRules, - FilteringValidationState, -} from '../types/connectors'; +import { FilteringRule, FilteringRules } from '../types/connectors'; export const updateFilteringDraft = async ( client: ElasticsearchClient, @@ -37,28 +32,25 @@ export const updateFilteringDraft = async ( created_at: filteringRule.created_at ? filteringRule.created_at : now, updated_at: now, })); - const draft: FilteringRules = { + + const draft = { advanced_snippet: { created_at: now, updated_at: now, value: parsedAdvancedSnippet, }, rules: parsedFilteringRules, - validation: { - errors: [], - state: FilteringValidationState.EDITED, - }, }; - const connector = await fetchConnectorById(client, connectorId); - if (!connector) { - throw new Error(`Could not find connector with id ${connectorId}`); - } - const result = await client.update({ - doc: { ...connector, filtering: [{ ...connector.filtering[0], draft }] }, - id: connectorId, - index: CONNECTORS_INDEX, + const updateDraftFilteringResult = await client.transport.request<{ result: Result }>({ + method: 'PUT', + path: `/_connector/${connectorId}/_filtering`, + body: draft, }); - return result.result === 'updated' ? draft : undefined; + if (updateDraftFilteringResult.result === 'updated') { + const connector = await fetchConnectorById(client, connectorId); + return connector?.filtering?.[0]?.draft; + } + return undefined; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/update_connector_filtering_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/update_connector_filtering_api_logic.ts index 00d300c32e058..798df56b7fae3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/update_connector_filtering_api_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/update_connector_filtering_api_logic.ts @@ -7,32 +7,21 @@ import { i18n } from '@kbn/i18n'; -import { FilteringRule, FilteringRules } from '@kbn/search-connectors'; +import { FilteringRules } from '@kbn/search-connectors'; import { createApiLogic } from '../../../shared/api_logic/create_api_logic'; import { HttpLogic } from '../../../shared/http'; export interface PutConnectorFilteringArgs { - advancedSnippet: string; connectorId: string; - filteringRules: FilteringRule[]; } export type PutConnectorFilteringResponse = FilteringRules; -export const putConnectorFiltering = async ({ - advancedSnippet, - connectorId, - filteringRules, -}: PutConnectorFilteringArgs) => { +export const putConnectorFiltering = async ({ connectorId }: PutConnectorFilteringArgs) => { const route = `/internal/enterprise_search/connectors/${connectorId}/filtering`; - return await HttpLogic.values.http.put(route, { - body: JSON.stringify({ - advanced_snippet: advancedSnippet, - filtering_rules: filteringRules, - }), - }); + return await HttpLogic.values.http.put(route); }; export const ConnectorFilteringApiLogic = createApiLogic( diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/connector_filtering_logic.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/connector_filtering_logic.tsx index 3ccdf83ce5dd8..825cea83f1510 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/connector_filtering_logic.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/sync_rules/connector_filtering_logic.tsx @@ -149,9 +149,7 @@ export const ConnectorFilteringLogic = kea< applyDraft: () => { if (isConnectorIndex(values.index)) { actions.makeRequest({ - advancedSnippet: values.localAdvancedSnippet ?? '', connectorId: values.index.connector.id, - filteringRules: values.localFilteringRules ?? [], }); } }, diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts index 395c114424864..66cea5b830730 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts @@ -472,21 +472,23 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) { { path: '/internal/enterprise_search/connectors/{connectorId}/filtering', validate: { - body: schema.object({ - advanced_snippet: schema.string(), - filtering_rules: schema.arrayOf( - schema.object({ - created_at: schema.string(), - field: schema.string(), - id: schema.string(), - order: schema.number(), - policy: schema.string(), - rule: schema.string(), - updated_at: schema.string(), - value: schema.string(), - }) - ), - }), + body: schema.maybe( + schema.object({ + advanced_snippet: schema.string(), + filtering_rules: schema.arrayOf( + schema.object({ + created_at: schema.string(), + field: schema.string(), + id: schema.string(), + order: schema.number(), + policy: schema.string(), + rule: schema.string(), + updated_at: schema.string(), + value: schema.string(), + }) + ), + }) + ), params: schema.object({ connectorId: schema.string(), }), @@ -495,13 +497,7 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) { elasticsearchErrorHandler(log, async (context, request, response) => { const { client } = (await context.core).elasticsearch; const { connectorId } = request.params; - const { advanced_snippet, filtering_rules } = request.body; - const result = await updateFiltering(client.asCurrentUser, connectorId, { - advancedSnippet: advanced_snippet, - // Have to cast here because our API schema validator doesn't know how to deal with enums - // We're relying on the schema in the validator above to flag if something goes wrong - filteringRules: filtering_rules as FilteringRule[], - }); + const result = await updateFiltering(client.asCurrentUser, connectorId); return result ? response.ok({ body: result }) : response.conflict(); }) ); From 7e10adac5c392484b6ed6813e1dc5d762cdf5beb Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 16 May 2024 16:50:40 +0100 Subject: [PATCH 09/71] skip flaky suite (#183579) --- .../e2e/investigations/timeline_templates/creation.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts index 40efaa1f20883..4e2da9bc9170b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts @@ -48,7 +48,8 @@ import { GLOBAL_SEARCH_BAR_FILTER_ITEM_AT } from '../../../screens/search_bar'; const mockTimeline = getTimeline(); -describe('Timeline Templates', { tags: ['@ess', '@serverless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/183579 +describe.skip('Timeline Templates', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); deleteTimelines(); From 38e7ab29912116c75e61824b81b860b03f22aeb7 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Thu, 16 May 2024 19:04:27 +0300 Subject: [PATCH 10/71] fix: [Obs Alerts > Alert Detail][KEYBOARD]: Table header has tooltips that must be keyboard focusable (#183523) Closes: https://github.com/elastic/observability-dev/issues/3379 ## Description The Obs Alert Detail view has a table with several tooltips that are not keyboard focusable. Screenshot attached below. ### Steps to recreate 1. Open the [Obs Alerts](https://keepserverless-qa-oblt-b4ba07.kb.eu-west-1.aws.qa.elastic.cloud/app/observability/alerts) table 2. Click the "Manage Rules" link 3. Create a new rule and verify it appears in the Rules table if you have none that are generating alerts 4. When the rule appears in the Rules table, click the `[ ... ]` menu and then click "Run rule" 5. After the rule runs, go back to the `Alerts` view and click on the `[ ... ]` menu in the data grid, then click on "View alert details" 6. Tab through the page until you get to the table 7. Verify the tooltips can be accessed with mouse hover, but not keyboard focus ### What was changed?: 1. **EuiTooltip** was replaced to **EuiIconTip** ### Screen https://github.com/elastic/kibana/assets/20072247/11d66ff6-9139-43fe-93fd-94c6dbb3be4e --- .../log_rate_analysis_results_table.tsx | 126 +++++++------- ...log_rate_analysis_results_table_groups.tsx | 159 ++++++++++-------- 2 files changed, 153 insertions(+), 132 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx index ac9ae24987b94..59e8f930398e9 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx @@ -16,10 +16,8 @@ import { EuiBadge, EuiBasicTable, EuiCode, - EuiIcon, EuiIconTip, EuiText, - EuiToolTip, } from '@elastic/eui'; import type { FieldStatsServices } from '@kbn/unified-field-list/src/components/field_stats'; @@ -150,7 +148,11 @@ export const LogRateAnalysisResultsTable: FC = /> )} {type === SIGNIFICANT_ITEM_TYPE.LOG_PATTERN && ( - = 'The field value for this field shows an example of the identified significant text field pattern.', } )} - > - - + /> )} @@ -206,21 +201,27 @@ export const LogRateAnalysisResultsTable: FC = width: NARROW_COLUMN_WIDTH, field: 'pValue', name: ( - - <> - - - - + <> + +   + + ), render: (_, { histogram, fieldName, fieldValue }) => ( = width: NARROW_COLUMN_WIDTH, field: 'pValue', name: ( - - <> - - - - + <> + +   + + ), render: (pValue: number | null) => pValue?.toPrecision(3) ?? NOT_AVAILABLE, sortable: true, @@ -278,23 +285,26 @@ export const LogRateAnalysisResultsTable: FC = width: NARROW_COLUMN_WIDTH, field: 'pValue', name: ( - - <> - - - - + <> + +   + + ), render: (_, { pValue }) => { if (typeof pValue !== 'number') return NOT_AVAILABLE; diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx index 1df6550309ae9..c388555bcf2ca 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx @@ -15,11 +15,10 @@ import { EuiBadge, EuiBasicTable, EuiButtonIcon, - EuiIcon, EuiScreenReaderOnly, EuiSpacer, EuiText, - EuiToolTip, + EuiIconTip, RIGHT_ALIGNMENT, useEuiTheme, euiPaletteColorBlind, @@ -176,25 +175,28 @@ export const LogRateAnalysisResultsGroupsTable: FC - <> - - - - + <> + +   + + ), render: (_, { uniqueItemsCount, groupItemsSortedByUniqueness }) => { const valuesBadges = []; @@ -262,24 +264,27 @@ export const LogRateAnalysisResultsGroupsTable: FC - <> - - - - + <> + +   + + ), render: (_, { histogram, id }) => ( - <> - - - - + <> + +   + + ), render: (pValue: number | null) => pValue?.toPrecision(3) ?? NOT_AVAILABLE, sortable: true, @@ -342,23 +350,26 @@ export const LogRateAnalysisResultsGroupsTable: FC - <> - - - - + <> + +   + + ), render: (_, { pValue }) => { if (!pValue) return NOT_AVAILABLE; From 7d6f2d4d0206897f6b6808c23d8cbf1c8a71389e Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Thu, 16 May 2024 09:44:52 -0700 Subject: [PATCH 11/71] [Security Solution] unskip endpoint cypress tests (#183534) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `vagrant up` was [occasionally timing out](https://github.com/elastic/kibana/issues/168284#issuecomment-2110637429). bumped the `config.vm.boot_timeout` to mitigate flakiness from this. flaky test run: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5998 (50x ✅ ) fixes https://github.com/elastic/kibana/issues/168284 ### 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 - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../management/cypress/e2e/endpoint_list/endpoints.cy.ts | 3 +-- .../scripts/endpoint/common/vagrant/Vagrantfile | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts index 0aeb46b32fb43..4396937e57228 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/endpoint_list/endpoints.cy.ts @@ -28,8 +28,7 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; -// FLAKY: https://github.com/elastic/kibana/issues/168284 -describe.skip('Endpoints page', { tags: ['@ess', '@serverless'] }, () => { +describe('Endpoints page', { tags: ['@ess', '@serverless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/vagrant/Vagrantfile b/x-pack/plugins/security_solution/scripts/endpoint/common/vagrant/Vagrantfile index 59c5bf12a1627..6f08c6265ecd7 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/vagrant/Vagrantfile +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/vagrant/Vagrantfile @@ -11,6 +11,7 @@ Vagrant.configure("2") do |config| config.vm.hostname = hostname config.vm.box = 'ubuntu/jammy64' config.vm.box_check_update = false + config.vm.boot_timeout = 600 config.vm.provider :virtualbox do |vb| vb.memory = 4096 From 9012edd20a47f4d077b3cb3439aa2292307c890f Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Thu, 16 May 2024 09:54:57 -0700 Subject: [PATCH 12/71] [ResponseOps] Fix flaky rule details execution log E2E tests (#183577) ## Summary issue: https://github.com/elastic/kibana/issues/173666 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/triggers_actions_ui/details.ts | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 3aee927ffcfd2..b71f659b10baa 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -987,46 +987,44 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/173666 - describe.skip('Execution log', () => { + describe('Execution log', () => { const testRunUuid = uuidv4(); - let rule: any; - before(async () => { + after(async () => { + await objectRemover.removeAll(); + }); + + it('renders the event log list and can filter/sort', async () => { await pageObjects.common.navigateToApp('triggersActions'); + await testSubjects.click('rulesTab'); const alerts = [{ id: 'us-central' }]; - rule = await createRuleWithActionsAndParams( - testRunUuid, - { - instances: alerts, - }, - { - schedule: { interval: '1s' }, - } - ); - - // refresh to see rule - await browser.refresh(); - await pageObjects.header.waitUntilLoadingHasFinished(); - - // click on first rule - await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(rule.name); + const rule = await createRuleWithActionsAndParams(testRunUuid, { + instances: alerts, + }); - // await first run to complete so we have an initial state await retry.try(async () => { const { alerts: alertInstances } = await getAlertSummary(rule.id); expect(Object.keys(alertInstances).length).to.eql(alerts.length); }); - }); - after(async () => { - await objectRemover.removeAll(); - }); + // Run again to generate some event logs + await retry.try(async () => { + await supertest + .post(`/internal/alerting/rule/${rule.id}/_run_soon`) + .set('kbn-xsrf', 'foo') + .expect(204); + }); + + await pageObjects.common.navigateToApp('triggersActions'); + await testSubjects.click('rulesTab'); + await pageObjects.header.waitUntilLoadingHasFinished(); + + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(rule.name); + await pageObjects.header.waitUntilLoadingHasFinished(); - it('renders the event log list and can filter/sort', async () => { - await browser.refresh(); await (await testSubjects.find('eventLogListTab')).click(); + await pageObjects.header.waitUntilLoadingHasFinished(); // Check to see if the experimental is enabled, if not, just return const tabbedContentExists = await testSubjects.exists('ruleDetailsTabbedContent'); @@ -1034,12 +1032,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { return; } - // Ensure we have some log data to work with - await new Promise((resolve) => setTimeout(resolve, 5000)); - - const refreshButton = await testSubjects.find('superDatePickerApplyTimeButton'); - await refreshButton.click(); - // List, date picker, and status picker all exists await testSubjects.existOrFail('eventLogList'); await testSubjects.existOrFail('ruleEventLogListDatePicker'); @@ -1051,7 +1043,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(statusNumber.getVisibleText()).to.eql(0); await statusFilter.click(); + await testSubjects.click('eventLogStatusFilter-success'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await statusFilter.click(); statusFilter = await testSubjects.find('eventLogStatusFilterButton'); From 21d7d7b1d800bfa4b18a8e95e5a3c6241b9d71db Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Thu, 16 May 2024 09:55:10 -0700 Subject: [PATCH 13/71] [ResponseOps] Fix flaky rules list bulk action E2E tests (#183574) ## Summary Issue: https://github.com/elastic/kibana/issues/177130 ### 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 --- .../rules_list/bulk_actions.ts | 89 ++++++++++++------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rules_list/bulk_actions.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rules_list/bulk_actions.ts index 740cbd4f78e20..109289e76a570 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rules_list/bulk_actions.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rules_list/bulk_actions.ts @@ -35,8 +35,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('rulesTab'); } - // Failing: See https://github.com/elastic/kibana/issues/177130 - describe.skip('rules list bulk actions', () => { + describe('rules list bulk actions', () => { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('rulesTab'); @@ -59,12 +58,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await refreshAlertsList(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click(`checkboxSelectRow-${rule1.id}`); await testSubjects.click(`checkboxSelectRow-${rule2.id}`); await testSubjects.click('showBulkActionButton'); await testSubjects.click('bulkSnooze'); await testSubjects.existOrFail('snoozePanel'); await testSubjects.click('linkSnooze1h'); + await pageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async () => { const toastTitle = await toasts.getTitleAndDismiss(); @@ -72,8 +74,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await pageObjects.triggersActionsUI.searchAlerts(rule1.name); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.existOrFail('rulesListNotifyBadge-snoozed'); + await pageObjects.triggersActionsUI.searchAlerts(rule2.name); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.existOrFail('rulesListNotifyBadge-snoozed'); }); @@ -98,12 +103,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await refreshAlertsList(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click(`checkboxSelectRow-${rule1.id}`); await testSubjects.click(`checkboxSelectRow-${rule2.id}`); await testSubjects.click('showBulkActionButton'); await testSubjects.click('bulkUnsnooze'); await testSubjects.existOrFail('bulkUnsnoozeConfirmationModal'); await testSubjects.click('confirmModalConfirmButton'); + await pageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async () => { const toastTitle = await toasts.getTitleAndDismiss(); @@ -111,12 +119,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await pageObjects.triggersActionsUI.searchAlerts(rule1.name); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.missingOrFail('rulesListNotifyBadge-snoozed'); + await pageObjects.triggersActionsUI.searchAlerts(rule2.name); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.missingOrFail('rulesListNotifyBadge-snoozed'); }); - it('should allow rules to be scheduled', async () => { + it('should allow rule snooze to be scheduled', async () => { const rule1 = await createAlert({ supertest, objectRemover, @@ -129,6 +140,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await refreshAlertsList(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click(`checkboxSelectRow-${rule1.id}`); await testSubjects.click(`checkboxSelectRow-${rule2.id}`); await testSubjects.click('showBulkActionButton'); @@ -136,14 +149,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.existOrFail('ruleSnoozeScheduler'); await testSubjects.click('scheduler-saveSchedule'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await retry.try(async () => { const toastTitle = await toasts.getTitleAndDismiss(); expect(toastTitle).to.eql('Updated snooze settings for 2 rules.'); }); await pageObjects.triggersActionsUI.searchAlerts(rule1.name); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.existOrFail('rulesListNotifyBadge-scheduled'); await pageObjects.triggersActionsUI.searchAlerts(rule2.name); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.existOrFail('rulesListNotifyBadge-scheduled'); }); @@ -168,21 +185,25 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await refreshAlertsList(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click(`checkboxSelectRow-${rule1.id}`); await testSubjects.click(`checkboxSelectRow-${rule2.id}`); await testSubjects.click('showBulkActionButton'); await testSubjects.click('bulkRemoveSnoozeSchedule'); await testSubjects.existOrFail('bulkRemoveScheduleConfirmationModal'); await testSubjects.click('confirmModalConfirmButton'); + await pageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async () => { - const toastTitle = await toasts.getTitleAndDismiss(); - expect(toastTitle).to.eql('Updated snooze settings for 2 rules.'); - }); + const toastTitle = await toasts.getTitleAndDismiss(); + expect(toastTitle).to.eql('Updated snooze settings for 2 rules.'); await pageObjects.triggersActionsUI.searchAlerts(rule1.name); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.missingOrFail('rulesListNotifyBadge-scheduled'); + await pageObjects.triggersActionsUI.searchAlerts(rule2.name); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.missingOrFail('rulesListNotifyBadge-scheduled'); }); @@ -199,6 +220,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await refreshAlertsList(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click(`checkboxSelectRow-${rule1.id}`); await testSubjects.click('selectAllRulesButton'); await testSubjects.click(`checkboxSelectRow-${rule2.id}`); @@ -206,11 +229,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('updateAPIKeys'); await testSubjects.existOrFail('updateApiKeyIdsConfirmation'); await testSubjects.click('confirmModalConfirmButton'); + await pageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async () => { - const toastTitle = await toasts.getTitleAndDismiss(); - expect(toastTitle).to.eql('Updated API key for 1 rule.'); - }); + const toastTitle = await toasts.getTitleAndDismiss(); + expect(toastTitle).to.eql('Updated API key for 1 rule.'); }); it('should apply filters to bulk actions when using the select all button', async () => { @@ -230,11 +252,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); await refreshAlertsList(); + await pageObjects.header.waitUntilLoadingHasFinished(); + expect(await testSubjects.getVisibleText('totalRulesCount')).to.be('3 rules'); await testSubjects.click('ruleTypeFilterButton'); - await testSubjects.existOrFail('ruleTypetest.noopFilterOption'); await testSubjects.click('ruleTypetest.noopFilterOption'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click('ruleTypeFilterButton'); + await testSubjects.click(`checkboxSelectRow-${rule1.id}`); await testSubjects.click('selectAllRulesButton'); @@ -242,28 +268,37 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('bulkDisable'); await testSubjects.existOrFail('untrackAlertsModal'); await testSubjects.click('confirmModalConfirmButton'); + await pageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async () => { - const toastTitle = await toasts.getTitleAndDismiss(); - expect(toastTitle).to.eql('Disabled 2 rules'); - }); + let toastTitle = await toasts.getTitleAndDismiss(); + expect(toastTitle).to.eql('Disabled 2 rules'); await testSubjects.click('rules-list-clear-filter'); await refreshAlertsList(); + await pageObjects.header.waitUntilLoadingHasFinished(); await pageObjects.triggersActionsUI.searchAlerts(rule1.name); + await pageObjects.header.waitUntilLoadingHasFinished(); expect(await testSubjects.getVisibleText('statusDropdown')).to.be('Disabled'); + await pageObjects.triggersActionsUI.searchAlerts(rule2.name); + await pageObjects.header.waitUntilLoadingHasFinished(); expect(await testSubjects.getVisibleText('statusDropdown')).to.be('Enabled'); + await pageObjects.triggersActionsUI.searchAlerts(rule3.name); + await pageObjects.header.waitUntilLoadingHasFinished(); expect(await testSubjects.getVisibleText('statusDropdown')).to.be('Disabled'); await testSubjects.click('rules-list-clear-filter'); + await pageObjects.header.waitUntilLoadingHasFinished(); await refreshAlertsList(); + await pageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.click('ruleStatusFilterButton'); - await testSubjects.existOrFail('ruleStatusFilterOption-enabled'); await testSubjects.click('ruleStatusFilterOption-enabled'); + await pageObjects.header.waitUntilLoadingHasFinished(); + await testSubjects.click('ruleStatusFilterButton'); + await testSubjects.click(`checkboxSelectRow-${rule2.id}`); await testSubjects.click('selectAllRulesButton'); @@ -271,24 +306,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('bulkDelete'); await testSubjects.existOrFail('rulesDeleteConfirmation'); await testSubjects.click('confirmModalConfirmButton'); + await pageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async () => { - const toastTitle = await toasts.getTitleAndDismiss(); - expect(toastTitle).to.eql('Deleted 1 rule'); - }); + toastTitle = await toasts.getTitleAndDismiss(); + expect(toastTitle).to.eql('Deleted 1 rule'); await testSubjects.click('rules-list-clear-filter'); + await pageObjects.header.waitUntilLoadingHasFinished(); await refreshAlertsList(); - - await retry.try( - async () => { - expect(await testSubjects.getVisibleText('totalRulesCount')).to.be('2 rules'); - }, - async () => { - // If the delete fails, make sure rule2 gets cleaned up - objectRemover.add(rule2.id, 'alert', 'alerts'); - } - ); + await pageObjects.header.waitUntilLoadingHasFinished(); + expect(await testSubjects.getVisibleText('totalRulesCount')).to.be('2 rules'); }); }); }; From be659de80321605ac4a393ff5d51d1151c051c69 Mon Sep 17 00:00:00 2001 From: Jiawei Wu <74562234+JiaweiWu@users.noreply.github.com> Date: Thu, 16 May 2024 09:55:22 -0700 Subject: [PATCH 14/71] [ResponseOps] Fix flaky global alerts page E2E tests (#183568) ## Summary Resolves: https://github.com/elastic/kibana/issues/181795 ### 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 --- .../apps/triggers_actions_ui/alerts_page.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_page.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_page.ts index 8223a5d0d29b1..6fd04fdaa23c9 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_page.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_page.ts @@ -25,9 +25,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const security = getService('security'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); const log = getService('log'); + const retry = getService('retry'); - // Failing: See https://github.com/elastic/kibana/issues/181795 - describe.skip('Stack alerts page', function () { + describe('Stack alerts page', function () { describe('Loads the page with limited privileges', () => { beforeEach(async () => { await security.testUser.restoreDefaults(); @@ -54,13 +54,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { shouldUseHashForSubUrl: false, } ); - await pageObjects.triggersActionsUI.clickAlertsPageShowQueryMenuButton(); - const quickFilters = await pageObjects.triggersActionsUI.getAlertsPageQuickFilters(); - const solutionFilters = getSolutionNamesFromFilters(quickFilters); - expect(solutionFilters).to.have.length(2); - expect(solutionFilters[0]).to.equal('Stack management'); - // Observability is included because of multi-consumer rules - expect(solutionFilters[1]).to.equal('Observability'); + + await pageObjects.header.waitUntilLoadingHasFinished(); + + await retry.try(async () => { + if (!(await testSubjects.exists('queryBarMenuPanel'))) { + await pageObjects.triggersActionsUI.clickAlertsPageShowQueryMenuButton(); + } + const quickFilters = await pageObjects.triggersActionsUI.getAlertsPageQuickFilters(); + const solutionFilters = getSolutionNamesFromFilters(quickFilters); + expect(solutionFilters).to.have.length(2); + expect(solutionFilters[0]).to.equal('Stack management'); + // Observability is included because of multi-consumer rules + expect(solutionFilters[1]).to.equal('Observability'); + }); }); }); From 2e9f1baf1355f9a433f05d3e100f3b929065f9a1 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Thu, 16 May 2024 12:56:51 -0400 Subject: [PATCH 15/71] fix(slo): history details data (#183097) --- .../routes/fetch_historical_summary.ts | 6 +- .../src/rest_specs/routes/get_preview_data.ts | 4 +- .../kbn-slo-schema/src/schema/common.ts | 4 +- .../slo/error_rate_chart/error_rate_chart.tsx | 6 +- .../slo/public/hooks/query_key_factory.ts | 2 +- .../hooks/use_fetch_historical_summary.ts | 11 +- .../slo/public/hooks/use_get_preview_data.ts | 2 +- .../components/events_chart_panel.tsx | 9 +- .../components/historical_data_charts.tsx | 6 +- .../history/slo_details_history.tsx | 47 +- .../slo_details/components/slo_details.tsx | 16 +- .../slo_details/components/wide_chart.tsx | 4 +- .../slo/public/pages/slo_details/types.ts | 6 +- .../components/common/data_preview_chart.tsx | 14 +- ...etics_availability_indicator_type_form.tsx | 21 +- .../pages/slo_edit/hooks/use_preview.ts | 2 +- .../common/good_bad_events_chart.tsx | 4 +- .../slo/public/utils/slo/duration.ts | 9 +- .../slo/server/routes/slo/route.ts | 9 +- .../historical_summary_client.test.ts.snap | 4516 +++++++++++++---- .../slo/server/services/burn_rates_client.ts | 3 +- .../services/fetch_historical_summary.ts | 25 - .../slo/server/services/get_preview_data.ts | 9 +- .../historical_summary_client.test.ts | 132 +- .../services/historical_summary_client.ts | 133 +- .../slo/server/services/index.ts | 1 - .../slo/server/services/summary_client.ts | 6 +- 27 files changed, 3906 insertions(+), 1101 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/slo/server/services/fetch_historical_summary.ts diff --git a/x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts b/x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts index 0df546beecc4c..7912c3ccbedc1 100644 --- a/x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts +++ b/x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts @@ -14,6 +14,7 @@ import { import { allOrAnyString, allOrAnyStringOrArray, + dateRangeSchema, dateType, summarySchema, } from '../../schema/common'; @@ -33,10 +34,7 @@ const fetchHistoricalSummaryParamsSchema = t.type({ }), t.partial({ remoteName: t.string, - range: t.type({ - from: t.string, - to: t.string, - }), + range: dateRangeSchema, }), ]) ), diff --git a/x-pack/packages/kbn-slo-schema/src/rest_specs/routes/get_preview_data.ts b/x-pack/packages/kbn-slo-schema/src/rest_specs/routes/get_preview_data.ts index 9cc0023704453..aa46600cb3442 100644 --- a/x-pack/packages/kbn-slo-schema/src/rest_specs/routes/get_preview_data.ts +++ b/x-pack/packages/kbn-slo-schema/src/rest_specs/routes/get_preview_data.ts @@ -13,8 +13,8 @@ const getPreviewDataParamsSchema = t.type({ t.type({ indicator: indicatorSchema, range: t.type({ - start: t.number, - end: t.number, + from: dateType, + to: dateType, }), }), t.partial({ diff --git a/x-pack/packages/kbn-slo-schema/src/schema/common.ts b/x-pack/packages/kbn-slo-schema/src/schema/common.ts index a803bcf735796..e2b6d5af0a023 100644 --- a/x-pack/packages/kbn-slo-schema/src/schema/common.ts +++ b/x-pack/packages/kbn-slo-schema/src/schema/common.ts @@ -87,8 +87,8 @@ const groupSummarySchema = t.type({ }); const dateRangeSchema = t.type({ - from: t.union([dateType, t.string]), - to: t.union([dateType, t.string]), + from: dateType, + to: dateType, }); export { diff --git a/x-pack/plugins/observability_solution/slo/public/components/slo/error_rate_chart/error_rate_chart.tsx b/x-pack/plugins/observability_solution/slo/public/components/slo/error_rate_chart/error_rate_chart.tsx index e42f5a9115bb6..793ebdb89ba6b 100644 --- a/x-pack/plugins/observability_solution/slo/public/components/slo/error_rate_chart/error_rate_chart.tsx +++ b/x-pack/plugins/observability_solution/slo/public/components/slo/error_rate_chart/error_rate_chart.tsx @@ -64,10 +64,8 @@ export function ErrorRateChart({ viewMode={ViewMode.VIEW} onBrushEnd={({ range }) => { onBrushed?.({ - from: range[0], - to: range[1], - fromUtc: moment(range[0]).format(), - toUtc: moment(range[1]).format(), + from: moment(range[0]).toDate(), + to: moment(range[1]).toDate(), }); }} noPadding diff --git a/x-pack/plugins/observability_solution/slo/public/hooks/query_key_factory.ts b/x-pack/plugins/observability_solution/slo/public/hooks/query_key_factory.ts index a493a9cc27066..11f10b624106f 100644 --- a/x-pack/plugins/observability_solution/slo/public/hooks/query_key_factory.ts +++ b/x-pack/plugins/observability_solution/slo/public/hooks/query_key_factory.ts @@ -62,7 +62,7 @@ export const sloKeys = { ) => [...sloKeys.all, 'burnRates', sloId, instanceId, windows] as const, preview: ( indicator: Indicator, - range: { start: number; end: number }, + range: { from: Date; to: Date }, groupings?: Record ) => [...sloKeys.all, 'preview', indicator, range, groupings] as const, burnRateRules: (search: string) => [...sloKeys.all, 'burnRateRules', search], diff --git a/x-pack/plugins/observability_solution/slo/public/hooks/use_fetch_historical_summary.ts b/x-pack/plugins/observability_solution/slo/public/hooks/use_fetch_historical_summary.ts index 95abc489d69de..fc77e0ba40c7a 100644 --- a/x-pack/plugins/observability_solution/slo/public/hooks/use_fetch_historical_summary.ts +++ b/x-pack/plugins/observability_solution/slo/public/hooks/use_fetch_historical_summary.ts @@ -24,8 +24,8 @@ export interface Params { sloList: SLOWithSummaryResponse[]; shouldRefetch?: boolean; range?: { - from: string; - to: string; + from: Date; + to: Date; }; } @@ -45,7 +45,12 @@ export function useFetchHistoricalSummary({ revision: slo.revision, objective: slo.objective, budgetingMethod: slo.budgetingMethod, - range, + range: range + ? { + from: range?.from.toISOString(), + to: range?.to.toISOString(), + } + : undefined, })); const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({ diff --git a/x-pack/plugins/observability_solution/slo/public/hooks/use_get_preview_data.ts b/x-pack/plugins/observability_solution/slo/public/hooks/use_get_preview_data.ts index fc032e96cf9cc..316c5300b9f9a 100644 --- a/x-pack/plugins/observability_solution/slo/public/hooks/use_get_preview_data.ts +++ b/x-pack/plugins/observability_solution/slo/public/hooks/use_get_preview_data.ts @@ -35,7 +35,7 @@ export function useGetPreviewData({ groupings?: Record; objective?: Objective; indicator: Indicator; - range: { start: number; end: number }; + range: { from: Date; to: Date }; }): UseGetPreviewData { const { http } = useKibana().services; diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/events_chart_panel.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/events_chart_panel.tsx index 340d6ce465884..bb99a6ce7c2c6 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/events_chart_panel.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/events_chart_panel.tsx @@ -37,7 +37,7 @@ import moment from 'moment'; import React, { useRef } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { TimeBounds } from '../types'; -import { getBrushData } from '../../../utils/slo/duration'; +import { getBrushTimeBounds } from '../../../utils/slo/duration'; import { SloTabId } from './slo_details'; import { useGetPreviewData } from '../../../hooks/use_get_preview_data'; import { useKibana } from '../../../utils/kibana_react'; @@ -47,10 +47,7 @@ import { getDiscoverLink } from '../../../utils/slo/get_discover_link'; export interface Props { slo: SLOWithSummaryResponse; - range: { - start: number; - end: number; - }; + range: { from: Date; to: Date }; selectedTabId: SloTabId; onBrushed?: (timeBounds: TimeBounds) => void; } @@ -234,7 +231,7 @@ export function EventsChartPanel({ slo, range, selectedTabId, onBrushed }: Props pointerUpdateTrigger={'x'} locale={i18n.getLocale()} onBrushEnd={(brushArea) => { - onBrushed?.(getBrushData(brushArea)); + onBrushed?.(getBrushTimeBounds(brushArea)); }} /> {annotation} diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/historical_data_charts.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/historical_data_charts.tsx index f2e096195f474..da766b765cac7 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/historical_data_charts.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/historical_data_charts.tsx @@ -18,12 +18,10 @@ export interface Props { slo: SLOWithSummaryResponse; isAutoRefreshing: boolean; selectedTabId: SloTabId; - range?: { - from: string; - to: string; - }; + range?: { from: Date; to: Date }; onBrushed?: (timeBounds: TimeBounds) => void; } + export function HistoricalDataCharts({ slo, range, diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/history/slo_details_history.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/history/slo_details_history.tsx index 0e20cb3960fbc..7ecf4d4672775 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/history/slo_details_history.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/history/slo_details_history.tsx @@ -4,23 +4,25 @@ * 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 { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { EuiFlexGroup, EuiFlexItem, + EuiSpacer, EuiSuperDatePicker, - OnTimeChangeProps, OnRefreshProps, - EuiSpacer, + OnTimeChangeProps, } from '@elastic/eui'; import DateMath from '@kbn/datemath'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import React, { useMemo, useState } from 'react'; +import { BurnRates } from '../../../../components/slo/burn_rate/burn_rates'; import { useKibana } from '../../../../utils/kibana_react'; -import { HistoricalDataCharts } from '../historical_data_charts'; import { useBurnRateOptions } from '../../hooks/use_burn_rate_options'; -import { SloTabId } from '../slo_details'; -import { BurnRates } from '../../../../components/slo/burn_rate/burn_rates'; +import { TimeBounds } from '../../types'; import { EventsChartPanel } from '../events_chart_panel'; +import { HistoricalDataCharts } from '../historical_data_charts'; +import { SloTabId } from '../slo_details'; + export interface Props { slo: SLOWithSummaryResponse; isAutoRefreshing: boolean; @@ -29,10 +31,8 @@ export interface Props { export function SLODetailsHistory({ slo, isAutoRefreshing, selectedTabId }: Props) { const { uiSettings } = useKibana().services; - const { burnRateOptions } = useBurnRateOptions(slo); - - const [start, setStart] = useState('now-30d'); + const [start, setStart] = useState(`now-${slo.timeWindow.duration}`); const [end, setEnd] = useState('now'); const onTimeChange = (val: OnTimeChangeProps) => { @@ -42,19 +42,17 @@ export function SLODetailsHistory({ slo, isAutoRefreshing, selectedTabId }: Prop const onRefresh = (val: OnRefreshProps) => {}; - const absRange = useMemo(() => { + const range = useMemo(() => { return { from: new Date(DateMath.parse(start)!.valueOf()), to: new Date(DateMath.parse(end, { roundUp: true })!.valueOf()), - absoluteFrom: DateMath.parse(start)!.valueOf(), - absoluteTo: DateMath.parse(end, { roundUp: true })!.valueOf(), }; }, [start, end]); - const onBrushed = useCallback(({ fromUtc, toUtc }) => { - setStart(fromUtc); - setEnd(toUtc); - }, []); + const onBrushed = ({ from, to }: TimeBounds) => { + setStart(from.toISOString()); + setEnd(to.toISOString()); + }; return ( <> @@ -94,10 +92,7 @@ export function SLODetailsHistory({ slo, isAutoRefreshing, selectedTabId }: Prop isAutoRefreshing={isAutoRefreshing} burnRateOptions={burnRateOptions} selectedTabId={selectedTabId} - range={{ - from: absRange.from, - to: absRange.to, - }} + range={range} onBrushed={onBrushed} /> @@ -105,19 +100,13 @@ export function SLODetailsHistory({ slo, isAutoRefreshing, selectedTabId }: Prop slo={slo} selectedTabId={selectedTabId} isAutoRefreshing={isAutoRefreshing} - range={{ - from: start, - to: end, - }} + range={range} onBrushed={onBrushed} /> diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/slo_details.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/slo_details.tsx index cb5a1420dc06a..249984fb3ed56 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/slo_details.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/slo_details.tsx @@ -7,12 +7,13 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import moment from 'moment'; import React, { useEffect, useState } from 'react'; -import { HistoricalDataCharts } from './historical_data_charts'; -import { useBurnRateOptions } from '../hooks/use_burn_rate_options'; -import { SLODetailsHistory } from './history/slo_details_history'; import { BurnRates } from '../../../components/slo/burn_rate/burn_rates'; +import { useBurnRateOptions } from '../hooks/use_burn_rate_options'; import { EventsChartPanel } from './events_chart_panel'; +import { HistoricalDataCharts } from './historical_data_charts'; +import { SLODetailsHistory } from './history/slo_details_history'; import { Overview } from './overview/overview'; import { SloDetailsAlerts } from './slo_detail_alerts'; import { SloHealthCallout } from './slo_health_callout'; @@ -22,7 +23,6 @@ export const TAB_ID_URL_PARAM = 'tabId'; export const OVERVIEW_TAB_ID = 'overview'; export const HISTORY_TAB_ID = 'history'; export const ALERTS_TAB_ID = 'alerts'; -const DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000; export type SloTabId = typeof OVERVIEW_TAB_ID | typeof ALERTS_TAB_ID | typeof HISTORY_TAB_ID; @@ -34,16 +34,16 @@ export interface Props { export function SloDetails({ slo, isAutoRefreshing, selectedTabId }: Props) { const { burnRateOptions } = useBurnRateOptions(slo); - const [range, setRange] = useState({ - start: new Date().getTime() - DAY_IN_MILLISECONDS, - end: new Date().getTime(), + const [range, setRange] = useState<{ from: Date; to: Date }>({ + from: moment().subtract(1, 'day').toDate(), + to: new Date(), }); useEffect(() => { let intervalId: any; if (isAutoRefreshing) { intervalId = setInterval(() => { - setRange({ start: new Date().getTime() - DAY_IN_MILLISECONDS, end: new Date().getTime() }); + setRange({ from: moment().subtract(1, 'day').toDate(), to: new Date() }); }, 60 * 1000); } diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/wide_chart.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/wide_chart.tsx index 5d6d68ae7d892..5ba7effaa9a99 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/wide_chart.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_details/components/wide_chart.tsx @@ -24,7 +24,7 @@ import moment from 'moment'; import React, { useRef } from 'react'; import { i18n } from '@kbn/i18n'; -import { getBrushData } from '../../../utils/slo/duration'; +import { getBrushTimeBounds } from '../../../utils/slo/duration'; import { TimeBounds } from '../types'; import { useKibana } from '../../../utils/kibana_react'; import { ChartData } from '../../../typings'; @@ -84,7 +84,7 @@ export function WideChart({ chart, data, id, isLoading, state, onBrushed }: Prop pointerUpdateTrigger={'x'} locale={i18n.getLocale()} onBrushEnd={(brushArea) => { - onBrushed?.(getBrushData(brushArea)); + onBrushed?.(getBrushTimeBounds(brushArea)); }} /> row.sliValue && row.sliValue > 1) != null; diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.tsx index a1ec5714de504..b796f1b25633d 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/synthetics_availability/synthetics_availability_indicator_type_form.tsx @@ -4,25 +4,24 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import { FilterStateStore } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { ALL_VALUE, - SyntheticsAvailabilityIndicator, - QuerySchema, FiltersSchema, + QuerySchema, + SyntheticsAvailabilityIndicator, } from '@kbn/slo-schema'; -import { FilterStateStore } from '@kbn/es-query'; +import moment from 'moment'; +import React, { useState } from 'react'; import { useFormContext } from 'react-hook-form'; -import { FieldSelector } from '../synthetics_common/field_selector'; +import { formatAllFilters } from '../../helpers/format_filters'; import { CreateSLOForm } from '../../types'; import { DataPreviewChart } from '../common/data_preview_chart'; -import { QueryBuilder } from '../common/query_builder'; import { GroupByCardinality } from '../common/group_by_cardinality'; -import { formatAllFilters } from '../../helpers/format_filters'; - -const ONE_DAY_IN_MILLISECONDS = 1 * 60 * 60 * 1000 * 24; +import { QueryBuilder } from '../common/query_builder'; +import { FieldSelector } from '../synthetics_common/field_selector'; export function SyntheticsAvailabilityIndicatorTypeForm() { const { watch } = useFormContext>(); @@ -36,8 +35,8 @@ export function SyntheticsAvailabilityIndicatorTypeForm() { ]); const [range, _] = useState({ - start: new Date().getTime() - ONE_DAY_IN_MILLISECONDS, - end: new Date().getTime(), + from: moment().subtract(1, 'day').toDate(), + to: new Date(), }); const filters = { diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_preview.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_preview.ts index 8d21ac3938b8d..bab6492ac3bd1 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_preview.ts +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/hooks/use_preview.ts @@ -13,7 +13,7 @@ import { useGetPreviewData } from '../../../hooks/use_get_preview_data'; export function useDebouncedGetPreviewData( isIndicatorValid: boolean, indicator: Indicator, - range: { start: number; end: number } + range: { from: Date; to: Date } ) { const serializedIndicator = JSON.stringify(indicator); const [indicatorState, setIndicatorState] = useState(serializedIndicator); diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slos/components/common/good_bad_events_chart.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slos/components/common/good_bad_events_chart.tsx index 3f0e88b912ec2..6d65c4e344d3d 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slos/components/common/good_bad_events_chart.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slos/components/common/good_bad_events_chart.tsx @@ -24,7 +24,7 @@ import { GetPreviewDataResponse, SLOWithSummaryResponse } from '@kbn/slo-schema' import moment from 'moment'; import React, { useRef } from 'react'; import { TimeBounds } from '../../../slo_details/types'; -import { getBrushData } from '../../../../utils/slo/duration'; +import { getBrushTimeBounds } from '../../../../utils/slo/duration'; import { useKibana } from '../../../../utils/kibana_react'; import { openInDiscover } from '../../../../utils/slo/get_discover_link'; @@ -120,7 +120,7 @@ export function GoodBadEventsChart({ locale={i18n.getLocale()} onElementClick={barClickHandler as ElementClickListener} onBrushEnd={(brushArea) => { - onBrushed?.(getBrushData(brushArea)); + onBrushed?.(getBrushTimeBounds(brushArea)); }} /> {annotation} diff --git a/x-pack/plugins/observability_solution/slo/public/utils/slo/duration.ts b/x-pack/plugins/observability_solution/slo/public/utils/slo/duration.ts index c4d0af54a893a..d577feb263ce7 100644 --- a/x-pack/plugins/observability_solution/slo/public/utils/slo/duration.ts +++ b/x-pack/plugins/observability_solution/slo/public/utils/slo/duration.ts @@ -9,6 +9,7 @@ import moment from 'moment'; import { assertNever } from '@kbn/std'; import { BrushEvent } from '@elastic/charts'; import { Duration, DurationUnit } from '../../typings'; +import { TimeBounds } from '../../pages/slo_details/types'; export function toDuration(duration: string): Duration { const durationValue = duration.substring(0, duration.length - 1); @@ -44,9 +45,9 @@ export function toCalendarAlignedMomentUnitOfTime(unit: string): moment.unitOfTi } } -export function getBrushData(e: BrushEvent) { - const [from, to] = [Number(e.x?.[0]), Number(e.x?.[1])]; - const [fromUtc, toUtc] = [moment(from).format(), moment(to).format()]; +export function getBrushTimeBounds(e: BrushEvent): TimeBounds { + const from = moment(Number(e.x?.[0])).toDate(); + const to = moment(Number(e.x?.[1])).toDate(); - return { from, to, fromUtc, toUtc }; + return { from, to }; } diff --git a/x-pack/plugins/observability_solution/slo/server/routes/slo/route.ts b/x-pack/plugins/observability_solution/slo/server/routes/slo/route.ts index d7e6d02583376..61c081b087e15 100644 --- a/x-pack/plugins/observability_solution/slo/server/routes/slo/route.ts +++ b/x-pack/plugins/observability_solution/slo/server/routes/slo/route.ts @@ -12,12 +12,13 @@ import { deleteSLOInstancesParamsSchema, deleteSLOParamsSchema, fetchHistoricalSummaryParamsSchema, + fetchHistoricalSummaryResponseSchema, + fetchSLOHealthParamsSchema, findSloDefinitionsParamsSchema, findSLOGroupsParamsSchema, findSLOParamsSchema, getPreviewDataParamsSchema, getSLOBurnRatesParamsSchema, - fetchSLOHealthParamsSchema, getSLOInstancesParamsSchema, getSLOParamsSchema, manageSLOParamsSchema, @@ -44,7 +45,6 @@ import { KibanaSavedObjectsSLORepository, UpdateSLO, } from '../../services'; -import { FetchHistoricalSummary } from '../../services/fetch_historical_summary'; import { FindSLODefinitions } from '../../services/find_slo_definitions'; import { getBurnRates } from '../../services/get_burn_rates'; import { getGlobalDiagnosis } from '../../services/get_diagnosis'; @@ -513,9 +513,10 @@ const fetchHistoricalSummary = createSloServerRoute({ const esClient = (await context.core).elasticsearch.client.asCurrentUser; const historicalSummaryClient = new DefaultHistoricalSummaryClient(esClient); - const fetchSummaryData = new FetchHistoricalSummary(historicalSummaryClient); - return await fetchSummaryData.execute(params.body); + const historicalSummary = await historicalSummaryClient.fetch(params.body); + + return fetchHistoricalSummaryResponseSchema.encode(historicalSummary); }, }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/historical_summary_client.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/historical_summary_client.test.ts.snap index 0609ce39540c0..2759ac43d2149 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/historical_summary_client.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/historical_summary_client.test.ts.snap @@ -3024,7 +3024,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 1`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 1`] = ` Object { "date": Any, "errorBudget": Object { @@ -3038,7 +3038,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 2`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 2`] = ` Object { "date": Any, "errorBudget": Object { @@ -3052,7 +3052,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 3`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 3`] = ` Object { "date": Any, "errorBudget": Object { @@ -3066,7 +3066,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 4`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 4`] = ` Object { "date": Any, "errorBudget": Object { @@ -3080,7 +3080,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 5`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 5`] = ` Object { "date": Any, "errorBudget": Object { @@ -3094,7 +3094,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 6`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 6`] = ` Object { "date": Any, "errorBudget": Object { @@ -3108,7 +3108,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 7`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 7`] = ` Object { "date": Any, "errorBudget": Object { @@ -3122,7 +3122,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 8`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 8`] = ` Object { "date": Any, "errorBudget": Object { @@ -3136,7 +3136,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 9`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 9`] = ` Object { "date": Any, "errorBudget": Object { @@ -3150,7 +3150,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 10`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 10`] = ` Object { "date": Any, "errorBudget": Object { @@ -3164,7 +3164,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 11`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 11`] = ` Object { "date": Any, "errorBudget": Object { @@ -3178,7 +3178,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 12`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 12`] = ` Object { "date": Any, "errorBudget": Object { @@ -3192,7 +3192,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 13`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 13`] = ` Object { "date": Any, "errorBudget": Object { @@ -3206,7 +3206,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 14`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 14`] = ` Object { "date": Any, "errorBudget": Object { @@ -3220,7 +3220,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 15`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 15`] = ` Object { "date": Any, "errorBudget": Object { @@ -3234,7 +3234,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 16`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 16`] = ` Object { "date": Any, "errorBudget": Object { @@ -3248,7 +3248,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 17`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 17`] = ` Object { "date": Any, "errorBudget": Object { @@ -3262,7 +3262,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 18`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 18`] = ` Object { "date": Any, "errorBudget": Object { @@ -3276,7 +3276,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 19`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 19`] = ` Object { "date": Any, "errorBudget": Object { @@ -3290,7 +3290,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 20`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 20`] = ` Object { "date": Any, "errorBudget": Object { @@ -3304,7 +3304,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 21`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 21`] = ` Object { "date": Any, "errorBudget": Object { @@ -3318,7 +3318,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 22`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 22`] = ` Object { "date": Any, "errorBudget": Object { @@ -3332,7 +3332,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 23`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 23`] = ` Object { "date": Any, "errorBudget": Object { @@ -3346,7 +3346,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 24`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 24`] = ` Object { "date": Any, "errorBudget": Object { @@ -3360,7 +3360,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 25`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 25`] = ` Object { "date": Any, "errorBudget": Object { @@ -3374,7 +3374,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 26`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 26`] = ` Object { "date": Any, "errorBudget": Object { @@ -3388,7 +3388,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 27`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 27`] = ` Object { "date": Any, "errorBudget": Object { @@ -3402,7 +3402,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 28`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 28`] = ` Object { "date": Any, "errorBudget": Object { @@ -3416,7 +3416,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 29`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 29`] = ` Object { "date": Any, "errorBudget": Object { @@ -3430,7 +3430,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 30`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 30`] = ` Object { "date": Any, "errorBudget": Object { @@ -3444,7 +3444,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 31`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 31`] = ` Object { "date": Any, "errorBudget": Object { @@ -3458,7 +3458,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 32`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 32`] = ` Object { "date": Any, "errorBudget": Object { @@ -3472,7 +3472,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 33`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 33`] = ` Object { "date": Any, "errorBudget": Object { @@ -3486,7 +3486,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 34`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 34`] = ` Object { "date": Any, "errorBudget": Object { @@ -3500,7 +3500,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 35`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 35`] = ` Object { "date": Any, "errorBudget": Object { @@ -3514,7 +3514,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 36`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 36`] = ` Object { "date": Any, "errorBudget": Object { @@ -3528,7 +3528,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 37`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 37`] = ` Object { "date": Any, "errorBudget": Object { @@ -3542,7 +3542,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 38`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 38`] = ` Object { "date": Any, "errorBudget": Object { @@ -3556,7 +3556,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 39`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 39`] = ` Object { "date": Any, "errorBudget": Object { @@ -3570,7 +3570,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 40`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 40`] = ` Object { "date": Any, "errorBudget": Object { @@ -3584,7 +3584,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 41`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 41`] = ` Object { "date": Any, "errorBudget": Object { @@ -3598,7 +3598,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 42`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 42`] = ` Object { "date": Any, "errorBudget": Object { @@ -3612,7 +3612,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 43`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 43`] = ` Object { "date": Any, "errorBudget": Object { @@ -3626,7 +3626,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 44`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 44`] = ` Object { "date": Any, "errorBudget": Object { @@ -3640,7 +3640,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 45`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 45`] = ` Object { "date": Any, "errorBudget": Object { @@ -3654,7 +3654,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 46`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 46`] = ` Object { "date": Any, "errorBudget": Object { @@ -3668,7 +3668,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 47`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 47`] = ` Object { "date": Any, "errorBudget": Object { @@ -3682,7 +3682,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 48`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 48`] = ` Object { "date": Any, "errorBudget": Object { @@ -3696,7 +3696,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 49`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 49`] = ` Object { "date": Any, "errorBudget": Object { @@ -3710,7 +3710,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 50`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 50`] = ` Object { "date": Any, "errorBudget": Object { @@ -3724,7 +3724,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 51`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 51`] = ` Object { "date": Any, "errorBudget": Object { @@ -3738,7 +3738,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 52`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 52`] = ` Object { "date": Any, "errorBudget": Object { @@ -3752,7 +3752,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 53`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 53`] = ` Object { "date": Any, "errorBudget": Object { @@ -3766,7 +3766,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 54`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 54`] = ` Object { "date": Any, "errorBudget": Object { @@ -3780,7 +3780,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 55`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 55`] = ` Object { "date": Any, "errorBudget": Object { @@ -3794,7 +3794,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 56`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 56`] = ` Object { "date": Any, "errorBudget": Object { @@ -3808,7 +3808,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 57`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 57`] = ` Object { "date": Any, "errorBudget": Object { @@ -3822,7 +3822,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 58`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 58`] = ` Object { "date": Any, "errorBudget": Object { @@ -3836,7 +3836,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 59`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 59`] = ` Object { "date": Any, "errorBudget": Object { @@ -3850,7 +3850,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 60`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 60`] = ` Object { "date": Any, "errorBudget": Object { @@ -3864,7 +3864,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 61`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 61`] = ` Object { "date": Any, "errorBudget": Object { @@ -3878,7 +3878,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 62`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 62`] = ` Object { "date": Any, "errorBudget": Object { @@ -3892,7 +3892,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 63`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 63`] = ` Object { "date": Any, "errorBudget": Object { @@ -3906,7 +3906,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 64`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 64`] = ` Object { "date": Any, "errorBudget": Object { @@ -3920,7 +3920,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 65`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 65`] = ` Object { "date": Any, "errorBudget": Object { @@ -3934,7 +3934,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 66`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 66`] = ` Object { "date": Any, "errorBudget": Object { @@ -3948,7 +3948,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 67`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 67`] = ` Object { "date": Any, "errorBudget": Object { @@ -3962,7 +3962,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 68`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 68`] = ` Object { "date": Any, "errorBudget": Object { @@ -3976,7 +3976,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 69`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 69`] = ` Object { "date": Any, "errorBudget": Object { @@ -3990,7 +3990,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 70`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 70`] = ` Object { "date": Any, "errorBudget": Object { @@ -4004,7 +4004,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 71`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 71`] = ` Object { "date": Any, "errorBudget": Object { @@ -4018,7 +4018,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 72`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 72`] = ` Object { "date": Any, "errorBudget": Object { @@ -4032,7 +4032,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 73`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 73`] = ` Object { "date": Any, "errorBudget": Object { @@ -4046,7 +4046,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 74`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 74`] = ` Object { "date": Any, "errorBudget": Object { @@ -4060,7 +4060,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 75`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 75`] = ` Object { "date": Any, "errorBudget": Object { @@ -4074,7 +4074,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 76`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 76`] = ` Object { "date": Any, "errorBudget": Object { @@ -4088,7 +4088,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 77`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 77`] = ` Object { "date": Any, "errorBudget": Object { @@ -4102,7 +4102,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 78`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 78`] = ` Object { "date": Any, "errorBudget": Object { @@ -4116,7 +4116,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 79`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 79`] = ` Object { "date": Any, "errorBudget": Object { @@ -4130,7 +4130,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 80`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 80`] = ` Object { "date": Any, "errorBudget": Object { @@ -4144,7 +4144,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 81`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 81`] = ` Object { "date": Any, "errorBudget": Object { @@ -4158,7 +4158,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 82`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 82`] = ` Object { "date": Any, "errorBudget": Object { @@ -4172,7 +4172,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 83`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 83`] = ` Object { "date": Any, "errorBudget": Object { @@ -4186,7 +4186,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 84`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 84`] = ` Object { "date": Any, "errorBudget": Object { @@ -4200,7 +4200,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 85`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 85`] = ` Object { "date": Any, "errorBudget": Object { @@ -4214,7 +4214,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 86`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 86`] = ` Object { "date": Any, "errorBudget": Object { @@ -4228,7 +4228,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 87`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 87`] = ` Object { "date": Any, "errorBudget": Object { @@ -4242,7 +4242,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 88`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 88`] = ` Object { "date": Any, "errorBudget": Object { @@ -4256,7 +4256,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 89`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 89`] = ` Object { "date": Any, "errorBudget": Object { @@ -4270,7 +4270,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 90`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 90`] = ` Object { "date": Any, "errorBudget": Object { @@ -4284,7 +4284,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 91`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 91`] = ` Object { "date": Any, "errorBudget": Object { @@ -4298,7 +4298,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 92`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 92`] = ` Object { "date": Any, "errorBudget": Object { @@ -4312,7 +4312,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 93`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 93`] = ` Object { "date": Any, "errorBudget": Object { @@ -4326,7 +4326,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 94`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 94`] = ` Object { "date": Any, "errorBudget": Object { @@ -4340,7 +4340,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 95`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 95`] = ` Object { "date": Any, "errorBudget": Object { @@ -4354,7 +4354,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 96`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 96`] = ` Object { "date": Any, "errorBudget": Object { @@ -4368,7 +4368,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 97`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 97`] = ` Object { "date": Any, "errorBudget": Object { @@ -4382,7 +4382,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 98`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 98`] = ` Object { "date": Any, "errorBudget": Object { @@ -4396,7 +4396,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 99`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 99`] = ` Object { "date": Any, "errorBudget": Object { @@ -4410,7 +4410,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 100`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 100`] = ` Object { "date": Any, "errorBudget": Object { @@ -4424,7 +4424,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 101`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 101`] = ` Object { "date": Any, "errorBudget": Object { @@ -4438,7 +4438,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 102`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 102`] = ` Object { "date": Any, "errorBudget": Object { @@ -4452,7 +4452,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 103`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 103`] = ` Object { "date": Any, "errorBudget": Object { @@ -4466,7 +4466,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 104`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 104`] = ` Object { "date": Any, "errorBudget": Object { @@ -4480,7 +4480,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 105`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 105`] = ` Object { "date": Any, "errorBudget": Object { @@ -4494,7 +4494,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 106`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 106`] = ` Object { "date": Any, "errorBudget": Object { @@ -4508,7 +4508,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 107`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 107`] = ` Object { "date": Any, "errorBudget": Object { @@ -4522,7 +4522,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 108`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 108`] = ` Object { "date": Any, "errorBudget": Object { @@ -4536,7 +4536,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 109`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 109`] = ` Object { "date": Any, "errorBudget": Object { @@ -4550,7 +4550,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 110`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 110`] = ` Object { "date": Any, "errorBudget": Object { @@ -4564,7 +4564,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 111`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 111`] = ` Object { "date": Any, "errorBudget": Object { @@ -4578,7 +4578,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 112`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 112`] = ` Object { "date": Any, "errorBudget": Object { @@ -4592,7 +4592,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 113`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 113`] = ` Object { "date": Any, "errorBudget": Object { @@ -4606,7 +4606,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 114`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 114`] = ` Object { "date": Any, "errorBudget": Object { @@ -4620,7 +4620,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 115`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 115`] = ` Object { "date": Any, "errorBudget": Object { @@ -4634,7 +4634,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 116`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 116`] = ` Object { "date": Any, "errorBudget": Object { @@ -4648,7 +4648,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 117`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 117`] = ` Object { "date": Any, "errorBudget": Object { @@ -4662,7 +4662,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 118`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 118`] = ` Object { "date": Any, "errorBudget": Object { @@ -4676,7 +4676,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 119`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 119`] = ` Object { "date": Any, "errorBudget": Object { @@ -4690,7 +4690,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 120`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 120`] = ` Object { "date": Any, "errorBudget": Object { @@ -4704,7 +4704,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 121`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 121`] = ` Object { "date": Any, "errorBudget": Object { @@ -4718,7 +4718,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 122`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 122`] = ` Object { "date": Any, "errorBudget": Object { @@ -4732,7 +4732,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 123`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 123`] = ` Object { "date": Any, "errorBudget": Object { @@ -4746,7 +4746,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 124`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 124`] = ` Object { "date": Any, "errorBudget": Object { @@ -4760,7 +4760,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 125`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 125`] = ` Object { "date": Any, "errorBudget": Object { @@ -4774,7 +4774,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 126`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 126`] = ` Object { "date": Any, "errorBudget": Object { @@ -4788,7 +4788,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 127`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 127`] = ` Object { "date": Any, "errorBudget": Object { @@ -4802,7 +4802,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 128`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 128`] = ` Object { "date": Any, "errorBudget": Object { @@ -4816,7 +4816,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 129`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 129`] = ` Object { "date": Any, "errorBudget": Object { @@ -4830,7 +4830,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 130`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 130`] = ` Object { "date": Any, "errorBudget": Object { @@ -4844,7 +4844,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 131`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 131`] = ` Object { "date": Any, "errorBudget": Object { @@ -4858,7 +4858,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 132`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 132`] = ` Object { "date": Any, "errorBudget": Object { @@ -4872,7 +4872,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 133`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 133`] = ` Object { "date": Any, "errorBudget": Object { @@ -4886,7 +4886,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 134`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 134`] = ` Object { "date": Any, "errorBudget": Object { @@ -4900,7 +4900,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 135`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 135`] = ` Object { "date": Any, "errorBudget": Object { @@ -4914,7 +4914,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 136`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 136`] = ` Object { "date": Any, "errorBudget": Object { @@ -4928,7 +4928,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 137`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 137`] = ` Object { "date": Any, "errorBudget": Object { @@ -4942,7 +4942,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 138`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 138`] = ` Object { "date": Any, "errorBudget": Object { @@ -4956,7 +4956,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 139`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 139`] = ` Object { "date": Any, "errorBudget": Object { @@ -4970,7 +4970,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 140`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 140`] = ` Object { "date": Any, "errorBudget": Object { @@ -4984,7 +4984,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 141`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 141`] = ` Object { "date": Any, "errorBudget": Object { @@ -4998,7 +4998,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 142`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 142`] = ` Object { "date": Any, "errorBudget": Object { @@ -5012,7 +5012,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 143`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 143`] = ` Object { "date": Any, "errorBudget": Object { @@ -5026,7 +5026,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 144`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 144`] = ` Object { "date": Any, "errorBudget": Object { @@ -5040,7 +5040,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 145`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 145`] = ` Object { "date": Any, "errorBudget": Object { @@ -5054,7 +5054,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 146`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 146`] = ` Object { "date": Any, "errorBudget": Object { @@ -5068,7 +5068,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 147`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 147`] = ` Object { "date": Any, "errorBudget": Object { @@ -5082,7 +5082,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 148`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 148`] = ` Object { "date": Any, "errorBudget": Object { @@ -5096,7 +5096,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 149`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 149`] = ` Object { "date": Any, "errorBudget": Object { @@ -5110,7 +5110,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 150`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 150`] = ` Object { "date": Any, "errorBudget": Object { @@ -5124,7 +5124,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 151`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 151`] = ` Object { "date": Any, "errorBudget": Object { @@ -5138,7 +5138,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 152`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 152`] = ` Object { "date": Any, "errorBudget": Object { @@ -5152,7 +5152,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 153`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 153`] = ` Object { "date": Any, "errorBudget": Object { @@ -5166,7 +5166,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 154`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 154`] = ` Object { "date": Any, "errorBudget": Object { @@ -5180,7 +5180,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 155`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 155`] = ` Object { "date": Any, "errorBudget": Object { @@ -5194,7 +5194,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 156`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 156`] = ` Object { "date": Any, "errorBudget": Object { @@ -5208,7 +5208,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 157`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 157`] = ` Object { "date": Any, "errorBudget": Object { @@ -5222,7 +5222,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 158`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 158`] = ` Object { "date": Any, "errorBudget": Object { @@ -5236,7 +5236,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 159`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 159`] = ` Object { "date": Any, "errorBudget": Object { @@ -5250,7 +5250,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 160`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 160`] = ` Object { "date": Any, "errorBudget": Object { @@ -5264,7 +5264,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 161`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 161`] = ` Object { "date": Any, "errorBudget": Object { @@ -5278,7 +5278,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 162`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 162`] = ` Object { "date": Any, "errorBudget": Object { @@ -5292,7 +5292,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 163`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 163`] = ` Object { "date": Any, "errorBudget": Object { @@ -5306,7 +5306,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 164`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 164`] = ` Object { "date": Any, "errorBudget": Object { @@ -5320,7 +5320,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 165`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 165`] = ` Object { "date": Any, "errorBudget": Object { @@ -5334,7 +5334,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 166`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 166`] = ` Object { "date": Any, "errorBudget": Object { @@ -5348,7 +5348,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 167`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 167`] = ` Object { "date": Any, "errorBudget": Object { @@ -5362,7 +5362,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 168`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 168`] = ` Object { "date": Any, "errorBudget": Object { @@ -5376,7 +5376,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 169`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 169`] = ` Object { "date": Any, "errorBudget": Object { @@ -5390,7 +5390,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 170`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 170`] = ` Object { "date": Any, "errorBudget": Object { @@ -5404,7 +5404,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 171`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 171`] = ` Object { "date": Any, "errorBudget": Object { @@ -5418,7 +5418,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 172`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 172`] = ` Object { "date": Any, "errorBudget": Object { @@ -5432,7 +5432,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 173`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 173`] = ` Object { "date": Any, "errorBudget": Object { @@ -5446,7 +5446,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 174`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 174`] = ` Object { "date": Any, "errorBudget": Object { @@ -5460,7 +5460,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 175`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 175`] = ` Object { "date": Any, "errorBudget": Object { @@ -5474,7 +5474,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 176`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 176`] = ` Object { "date": Any, "errorBudget": Object { @@ -5488,7 +5488,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 177`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 177`] = ` Object { "date": Any, "errorBudget": Object { @@ -5502,7 +5502,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 178`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 178`] = ` Object { "date": Any, "errorBudget": Object { @@ -5516,7 +5516,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 179`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 179`] = ` Object { "date": Any, "errorBudget": Object { @@ -5530,7 +5530,7 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary 180`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the SLO timeWindow date range 180`] = ` Object { "date": Any, "errorBudget": Object { @@ -5544,2522 +5544,5238 @@ Object { } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 1`] = ` +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 1`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.00138, + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 2`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 3`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 4`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 5`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 6`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 7`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 8`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 9`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 10`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 11`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 12`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 13`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 14`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 15`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 16`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 17`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 18`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 19`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 20`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 21`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 22`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 23`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 24`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 25`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 26`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 27`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 28`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 29`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 30`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 31`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 32`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 33`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 34`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 35`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 36`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 37`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 38`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 39`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 40`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 41`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 42`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 43`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 44`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 45`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 46`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 47`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 48`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 49`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 50`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 51`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 52`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 53`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 54`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 55`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 56`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 57`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 58`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 59`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 60`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 61`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 62`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 63`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 64`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 65`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 66`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 67`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 68`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 69`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 70`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 71`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 72`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 73`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 74`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 75`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 76`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 77`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 78`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 79`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 80`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 81`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 82`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 83`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 84`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 85`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 86`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 87`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 88`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 89`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 90`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 91`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 92`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 93`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 94`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 95`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 96`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Occurrences SLOs returns the summary using the provided date range 97`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.3, + "initial": 0.1, + "isEstimated": false, + "remaining": 0.7, + }, + "sliValue": 0.97, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 1`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 2`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 3`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 4`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 5`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 6`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 7`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 8`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 9`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 10`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 11`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 12`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 13`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 14`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 15`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 16`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 17`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 18`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 19`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 20`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 21`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 22`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 23`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 24`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 25`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 26`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 27`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 28`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 29`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 30`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 31`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 32`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 33`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 34`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 35`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 36`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 37`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 38`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 39`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 40`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 41`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 42`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 43`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 44`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 45`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 46`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 47`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 48`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 49`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 50`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 51`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 52`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 53`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 54`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 55`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 56`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 57`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 58`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 59`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 60`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 61`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 62`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 63`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 64`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 65`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 66`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 67`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 68`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 69`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 70`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 71`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 72`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 73`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 74`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.99862, + "remaining": 0.33334, }, - "sliValue": 0.999931, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 2`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 75`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.00278, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.99722, + "remaining": 0.33334, }, - "sliValue": 0.999861, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 3`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 76`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.00416, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.99584, + "remaining": 0.33334, }, - "sliValue": 0.999792, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 4`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 77`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.00556, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.99444, + "remaining": 0.33334, }, - "sliValue": 0.999722, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 5`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 78`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.00694, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.99306, + "remaining": 0.33334, }, - "sliValue": 0.999653, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 6`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 79`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.00834, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.99166, + "remaining": 0.33334, }, - "sliValue": 0.999583, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 7`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 80`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.00972, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.99028, + "remaining": 0.33334, }, - "sliValue": 0.999514, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 8`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 81`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.01112, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.98888, + "remaining": 0.33334, }, - "sliValue": 0.999444, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 9`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 82`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.0125, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.9875, + "remaining": 0.33334, }, - "sliValue": 0.999375, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 10`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 83`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.01388, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.98612, + "remaining": 0.33334, }, - "sliValue": 0.999306, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 11`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 84`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.01528, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.98472, + "remaining": 0.33334, }, - "sliValue": 0.999236, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 12`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 85`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.01666, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.98334, + "remaining": 0.33334, }, - "sliValue": 0.999167, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 13`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 86`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.01806, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.98194, + "remaining": 0.33334, }, - "sliValue": 0.999097, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 14`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 87`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.01944, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.98056, + "remaining": 0.33334, }, - "sliValue": 0.999028, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 15`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 88`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.02084, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.97916, + "remaining": 0.33334, }, - "sliValue": 0.998958, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 16`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 89`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.02222, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.97778, + "remaining": 0.33334, }, - "sliValue": 0.998889, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 17`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 90`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.02362, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.97638, + "remaining": 0.33334, }, - "sliValue": 0.998819, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 18`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 91`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.025, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.975, + "remaining": 0.33334, }, - "sliValue": 0.99875, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 19`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 92`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.02638, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.97362, + "remaining": 0.33334, }, - "sliValue": 0.998681, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 20`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 93`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.02778, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.97222, + "remaining": 0.33334, }, - "sliValue": 0.998611, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 21`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 94`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.02916, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.97084, + "remaining": 0.33334, }, - "sliValue": 0.998542, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 22`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 95`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.03056, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.96944, + "remaining": 0.33334, }, - "sliValue": 0.998472, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 23`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 96`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.03194, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.96806, + "remaining": 0.33334, }, - "sliValue": 0.998403, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 24`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 97`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.03334, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.96666, + "remaining": 0.33334, }, - "sliValue": 0.998333, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 25`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 98`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.03472, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.96528, + "remaining": 0.33334, }, - "sliValue": 0.998264, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 26`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 99`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.03612, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.96388, + "remaining": 0.33334, }, - "sliValue": 0.998194, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 27`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 100`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.0375, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.9625, + "remaining": 0.33334, }, - "sliValue": 0.998125, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 28`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 101`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.03888, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.96112, + "remaining": 0.33334, }, - "sliValue": 0.998056, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 29`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 102`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.04028, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.95972, + "remaining": 0.33334, }, - "sliValue": 0.997986, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 30`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 103`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.04166, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.95834, + "remaining": 0.33334, }, - "sliValue": 0.997917, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 31`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 104`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.04306, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.95694, + "remaining": 0.33334, }, - "sliValue": 0.997847, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 32`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 105`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.04444, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.95556, + "remaining": 0.33334, }, - "sliValue": 0.997778, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 33`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 106`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.04584, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.95416, + "remaining": 0.33334, }, - "sliValue": 0.997708, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 34`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 107`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.04722, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.95278, + "remaining": 0.33334, }, - "sliValue": 0.997639, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 35`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 108`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.04862, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.95138, + "remaining": 0.33334, }, - "sliValue": 0.997569, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 36`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 109`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.05, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.95, + "remaining": 0.33334, }, - "sliValue": 0.9975, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 37`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 110`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.05138, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.94862, + "remaining": 0.33334, }, - "sliValue": 0.997431, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 38`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 111`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.05278, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.94722, + "remaining": 0.33334, }, - "sliValue": 0.997361, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 39`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 112`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.05416, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.94584, + "remaining": 0.33334, }, - "sliValue": 0.997292, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 40`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 113`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.05556, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.94444, + "remaining": 0.33334, }, - "sliValue": 0.997222, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 41`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 114`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.05694, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.94306, + "remaining": 0.33334, }, - "sliValue": 0.997153, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 42`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 115`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.05834, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.94166, + "remaining": 0.33334, }, - "sliValue": 0.997083, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 43`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 116`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.05972, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.94028, + "remaining": 0.33334, }, - "sliValue": 0.997014, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 44`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 117`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.06112, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.93888, + "remaining": 0.33334, }, - "sliValue": 0.996944, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 45`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 118`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.0625, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.9375, + "remaining": 0.33334, }, - "sliValue": 0.996875, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 46`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 119`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.06388, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.93612, + "remaining": 0.33334, }, - "sliValue": 0.996806, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 47`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 120`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.06528, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.93472, + "remaining": 0.33334, }, - "sliValue": 0.996736, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 48`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 121`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.06666, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.93334, + "remaining": 0.33334, }, - "sliValue": 0.996667, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 49`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 122`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.06806, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.93194, + "remaining": 0.33334, }, - "sliValue": 0.996597, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 50`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 123`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.06944, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.93056, + "remaining": 0.33334, }, - "sliValue": 0.996528, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 51`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 124`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.07084, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.92916, + "remaining": 0.33334, }, - "sliValue": 0.996458, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 52`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 125`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.07222, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.92778, + "remaining": 0.33334, }, - "sliValue": 0.996389, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 53`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 126`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.07362, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.92638, + "remaining": 0.33334, }, - "sliValue": 0.996319, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 54`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 127`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.075, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.925, + "remaining": 0.33334, }, - "sliValue": 0.99625, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 55`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 128`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.07638, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.92362, + "remaining": 0.33334, }, - "sliValue": 0.996181, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 56`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 129`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.07778, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.92222, + "remaining": 0.33334, }, - "sliValue": 0.996111, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 57`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 130`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.07916, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.92084, + "remaining": 0.33334, }, - "sliValue": 0.996042, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 58`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 131`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.08056, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.91944, + "remaining": 0.33334, }, - "sliValue": 0.995972, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 59`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 132`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.08194, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.91806, + "remaining": 0.33334, }, - "sliValue": 0.995903, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 60`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 133`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.08334, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.91666, + "remaining": 0.33334, }, - "sliValue": 0.995833, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 61`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 134`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.08472, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.91528, + "remaining": 0.33334, }, - "sliValue": 0.995764, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 62`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 135`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.08612, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.91388, + "remaining": 0.33334, }, - "sliValue": 0.995694, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 63`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 136`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.0875, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.9125, + "remaining": 0.33334, }, - "sliValue": 0.995625, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 64`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 137`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.08888, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.91112, + "remaining": 0.33334, }, - "sliValue": 0.995556, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 65`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 138`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.09028, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.90972, + "remaining": 0.33334, }, - "sliValue": 0.995486, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 66`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 139`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.09166, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.90834, + "remaining": 0.33334, }, - "sliValue": 0.995417, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 67`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 140`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.09306, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.90694, + "remaining": 0.33334, }, - "sliValue": 0.995347, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 68`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 141`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.09444, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.90556, + "remaining": 0.33334, }, - "sliValue": 0.995278, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 69`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 142`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.09584, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.90416, + "remaining": 0.33334, }, - "sliValue": 0.995208, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 70`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 143`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.09722, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.90278, + "remaining": 0.33334, }, - "sliValue": 0.995139, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 71`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 144`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.09862, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.90138, + "remaining": 0.33334, }, - "sliValue": 0.995069, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 72`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 145`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.1, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.9, + "remaining": 0.33334, }, - "sliValue": 0.995, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 73`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 146`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.10138, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.89862, + "remaining": 0.33334, }, - "sliValue": 0.994931, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 74`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 147`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.10278, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.89722, + "remaining": 0.33334, }, - "sliValue": 0.994861, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 75`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 148`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.10416, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.89584, + "remaining": 0.33334, }, - "sliValue": 0.994792, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 76`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 149`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.10556, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.89444, + "remaining": 0.33334, }, - "sliValue": 0.994722, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 77`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 150`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.10694, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.89306, + "remaining": 0.33334, }, - "sliValue": 0.994653, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 78`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 151`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.10834, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.89166, + "remaining": 0.33334, }, - "sliValue": 0.994583, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 79`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 152`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.10972, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.89028, + "remaining": 0.33334, }, - "sliValue": 0.994514, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 80`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 153`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.11112, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.88888, + "remaining": 0.33334, }, - "sliValue": 0.994444, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 81`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 154`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.1125, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.8875, + "remaining": 0.33334, }, - "sliValue": 0.994375, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 82`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 155`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.11388, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.88612, + "remaining": 0.33334, }, - "sliValue": 0.994306, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 83`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 156`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.11528, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.88472, + "remaining": 0.33334, }, - "sliValue": 0.994236, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 84`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 157`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.11666, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.88334, + "remaining": 0.33334, }, - "sliValue": 0.994167, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 85`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 158`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.11806, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.88194, + "remaining": 0.33334, }, - "sliValue": 0.994097, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 86`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 159`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.11944, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.88056, + "remaining": 0.33334, }, - "sliValue": 0.994028, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 87`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 160`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.12084, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.87916, + "remaining": 0.33334, }, - "sliValue": 0.993958, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 88`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 161`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.12222, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.87778, + "remaining": 0.33334, }, - "sliValue": 0.993889, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 89`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 162`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.12362, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.87638, + "remaining": 0.33334, }, - "sliValue": 0.993819, + "sliValue": 0.966667, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 90`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 163`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.125, + "consumed": 0.66666, "initial": 0.05, "isEstimated": false, - "remaining": 0.875, + "remaining": 0.33334, }, - "sliValue": 0.99375, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 164`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 165`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 166`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 167`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 168`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 169`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 170`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 171`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 172`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 173`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 174`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 175`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 176`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 177`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 178`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 179`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the SLO timeWindow date range 180`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.66666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.33334, + }, + "sliValue": 0.966667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 1`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.56574, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.43426, + }, + "sliValue": 0.971713, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 2`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.56666, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.43334, + }, + "sliValue": 0.971667, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 3`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.5676, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.4324, + }, + "sliValue": 0.97162, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 4`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.56852, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.43148, + }, + "sliValue": 0.971574, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 5`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.56944, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.43056, + }, + "sliValue": 0.971528, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 6`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.57038, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.42962, + }, + "sliValue": 0.971481, + "status": "HEALTHY", +} +`; + +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 7`] = ` +Object { + "date": Any, + "errorBudget": Object { + "consumed": 0.5713, + "initial": 0.05, + "isEstimated": false, + "remaining": 0.4287, + }, + "sliValue": 0.971435, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 91`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 8`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.12638, + "consumed": 0.57222, "initial": 0.05, "isEstimated": false, - "remaining": 0.87362, + "remaining": 0.42778, }, - "sliValue": 0.993681, + "sliValue": 0.971389, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 92`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 9`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.12778, + "consumed": 0.57314, "initial": 0.05, "isEstimated": false, - "remaining": 0.87222, + "remaining": 0.42686, }, - "sliValue": 0.993611, + "sliValue": 0.971343, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 93`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 10`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.12916, + "consumed": 0.57408, "initial": 0.05, "isEstimated": false, - "remaining": 0.87084, + "remaining": 0.42592, }, - "sliValue": 0.993542, + "sliValue": 0.971296, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 94`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 11`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.13056, + "consumed": 0.575, "initial": 0.05, "isEstimated": false, - "remaining": 0.86944, + "remaining": 0.425, }, - "sliValue": 0.993472, + "sliValue": 0.97125, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 95`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 12`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.13194, + "consumed": 0.57592, "initial": 0.05, "isEstimated": false, - "remaining": 0.86806, + "remaining": 0.42408, }, - "sliValue": 0.993403, + "sliValue": 0.971204, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 96`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 13`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.13334, + "consumed": 0.57686, "initial": 0.05, "isEstimated": false, - "remaining": 0.86666, + "remaining": 0.42314, }, - "sliValue": 0.993333, + "sliValue": 0.971157, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 97`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 14`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.13472, + "consumed": 0.57778, "initial": 0.05, "isEstimated": false, - "remaining": 0.86528, + "remaining": 0.42222, }, - "sliValue": 0.993264, + "sliValue": 0.971111, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 98`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 15`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.13612, + "consumed": 0.5787, "initial": 0.05, "isEstimated": false, - "remaining": 0.86388, + "remaining": 0.4213, }, - "sliValue": 0.993194, + "sliValue": 0.971065, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 99`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 16`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.1375, + "consumed": 0.57962, "initial": 0.05, "isEstimated": false, - "remaining": 0.8625, + "remaining": 0.42038, }, - "sliValue": 0.993125, + "sliValue": 0.971019, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 100`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 17`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.13888, + "consumed": 0.58056, "initial": 0.05, "isEstimated": false, - "remaining": 0.86112, + "remaining": 0.41944, }, - "sliValue": 0.993056, + "sliValue": 0.970972, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 101`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 18`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.14028, + "consumed": 0.58148, "initial": 0.05, "isEstimated": false, - "remaining": 0.85972, + "remaining": 0.41852, }, - "sliValue": 0.992986, + "sliValue": 0.970926, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 102`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 19`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.14166, + "consumed": 0.5824, "initial": 0.05, "isEstimated": false, - "remaining": 0.85834, + "remaining": 0.4176, }, - "sliValue": 0.992917, + "sliValue": 0.97088, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 103`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 20`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.14306, + "consumed": 0.58334, "initial": 0.05, "isEstimated": false, - "remaining": 0.85694, + "remaining": 0.41666, }, - "sliValue": 0.992847, + "sliValue": 0.970833, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 104`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 21`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.14444, + "consumed": 0.58426, "initial": 0.05, "isEstimated": false, - "remaining": 0.85556, + "remaining": 0.41574, }, - "sliValue": 0.992778, + "sliValue": 0.970787, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 105`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 22`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.14584, + "consumed": 0.58518, "initial": 0.05, "isEstimated": false, - "remaining": 0.85416, + "remaining": 0.41482, }, - "sliValue": 0.992708, + "sliValue": 0.970741, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 106`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 23`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.14722, + "consumed": 0.58612, "initial": 0.05, "isEstimated": false, - "remaining": 0.85278, + "remaining": 0.41388, }, - "sliValue": 0.992639, + "sliValue": 0.970694, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 107`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 24`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.14862, + "consumed": 0.58704, "initial": 0.05, "isEstimated": false, - "remaining": 0.85138, + "remaining": 0.41296, }, - "sliValue": 0.992569, + "sliValue": 0.970648, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 108`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 25`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.15, + "consumed": 0.58796, "initial": 0.05, "isEstimated": false, - "remaining": 0.85, + "remaining": 0.41204, }, - "sliValue": 0.9925, + "sliValue": 0.970602, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 109`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 26`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.15138, + "consumed": 0.58888, "initial": 0.05, "isEstimated": false, - "remaining": 0.84862, + "remaining": 0.41112, }, - "sliValue": 0.992431, + "sliValue": 0.970556, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 110`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 27`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.15278, + "consumed": 0.58982, "initial": 0.05, "isEstimated": false, - "remaining": 0.84722, + "remaining": 0.41018, }, - "sliValue": 0.992361, + "sliValue": 0.970509, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 111`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 28`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.15416, + "consumed": 0.59074, "initial": 0.05, "isEstimated": false, - "remaining": 0.84584, + "remaining": 0.40926, }, - "sliValue": 0.992292, + "sliValue": 0.970463, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 112`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 29`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.15556, + "consumed": 0.59166, "initial": 0.05, "isEstimated": false, - "remaining": 0.84444, + "remaining": 0.40834, }, - "sliValue": 0.992222, + "sliValue": 0.970417, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 113`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 30`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.15694, + "consumed": 0.5926, "initial": 0.05, "isEstimated": false, - "remaining": 0.84306, + "remaining": 0.4074, }, - "sliValue": 0.992153, + "sliValue": 0.97037, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 114`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 31`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.15834, + "consumed": 0.59352, "initial": 0.05, "isEstimated": false, - "remaining": 0.84166, + "remaining": 0.40648, }, - "sliValue": 0.992083, + "sliValue": 0.970324, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 115`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 32`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.15972, + "consumed": 0.59444, "initial": 0.05, "isEstimated": false, - "remaining": 0.84028, + "remaining": 0.40556, }, - "sliValue": 0.992014, + "sliValue": 0.970278, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 116`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 33`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.16112, + "consumed": 0.59538, "initial": 0.05, "isEstimated": false, - "remaining": 0.83888, + "remaining": 0.40462, }, - "sliValue": 0.991944, + "sliValue": 0.970231, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 117`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 34`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.1625, + "consumed": 0.5963, "initial": 0.05, "isEstimated": false, - "remaining": 0.8375, + "remaining": 0.4037, }, - "sliValue": 0.991875, + "sliValue": 0.970185, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 118`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 35`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.16388, + "consumed": 0.59722, "initial": 0.05, "isEstimated": false, - "remaining": 0.83612, + "remaining": 0.40278, }, - "sliValue": 0.991806, + "sliValue": 0.970139, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 119`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 36`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.16528, + "consumed": 0.59814, "initial": 0.05, "isEstimated": false, - "remaining": 0.83472, + "remaining": 0.40186, }, - "sliValue": 0.991736, + "sliValue": 0.970093, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 120`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 37`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.16666, + "consumed": 0.59908, "initial": 0.05, "isEstimated": false, - "remaining": 0.83334, + "remaining": 0.40092, }, - "sliValue": 0.991667, + "sliValue": 0.970046, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 121`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 38`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.16806, + "consumed": 0.6, "initial": 0.05, "isEstimated": false, - "remaining": 0.83194, + "remaining": 0.4, }, - "sliValue": 0.991597, + "sliValue": 0.97, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 122`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 39`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.16944, + "consumed": 0.60092, "initial": 0.05, "isEstimated": false, - "remaining": 0.83056, + "remaining": 0.39908, }, - "sliValue": 0.991528, + "sliValue": 0.969954, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 123`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 40`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.17084, + "consumed": 0.60186, "initial": 0.05, "isEstimated": false, - "remaining": 0.82916, + "remaining": 0.39814, }, - "sliValue": 0.991458, + "sliValue": 0.969907, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 124`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 41`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.17222, + "consumed": 0.60278, "initial": 0.05, "isEstimated": false, - "remaining": 0.82778, + "remaining": 0.39722, }, - "sliValue": 0.991389, + "sliValue": 0.969861, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 125`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 42`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.17362, + "consumed": 0.6037, "initial": 0.05, "isEstimated": false, - "remaining": 0.82638, + "remaining": 0.3963, }, - "sliValue": 0.991319, + "sliValue": 0.969815, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 126`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 43`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.175, + "consumed": 0.60462, "initial": 0.05, "isEstimated": false, - "remaining": 0.825, + "remaining": 0.39538, }, - "sliValue": 0.99125, + "sliValue": 0.969769, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 127`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 44`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.17638, + "consumed": 0.60556, "initial": 0.05, "isEstimated": false, - "remaining": 0.82362, + "remaining": 0.39444, }, - "sliValue": 0.991181, + "sliValue": 0.969722, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 128`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 45`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.17778, + "consumed": 0.60648, "initial": 0.05, "isEstimated": false, - "remaining": 0.82222, + "remaining": 0.39352, }, - "sliValue": 0.991111, + "sliValue": 0.969676, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 129`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 46`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.17916, + "consumed": 0.6074, "initial": 0.05, "isEstimated": false, - "remaining": 0.82084, + "remaining": 0.3926, }, - "sliValue": 0.991042, + "sliValue": 0.96963, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 130`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 47`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.18056, + "consumed": 0.60834, "initial": 0.05, "isEstimated": false, - "remaining": 0.81944, + "remaining": 0.39166, }, - "sliValue": 0.990972, + "sliValue": 0.969583, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 131`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 48`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.18194, + "consumed": 0.60926, "initial": 0.05, "isEstimated": false, - "remaining": 0.81806, + "remaining": 0.39074, }, - "sliValue": 0.990903, + "sliValue": 0.969537, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 132`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 49`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.18334, + "consumed": 0.61018, "initial": 0.05, "isEstimated": false, - "remaining": 0.81666, + "remaining": 0.38982, }, - "sliValue": 0.990833, + "sliValue": 0.969491, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 133`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 50`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.18472, + "consumed": 0.61112, "initial": 0.05, "isEstimated": false, - "remaining": 0.81528, + "remaining": 0.38888, }, - "sliValue": 0.990764, + "sliValue": 0.969444, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 134`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 51`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.18612, + "consumed": 0.61204, "initial": 0.05, "isEstimated": false, - "remaining": 0.81388, + "remaining": 0.38796, }, - "sliValue": 0.990694, + "sliValue": 0.969398, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 135`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 52`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.1875, + "consumed": 0.61296, "initial": 0.05, "isEstimated": false, - "remaining": 0.8125, + "remaining": 0.38704, }, - "sliValue": 0.990625, + "sliValue": 0.969352, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 136`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 53`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.18888, + "consumed": 0.61388, "initial": 0.05, "isEstimated": false, - "remaining": 0.81112, + "remaining": 0.38612, }, - "sliValue": 0.990556, + "sliValue": 0.969306, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 137`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 54`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.19028, + "consumed": 0.61482, "initial": 0.05, "isEstimated": false, - "remaining": 0.80972, + "remaining": 0.38518, }, - "sliValue": 0.990486, + "sliValue": 0.969259, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 138`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 55`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.19166, + "consumed": 0.61574, "initial": 0.05, "isEstimated": false, - "remaining": 0.80834, + "remaining": 0.38426, }, - "sliValue": 0.990417, + "sliValue": 0.969213, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 139`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 56`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.19306, + "consumed": 0.61666, "initial": 0.05, "isEstimated": false, - "remaining": 0.80694, + "remaining": 0.38334, }, - "sliValue": 0.990347, + "sliValue": 0.969167, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 140`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 57`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.19444, + "consumed": 0.6176, "initial": 0.05, "isEstimated": false, - "remaining": 0.80556, + "remaining": 0.3824, }, - "sliValue": 0.990278, + "sliValue": 0.96912, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 141`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 58`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.19584, + "consumed": 0.61852, "initial": 0.05, "isEstimated": false, - "remaining": 0.80416, + "remaining": 0.38148, }, - "sliValue": 0.990208, + "sliValue": 0.969074, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 142`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 59`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.19722, + "consumed": 0.61944, "initial": 0.05, "isEstimated": false, - "remaining": 0.80278, + "remaining": 0.38056, }, - "sliValue": 0.990139, + "sliValue": 0.969028, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 143`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 60`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.19862, + "consumed": 0.62038, "initial": 0.05, "isEstimated": false, - "remaining": 0.80138, + "remaining": 0.37962, }, - "sliValue": 0.990069, + "sliValue": 0.968981, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 144`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 61`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.2, + "consumed": 0.6213, "initial": 0.05, "isEstimated": false, - "remaining": 0.8, + "remaining": 0.3787, }, - "sliValue": 0.99, + "sliValue": 0.968935, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 145`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 62`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.20138, + "consumed": 0.62222, "initial": 0.05, "isEstimated": false, - "remaining": 0.79862, + "remaining": 0.37778, }, - "sliValue": 0.989931, + "sliValue": 0.968889, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 146`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 63`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.20278, + "consumed": 0.62314, "initial": 0.05, "isEstimated": false, - "remaining": 0.79722, + "remaining": 0.37686, }, - "sliValue": 0.989861, + "sliValue": 0.968843, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 147`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 64`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.20416, + "consumed": 0.62408, "initial": 0.05, "isEstimated": false, - "remaining": 0.79584, + "remaining": 0.37592, }, - "sliValue": 0.989792, + "sliValue": 0.968796, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 148`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 65`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.20556, + "consumed": 0.625, "initial": 0.05, "isEstimated": false, - "remaining": 0.79444, + "remaining": 0.375, }, - "sliValue": 0.989722, + "sliValue": 0.96875, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 149`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 66`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.20694, + "consumed": 0.62592, "initial": 0.05, "isEstimated": false, - "remaining": 0.79306, + "remaining": 0.37408, }, - "sliValue": 0.989653, + "sliValue": 0.968704, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 150`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 67`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.20834, + "consumed": 0.62686, "initial": 0.05, "isEstimated": false, - "remaining": 0.79166, + "remaining": 0.37314, }, - "sliValue": 0.989583, + "sliValue": 0.968657, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 151`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 68`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.20972, + "consumed": 0.62778, "initial": 0.05, "isEstimated": false, - "remaining": 0.79028, + "remaining": 0.37222, }, - "sliValue": 0.989514, + "sliValue": 0.968611, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 152`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 69`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.21112, + "consumed": 0.6287, "initial": 0.05, "isEstimated": false, - "remaining": 0.78888, + "remaining": 0.3713, }, - "sliValue": 0.989444, + "sliValue": 0.968565, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 153`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 70`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.2125, + "consumed": 0.62962, "initial": 0.05, "isEstimated": false, - "remaining": 0.7875, + "remaining": 0.37038, }, - "sliValue": 0.989375, + "sliValue": 0.968519, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 154`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 71`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.21388, + "consumed": 0.63056, "initial": 0.05, "isEstimated": false, - "remaining": 0.78612, + "remaining": 0.36944, }, - "sliValue": 0.989306, + "sliValue": 0.968472, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 155`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 72`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.21528, + "consumed": 0.63148, "initial": 0.05, "isEstimated": false, - "remaining": 0.78472, + "remaining": 0.36852, }, - "sliValue": 0.989236, + "sliValue": 0.968426, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 156`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 73`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.21666, + "consumed": 0.6324, "initial": 0.05, "isEstimated": false, - "remaining": 0.78334, + "remaining": 0.3676, }, - "sliValue": 0.989167, + "sliValue": 0.96838, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 157`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 74`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.21806, + "consumed": 0.63334, "initial": 0.05, "isEstimated": false, - "remaining": 0.78194, + "remaining": 0.36666, }, - "sliValue": 0.989097, + "sliValue": 0.968333, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 158`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 75`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.21944, + "consumed": 0.63426, "initial": 0.05, "isEstimated": false, - "remaining": 0.78056, + "remaining": 0.36574, }, - "sliValue": 0.989028, + "sliValue": 0.968287, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 159`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 76`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.22084, + "consumed": 0.63518, "initial": 0.05, "isEstimated": false, - "remaining": 0.77916, + "remaining": 0.36482, }, - "sliValue": 0.988958, + "sliValue": 0.968241, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 160`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 77`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.22222, + "consumed": 0.63612, "initial": 0.05, "isEstimated": false, - "remaining": 0.77778, + "remaining": 0.36388, }, - "sliValue": 0.988889, + "sliValue": 0.968194, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 161`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 78`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.22362, + "consumed": 0.63704, "initial": 0.05, "isEstimated": false, - "remaining": 0.77638, + "remaining": 0.36296, }, - "sliValue": 0.988819, + "sliValue": 0.968148, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 162`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 79`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.225, + "consumed": 0.63796, "initial": 0.05, "isEstimated": false, - "remaining": 0.775, + "remaining": 0.36204, }, - "sliValue": 0.98875, + "sliValue": 0.968102, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 163`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 80`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.22638, + "consumed": 0.63888, "initial": 0.05, "isEstimated": false, - "remaining": 0.77362, + "remaining": 0.36112, }, - "sliValue": 0.988681, + "sliValue": 0.968056, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 164`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 81`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.22778, + "consumed": 0.63982, "initial": 0.05, "isEstimated": false, - "remaining": 0.77222, + "remaining": 0.36018, }, - "sliValue": 0.988611, + "sliValue": 0.968009, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 165`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 82`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.22916, + "consumed": 0.64074, "initial": 0.05, "isEstimated": false, - "remaining": 0.77084, + "remaining": 0.35926, }, - "sliValue": 0.988542, + "sliValue": 0.967963, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 166`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 83`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.23056, + "consumed": 0.64166, "initial": 0.05, "isEstimated": false, - "remaining": 0.76944, + "remaining": 0.35834, }, - "sliValue": 0.988472, + "sliValue": 0.967917, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 167`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 84`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.23194, + "consumed": 0.6426, "initial": 0.05, "isEstimated": false, - "remaining": 0.76806, + "remaining": 0.3574, }, - "sliValue": 0.988403, + "sliValue": 0.96787, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 168`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 85`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.23334, + "consumed": 0.64352, "initial": 0.05, "isEstimated": false, - "remaining": 0.76666, + "remaining": 0.35648, }, - "sliValue": 0.988333, + "sliValue": 0.967824, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 169`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 86`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.23472, + "consumed": 0.64444, "initial": 0.05, "isEstimated": false, - "remaining": 0.76528, + "remaining": 0.35556, }, - "sliValue": 0.988264, + "sliValue": 0.967778, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 170`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 87`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.23612, + "consumed": 0.64538, "initial": 0.05, "isEstimated": false, - "remaining": 0.76388, + "remaining": 0.35462, }, - "sliValue": 0.988194, + "sliValue": 0.967731, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 171`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 88`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.2375, + "consumed": 0.6463, "initial": 0.05, "isEstimated": false, - "remaining": 0.7625, + "remaining": 0.3537, }, - "sliValue": 0.988125, + "sliValue": 0.967685, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 172`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 89`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.23888, + "consumed": 0.64722, "initial": 0.05, "isEstimated": false, - "remaining": 0.76112, + "remaining": 0.35278, }, - "sliValue": 0.988056, + "sliValue": 0.967639, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 173`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 90`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.24028, + "consumed": 0.64814, "initial": 0.05, "isEstimated": false, - "remaining": 0.75972, + "remaining": 0.35186, }, - "sliValue": 0.987986, + "sliValue": 0.967593, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 174`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 91`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.24166, + "consumed": 0.64908, "initial": 0.05, "isEstimated": false, - "remaining": 0.75834, + "remaining": 0.35092, }, - "sliValue": 0.987917, + "sliValue": 0.967546, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 175`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 92`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.24306, + "consumed": 0.65, "initial": 0.05, "isEstimated": false, - "remaining": 0.75694, + "remaining": 0.35, }, - "sliValue": 0.987847, + "sliValue": 0.9675, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 176`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 93`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.24444, + "consumed": 0.65092, "initial": 0.05, "isEstimated": false, - "remaining": 0.75556, + "remaining": 0.34908, }, - "sliValue": 0.987778, + "sliValue": 0.967454, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 177`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 94`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.24584, + "consumed": 0.65186, "initial": 0.05, "isEstimated": false, - "remaining": 0.75416, + "remaining": 0.34814, }, - "sliValue": 0.987708, + "sliValue": 0.967407, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 178`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 95`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.24722, + "consumed": 0.65278, "initial": 0.05, "isEstimated": false, - "remaining": 0.75278, + "remaining": 0.34722, }, - "sliValue": 0.987639, + "sliValue": 0.967361, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 179`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 96`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.24862, + "consumed": 0.6537, "initial": 0.05, "isEstimated": false, - "remaining": 0.75138, + "remaining": 0.3463, }, - "sliValue": 0.987569, + "sliValue": 0.967315, "status": "HEALTHY", } `; -exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary 180`] = ` +exports[`FetchHistoricalSummary Rolling and Timeslices SLOs returns the summary using the provided date range 97`] = ` Object { "date": Any, "errorBudget": Object { - "consumed": 0.25, + "consumed": 0.65462, "initial": 0.05, "isEstimated": false, - "remaining": 0.75, + "remaining": 0.34538, }, - "sliValue": 0.9875, + "sliValue": 0.967269, "status": "HEALTHY", } `; diff --git a/x-pack/plugins/observability_solution/slo/server/services/burn_rates_client.ts b/x-pack/plugins/observability_solution/slo/server/services/burn_rates_client.ts index ebcf04538886b..08b1c460bdf2b 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/burn_rates_client.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/burn_rates_client.ts @@ -19,7 +19,6 @@ import { occurrencesBudgetingMethodSchema, timeslicesBudgetingMethodSchema, } from '@kbn/slo-schema'; -import { getEsDateRange } from './historical_summary_client'; import { SLO_DESTINATION_INDEX_PATTERN } from '../../common/constants'; import { DateRange, Duration, SLODefinition } from '../domain/models'; import { computeBurnRate, computeSLI } from '../domain/services'; @@ -99,7 +98,7 @@ function commonQuery( { term: { 'slo.revision': slo.revision } }, { range: { - '@timestamp': getEsDateRange(dateRange), + '@timestamp': { gte: dateRange.from.toISOString(), lt: dateRange.to.toISOString() }, }, }, ]; diff --git a/x-pack/plugins/observability_solution/slo/server/services/fetch_historical_summary.ts b/x-pack/plugins/observability_solution/slo/server/services/fetch_historical_summary.ts deleted file mode 100644 index 480231a6838fe..0000000000000 --- a/x-pack/plugins/observability_solution/slo/server/services/fetch_historical_summary.ts +++ /dev/null @@ -1,25 +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 { - FetchHistoricalSummaryParams, - FetchHistoricalSummaryResponse, - fetchHistoricalSummaryResponseSchema, -} from '@kbn/slo-schema'; -import { HistoricalSummaryClient } from './historical_summary_client'; - -export class FetchHistoricalSummary { - constructor(private historicalSummaryClient: HistoricalSummaryClient) {} - - public async execute( - params: FetchHistoricalSummaryParams - ): Promise { - const historicalSummary = await this.historicalSummaryClient.fetch(params); - - return fetchHistoricalSummaryResponseSchema.encode(historicalSummary); - } -} diff --git a/x-pack/plugins/observability_solution/slo/server/services/get_preview_data.ts b/x-pack/plugins/observability_solution/slo/server/services/get_preview_data.ts index e927a9758e375..06d740aca3508 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/get_preview_data.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/get_preview_data.ts @@ -592,20 +592,19 @@ export class GetPreviewData { // Timeslice metric so that the chart is as close to the evaluation as possible. // Otherwise due to how the statistics work, the values might not look like // they've breached the threshold. + const rangeDuration = moment(params.range.to).diff(params.range.from, 'ms'); const bucketSize = params.indicator.type === 'sli.metric.timeslice' && - params.range.end - params.range.start <= 86_400_000 && + rangeDuration <= 86_400_000 && params.objective?.timesliceWindow ? params.objective.timesliceWindow.asMinutes() : Math.max( - calculateAuto - .near(100, moment.duration(params.range.end - params.range.start, 'ms')) - ?.asMinutes() ?? 0, + calculateAuto.near(100, moment.duration(rangeDuration, 'ms'))?.asMinutes() ?? 0, 1 ); const options: Options = { instanceId: params.instanceId, - range: params.range, + range: { start: params.range.from.getTime(), end: params.range.to.getTime() }, groupBy: params.groupBy, remoteName: params.remoteName, groupings: params.groupings, diff --git a/x-pack/plugins/observability_solution/slo/server/services/historical_summary_client.test.ts b/x-pack/plugins/observability_solution/slo/server/services/historical_summary_client.test.ts index ee49c439fb5b7..939b240f5d88f 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/historical_summary_client.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/historical_summary_client.test.ts @@ -8,7 +8,8 @@ import { ElasticsearchClientMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { ALL_VALUE } from '@kbn/slo-schema'; import moment from 'moment'; -import { oneMinute, oneMonth, thirtyDays } from './fixtures/duration'; +import { DateRange, SLODefinition } from '../domain/models'; +import { oneMinute, oneMonth, sevenDays, thirtyDays } from './fixtures/duration'; import { createSLO } from './fixtures/slo'; import { DefaultHistoricalSummaryClient, @@ -29,16 +30,33 @@ const commonEsResponse = { }, }; -const generateEsResponseForRollingSLO = ( - rollingDays: number = 30, - good: number = 97, - total: number = 100 -) => { - const { fixedInterval, bucketsPerDay } = getFixedIntervalAndBucketsPerDay(rollingDays); - const numberOfBuckets = rollingDays * bucketsPerDay; - const doubleDuration = rollingDays * 2; - const startDay = moment.utc().subtract(doubleDuration, 'day').startOf('day'); - const bucketSize = fixedInterval === '1d' ? 24 : Number(fixedInterval.slice(0, -1)); +const MINUTES_IN_DAY = 1440; + +const generateEsResponseForRollingSLO = (slo: SLODefinition, overridedRange?: DateRange) => { + const rollingDurationInDays = slo.timeWindow.duration.asMinutes() / MINUTES_IN_DAY; + const timesliceInMin = slo.objective.timesliceWindow?.asMinutes(); + const overridedRangeInDays = overridedRange + ? moment(overridedRange.to).diff(moment(overridedRange.from), 'days') + : 0; + + const { fixedInterval, bucketsPerDay } = getFixedIntervalAndBucketsPerDay( + overridedRangeInDays ? overridedRangeInDays : rollingDurationInDays + ); + const fullDuration = overridedRange + ? rollingDurationInDays + overridedRangeInDays + : rollingDurationInDays * 2; + const numberOfBuckets = fullDuration * bucketsPerDay; + const startDay = moment().subtract(fullDuration, 'day').startOf('day'); + const bucketSizeInHour = moment + .duration( + fixedInterval.slice(0, -1), + fixedInterval.slice(-1) as moment.unitOfTime.DurationConstructor + ) + .asHours(); + + const good = timesliceInMin ? Math.floor(((bucketSizeInHour * 60) / timesliceInMin) * 0.97) : 97; + const total = timesliceInMin ? Math.floor((bucketSizeInHour * 60) / timesliceInMin) : 100; + return { ...commonEsResponse, responses: [ @@ -51,11 +69,11 @@ const generateEsResponseForRollingSLO = ( .map((_, index) => ({ key_as_string: startDay .clone() - .add(index * bucketSize, 'hours') + .add(index * bucketSizeInHour, 'hours') .toISOString(), key: startDay .clone() - .add(index * bucketSize, 'hours') + .add(index * bucketSizeInHour, 'hours') .format('x'), doc_count: 1440, total: { @@ -65,10 +83,16 @@ const generateEsResponseForRollingSLO = ( value: good, }, cumulative_good: { - value: good * (index + 1), + value: + index < rollingDurationInDays * bucketsPerDay + ? good * (index + 1) + : good * rollingDurationInDays * bucketsPerDay, }, cumulative_total: { - value: total * (index + 1), + value: + index < rollingDurationInDays * bucketsPerDay + ? total * (index + 1) + : total * rollingDurationInDays * bucketsPerDay, }, })), }, @@ -137,13 +161,13 @@ describe('FetchHistoricalSummary', () => { }); describe('Rolling and Occurrences SLOs', () => { - it('returns the summary', async () => { + it('returns the summary using the SLO timeWindow date range', async () => { const slo = createSLO({ timeWindow: { type: 'rolling', duration: thirtyDays() }, objective: { target: 0.95 }, groupBy: ALL_VALUE, }); - esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(30)); + esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(slo)); const client = new DefaultHistoricalSummaryClient(esClientMock); const results = await client.fetch({ @@ -163,20 +187,52 @@ describe('FetchHistoricalSummary', () => { results[0].data.forEach((dailyResult) => expect(dailyResult).toMatchSnapshot({ date: expect.any(Date) }) ); + }); - expect(results[0].data).toHaveLength(180); + it('returns the summary using the provided date range', async () => { + const slo = createSLO({ + timeWindow: { type: 'rolling', duration: sevenDays() }, + objective: { target: 0.9 }, + groupBy: ALL_VALUE, + }); + const range: DateRange = { + from: new Date('2023-01-09T15:00:00.000Z'), + to: new Date('2023-01-13T15:00:00.000Z'), + }; + + esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(slo, range)); + const client = new DefaultHistoricalSummaryClient(esClientMock); + + const results = await client.fetch({ + list: [ + { + timeWindow: slo.timeWindow, + groupBy: slo.groupBy, + budgetingMethod: slo.budgetingMethod, + objective: slo.objective, + revision: slo.revision, + sloId: slo.id, + instanceId: ALL_VALUE, + range, + }, + ], + }); + + results[0].data.forEach((dailyResult) => + expect(dailyResult).toMatchSnapshot({ date: expect.any(Date) }) + ); }); }); describe('Rolling and Timeslices SLOs', () => { - it('returns the summary', async () => { + it('returns the summary using the SLO timeWindow date range', async () => { const slo = createSLO({ timeWindow: { type: 'rolling', duration: thirtyDays() }, budgetingMethod: 'timeslices', objective: { target: 0.95, timesliceTarget: 0.9, timesliceWindow: oneMinute() }, groupBy: ALL_VALUE, }); - esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(30)); + esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(slo)); const client = new DefaultHistoricalSummaryClient(esClientMock); const results = await client.fetch({ @@ -198,6 +254,40 @@ describe('FetchHistoricalSummary', () => { ); expect(results[0].data).toHaveLength(180); }); + + it('returns the summary using the provided date range', async () => { + const slo = createSLO({ + timeWindow: { type: 'rolling', duration: thirtyDays() }, + budgetingMethod: 'timeslices', + objective: { target: 0.95, timesliceTarget: 0.9, timesliceWindow: oneMinute() }, + groupBy: ALL_VALUE, + }); + const range: DateRange = { + from: new Date('2023-01-09T15:00:00.000Z'), + to: new Date('2023-01-13T15:00:00.000Z'), + }; + esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(slo, range)); + const client = new DefaultHistoricalSummaryClient(esClientMock); + + const results = await client.fetch({ + list: [ + { + timeWindow: slo.timeWindow, + groupBy: slo.groupBy, + budgetingMethod: slo.budgetingMethod, + objective: slo.objective, + revision: slo.revision, + sloId: slo.id, + instanceId: ALL_VALUE, + range, + }, + ], + }); + + results[0].data.forEach((dailyResult) => + expect(dailyResult).toMatchSnapshot({ date: expect.any(Date) }) + ); + }); }); describe('Calendar Aligned and Timeslices SLOs', () => { @@ -275,7 +365,7 @@ describe('FetchHistoricalSummary', () => { objective: { target: 0.95 }, groupBy: 'host', }); - esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(30)); + esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(slo)); const client = new DefaultHistoricalSummaryClient(esClientMock); const results = await client.fetch({ diff --git a/x-pack/plugins/observability_solution/slo/server/services/historical_summary_client.ts b/x-pack/plugins/observability_solution/slo/server/services/historical_summary_client.ts index 6e07bb30c2cb6..263111c944da3 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/historical_summary_client.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/historical_summary_client.ts @@ -63,13 +63,12 @@ export class DefaultHistoricalSummaryClient implements HistoricalSummaryClient { constructor(private esClient: ElasticsearchClient) {} async fetch(params: FetchHistoricalSummaryParams): Promise { - const dateRangeBySlo = params.list.reduce>( - (acc, { sloId, timeWindow, range }) => { - acc[sloId] = range ?? getDateRange(timeWindow); - return acc; - }, - {} - ); + const dateRangeBySlo = params.list.reduce< + Record + >((acc, { sloId, timeWindow, range }) => { + acc[sloId] = getDateRange(timeWindow, range); + return acc; + }, {}); const searches = params.list.flatMap( ({ sloId, revision, budgetingMethod, instanceId, groupBy, timeWindow, remoteName }) => [ @@ -113,7 +112,12 @@ export class DefaultHistoricalSummaryClient implements HistoricalSummaryClient { historicalSummary.push({ sloId, instanceId, - data: handleResultForRollingAndTimeslices(objective, timeWindow, buckets), + data: handleResultForRollingAndTimeslices( + objective, + timeWindow, + buckets, + dateRangeBySlo[sloId] + ), }); continue; @@ -123,7 +127,7 @@ export class DefaultHistoricalSummaryClient implements HistoricalSummaryClient { historicalSummary.push({ sloId, instanceId, - data: handleResultForRollingAndOccurrences(objective, timeWindow, buckets), + data: handleResultForRollingAndOccurrences(objective, buckets, dateRangeBySlo[sloId]), }); continue; } @@ -187,10 +191,10 @@ function handleResultForCalendarAlignedAndOccurrences( function handleResultForCalendarAlignedAndTimeslices( objective: Objective, buckets: DailyAggBucket[], - dateRange: DateRange + dateRange: { range: DateRange; queryRange: DateRange } ): HistoricalSummary[] { const initialErrorBudget = 1 - objective.target; - const totalSlices = computeTotalSlicesFromDateRange(dateRange, objective.timesliceWindow!); + const totalSlices = computeTotalSlicesFromDateRange(dateRange.range, objective.timesliceWindow!); return buckets.map((bucket: DailyAggBucket): HistoricalSummary => { const good = bucket.cumulative_good?.value ?? 0; @@ -210,18 +214,17 @@ function handleResultForCalendarAlignedAndTimeslices( function handleResultForRollingAndOccurrences( objective: Objective, - timeWindow: TimeWindow, - buckets: DailyAggBucket[] + buckets: DailyAggBucket[], + dateRange: { range: DateRange; queryRange: DateRange } ): HistoricalSummary[] { const initialErrorBudget = 1 - objective.target; - const rollingWindowDurationInDays = moment - .duration(timeWindow.duration.value, toMomentUnitOfTime(timeWindow.duration.unit)) - .asDays(); - - const { bucketsPerDay } = getFixedIntervalAndBucketsPerDay(rollingWindowDurationInDays); return buckets - .slice(-bucketsPerDay * rollingWindowDurationInDays) + .filter( + (bucket) => + moment(bucket.key_as_string).isSameOrAfter(dateRange.range.from) && + moment(bucket.key_as_string).isSameOrBefore(dateRange.range.to) + ) .map((bucket: DailyAggBucket): HistoricalSummary => { const good = bucket.cumulative_good?.value ?? 0; const total = bucket.cumulative_total?.value ?? 0; @@ -242,20 +245,21 @@ function handleResultForRollingAndOccurrences( function handleResultForRollingAndTimeslices( objective: Objective, timeWindow: TimeWindow, - buckets: DailyAggBucket[] + buckets: DailyAggBucket[], + dateRange: { range: DateRange; queryRange: DateRange } ): HistoricalSummary[] { const initialErrorBudget = 1 - objective.target; - const rollingWindowDurationInDays = moment - .duration(timeWindow.duration.value, toMomentUnitOfTime(timeWindow.duration.unit)) - .asDays(); - const { bucketsPerDay } = getFixedIntervalAndBucketsPerDay(rollingWindowDurationInDays); const totalSlices = Math.ceil( timeWindow.duration.asSeconds() / objective.timesliceWindow!.asSeconds() ); return buckets - .slice(-bucketsPerDay * rollingWindowDurationInDays) + .filter( + (bucket) => + moment(bucket.key_as_string).isSameOrAfter(dateRange.range.from) && + moment(bucket.key_as_string).isSameOrBefore(dateRange.range.to) + ) .map((bucket: DailyAggBucket): HistoricalSummary => { const good = bucket.cumulative_good?.value ?? 0; const total = bucket.cumulative_total?.value ?? 0; @@ -272,13 +276,6 @@ function handleResultForRollingAndTimeslices( }); } -export const getEsDateRange = (dateRange: DateRange) => { - return { - gte: typeof dateRange.from === 'string' ? dateRange.from : dateRange.from.toISOString(), - lte: typeof dateRange.to === 'string' ? dateRange.to : dateRange.to.toISOString(), - }; -}; - function generateSearchQuery({ sloId, groupBy, @@ -292,15 +289,19 @@ function generateSearchQuery({ sloId: string; groupBy: GroupBy; revision: number; - dateRange: DateRange; + dateRange: { range: DateRange; queryRange: DateRange }; timeWindow: TimeWindow; budgetingMethod: BudgetingMethod; }): MsearchMultisearchBody { const unit = toMomentUnitOfTime(timeWindow.duration.unit); const timeWindowDurationInDays = moment.duration(timeWindow.duration.value, unit).asDays(); + const queryRangeDurationInDays = Math.ceil( + moment(dateRange.range.to).diff(dateRange.range.from, 'days') + ); + const { fixedInterval, bucketsPerDay } = - getFixedIntervalAndBucketsPerDay(timeWindowDurationInDays); + getFixedIntervalAndBucketsPerDay(queryRangeDurationInDays); const extraFilterByInstanceId = !!groupBy && ![groupBy].flat().includes(ALL_VALUE) && instanceId !== ALL_VALUE @@ -316,7 +317,10 @@ function generateSearchQuery({ { term: { 'slo.revision': revision } }, { range: { - '@timestamp': getEsDateRange(dateRange), + '@timestamp': { + gte: dateRange.queryRange.from.toISOString(), + lte: dateRange.queryRange.to.toISOString(), + }, }, }, ...extraFilterByInstanceId, @@ -329,8 +333,8 @@ function generateSearchQuery({ field: '@timestamp', fixed_interval: fixedInterval, extended_bounds: { - min: typeof dateRange.from === 'string' ? dateRange.from : dateRange.from.toISOString(), - max: 'now/d', + min: dateRange.queryRange.from.toISOString(), + max: dateRange.queryRange.to.toISOString(), }, }, aggs: { @@ -382,26 +386,62 @@ function generateSearchQuery({ }; } -function getDateRange(timeWindow: TimeWindow) { +/** + * queryRange is used for the filter range on the query, + * while range is used for storing the actual range requested + * For a rolling window, the query range starts 1 timeWindow duration before the actual range from. + * For calednar window, the query range is the same as the range. + * + * @param timeWindow + * @param range + * @returns the request {range} and the query range {queryRange} + * + */ +function getDateRange( + timeWindow: TimeWindow, + range?: DateRange +): { range: DateRange; queryRange: DateRange } { if (rollingTimeWindowSchema.is(timeWindow)) { const unit = toMomentUnitOfTime(timeWindow.duration.unit as DurationUnit); + + if (range) { + return { + range, + queryRange: { + from: moment(range.from) + .subtract(timeWindow.duration.value, unit) + .startOf('day') + .toDate(), + to: moment(range.to).startOf('minute').toDate(), + }, + }; + } + const now = moment(); return { - from: now - .clone() - .subtract(timeWindow.duration.value * 2, unit) - .startOf('day') - .toDate(), - to: now.startOf('minute').toDate(), + range: { + from: now.clone().subtract(timeWindow.duration.value, unit).startOf('day').toDate(), + to: now.clone().startOf('minute').toDate(), + }, + queryRange: { + from: now + .clone() + .subtract(timeWindow.duration.value * 2, unit) + .startOf('day') + .toDate(), + to: now.clone().startOf('minute').toDate(), + }, }; } + if (calendarAlignedTimeWindowSchema.is(timeWindow)) { const now = moment(); const unit = toCalendarAlignedTimeWindowMomentUnit(timeWindow); const from = moment.utc(now).startOf(unit); const to = moment.utc(now).endOf(unit); - return { from: from.toDate(), to: to.toDate() }; + const calendarRange = { from: from.toDate(), to: to.toDate() }; + return { range: calendarRange, queryRange: calendarRange }; } assertNever(timeWindow); @@ -411,6 +451,9 @@ export function getFixedIntervalAndBucketsPerDay(durationInDays: number): { fixedInterval: string; bucketsPerDay: number; } { + if (durationInDays <= 3) { + return { fixedInterval: '30m', bucketsPerDay: 48 }; + } if (durationInDays <= 7) { return { fixedInterval: '1h', bucketsPerDay: 24 }; } diff --git a/x-pack/plugins/observability_solution/slo/server/services/index.ts b/x-pack/plugins/observability_solution/slo/server/services/index.ts index 7f6e7683e582d..6c9bdc906914c 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/index.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/index.ts @@ -8,7 +8,6 @@ export * from './create_slo'; export * from './delete_slo'; export * from './delete_slo_instances'; -export * from './fetch_historical_summary'; export * from './find_slo'; export * from './get_slo'; export * from './historical_summary_client'; diff --git a/x-pack/plugins/observability_solution/slo/server/services/summary_client.ts b/x-pack/plugins/observability_solution/slo/server/services/summary_client.ts index 5e5ee5a7228a0..635dbdd654ead 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/summary_client.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/summary_client.ts @@ -16,7 +16,6 @@ import { occurrencesBudgetingMethodSchema, timeslicesBudgetingMethodSchema, } from '@kbn/slo-schema'; -import { getEsDateRange } from './historical_summary_client'; import { SLO_DESTINATION_INDEX_PATTERN } from '../../common/constants'; import { Groupings, Meta, SLODefinition, Summary } from '../domain/models'; import { computeSLI, computeSummaryStatus, toErrorBudget } from '../domain/services'; @@ -76,7 +75,10 @@ export class DefaultSummaryClient implements SummaryClient { { term: { 'slo.revision': slo.revision } }, { range: { - '@timestamp': getEsDateRange(dateRange), + '@timestamp': { + gte: dateRange.from.toISOString(), + lte: dateRange.to.toISOString(), + }, }, }, ...instanceIdFilter, From fe0e1e3d7dfa33dde65305f2709bbb85a825dc5e Mon Sep 17 00:00:00 2001 From: Saikat Sarkar <132922331+saikatsarkar056@users.noreply.github.com> Date: Thu, 16 May 2024 10:57:24 -0600 Subject: [PATCH 16/71] [Semantic Text UI] Adapt inference endpoint calls to new API spec (#183320) The inference endpoint API spec has changed. Now, we need to refer to 'endpoints' property instead of the 'models' property while gathering the inference_endpoints informations using _inference/_all --- .../inference_models/register_get_route.ts | 6 +- .../apis/management/index_management/index.ts | 1 + .../index_management/inference_endpoints.ts | 62 +++++++++++++++++++ x-pack/test/functional/services/ml/api.ts | 26 ++++++++ .../common/index_management/index.ts | 1 + .../index_management/inference_endpoints.ts | 62 +++++++++++++++++++ 6 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts diff --git a/x-pack/plugins/index_management/server/routes/api/inference_models/register_get_route.ts b/x-pack/plugins/index_management/server/routes/api/inference_models/register_get_route.ts index 68f385228f776..4709abadc3345 100644 --- a/x-pack/plugins/index_management/server/routes/api/inference_models/register_get_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/inference_models/register_get_route.ts @@ -21,15 +21,15 @@ export function registerGetAllRoute({ router, lib: { handleEsError } }: RouteDep // TODO: Use the client's built-in function rather than the transport when it's available try { - const { models } = await client.asCurrentUser.transport.request<{ - models: InferenceAPIConfigResponse[]; + const { endpoints } = await client.asCurrentUser.transport.request<{ + endpoints: InferenceAPIConfigResponse[]; }>({ method: 'GET', path: `/_inference/_all`, }); return response.ok({ - body: models, + body: endpoints, }); } catch (error) { return handleEsError({ error, response }); diff --git a/x-pack/test/api_integration/apis/management/index_management/index.ts b/x-pack/test/api_integration/apis/management/index_management/index.ts index 97d25f93a4982..63ab1f3371941 100644 --- a/x-pack/test/api_integration/apis/management/index_management/index.ts +++ b/x-pack/test/api_integration/apis/management/index_management/index.ts @@ -15,6 +15,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./stats')); loadTestFile(require.resolve('./data_streams')); loadTestFile(require.resolve('./templates')); + loadTestFile(require.resolve('./inference_endpoints')); loadTestFile(require.resolve('./component_templates')); loadTestFile(require.resolve('./cluster_nodes')); loadTestFile(require.resolve('./index_details')); diff --git a/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts b/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts new file mode 100644 index 0000000000000..ecfcff804d69a --- /dev/null +++ b/x-pack/test/api_integration/apis/management/index_management/inference_endpoints.ts @@ -0,0 +1,62 @@ +/* + * 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +const API_BASE_PATH = '/api/index_management'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const log = getService('log'); + const ml = getService('ml'); + const inferenceId = 'my-elser-model'; + const taskType = 'sparse_embedding'; + const service = 'elser'; + + describe('Inference endpoints', function () { + before(async () => { + log.debug(`Creating inference endpoint`); + try { + await ml.api.createInferenceEndpoint(inferenceId, taskType, { + service, + service_settings: { + num_allocations: 1, + num_threads: 1, + }, + }); + } catch (err) { + log.debug('[Setup error] Error creating inference endpoint'); + throw err; + } + }); + + after(async () => { + // Cleanup inference endpoints created for testing purposes + try { + log.debug(`Deleting inference endpoint`); + await ml.api.deleteInferenceEndpoint(inferenceId, taskType); + } catch (err) { + log.debug('[Cleanup error] Error deleting inference endpoint'); + throw err; + } + }); + + describe('get inference endpoints', () => { + it('returns the existing inference endpoints', async () => { + const { body: inferenceEndpoints } = await supertest + .get(`${API_BASE_PATH}/inference/all`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(200); + + expect(inferenceEndpoints).to.be.ok(); + expect(inferenceEndpoints[0].model_id).to.eql(inferenceId); + }); + }); + }); +} diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index 0d5cbfa47b00b..fdbbad01d003b 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -226,6 +226,32 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { ); }, + async getInferenceEndpoint(inferenceId: string) { + const { status } = await esSupertest.get(`/_inference/${inferenceId}`); + return status === 200; + }, + + async createInferenceEndpoint(inferenceId: string, taskType: string, requestBody: object) { + const found = await this.getInferenceEndpoint(inferenceId); + if (found) { + log.debug(`Inference endpoint '${inferenceId}' already exists. Nothing to create.`); + return; + } + const { body, status } = await esSupertest + .put(`/_inference/${taskType}/${inferenceId}`) + .send(requestBody); + this.assertResponseStatusCode(200, status, body); + + return body; + }, + + async deleteInferenceEndpoint(inferenceId: string, taskType: string) { + const { body, status } = await esSupertest.delete(`/_inference/${taskType}/${inferenceId}`); + this.assertResponseStatusCode(200, status, body); + + return body; + }, + async createIndex( indices: string, mappings?: Record | estypes.MappingTypeMapping, diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/index.ts index c7e8c7b171bc9..608530a51e72a 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/index.ts @@ -13,6 +13,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./index_templates')); loadTestFile(require.resolve('./indices')); + loadTestFile(require.resolve('./inference_endpoints')); loadTestFile(require.resolve('./enrich_policies')); loadTestFile(require.resolve('./create_enrich_policies')); loadTestFile(require.resolve('./index_component_templates')); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts new file mode 100644 index 0000000000000..ecfcff804d69a --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts @@ -0,0 +1,62 @@ +/* + * 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +const API_BASE_PATH = '/api/index_management'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const log = getService('log'); + const ml = getService('ml'); + const inferenceId = 'my-elser-model'; + const taskType = 'sparse_embedding'; + const service = 'elser'; + + describe('Inference endpoints', function () { + before(async () => { + log.debug(`Creating inference endpoint`); + try { + await ml.api.createInferenceEndpoint(inferenceId, taskType, { + service, + service_settings: { + num_allocations: 1, + num_threads: 1, + }, + }); + } catch (err) { + log.debug('[Setup error] Error creating inference endpoint'); + throw err; + } + }); + + after(async () => { + // Cleanup inference endpoints created for testing purposes + try { + log.debug(`Deleting inference endpoint`); + await ml.api.deleteInferenceEndpoint(inferenceId, taskType); + } catch (err) { + log.debug('[Cleanup error] Error deleting inference endpoint'); + throw err; + } + }); + + describe('get inference endpoints', () => { + it('returns the existing inference endpoints', async () => { + const { body: inferenceEndpoints } = await supertest + .get(`${API_BASE_PATH}/inference/all`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .expect(200); + + expect(inferenceEndpoints).to.be.ok(); + expect(inferenceEndpoints[0].model_id).to.eql(inferenceId); + }); + }); + }); +} From 0be35b3af739fb84014beb3d979ad60ecf24977b Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Thu, 16 May 2024 18:58:53 +0200 Subject: [PATCH 17/71] Remove GH project assignment for closed project (#183658) ## Summary The `Lens` [GH Project](https://github.com/elastic/kibana/projects/32) is closed and we no longer use it. --- .github/workflows/project-assigner.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/project-assigner.yml b/.github/workflows/project-assigner.yml index 8c381dd1ecdef..a5fe7c736ad3a 100644 --- a/.github/workflows/project-assigner.yml +++ b/.github/workflows/project-assigner.yml @@ -13,7 +13,6 @@ jobs: with: issue-mappings: | [ - {"label": "Feature:Lens", "projectNumber": 32, "columnName": "Long-term goals"}, {"label": "Team:DataDiscovery", "projectNumber": 44, "columnName": "Inbox"}, {"label": "Feature:Canvas", "projectNumber": 38, "columnName": "Inbox"}, {"label": "Feature:Dashboard", "projectNumber": 68, "columnName": "Inbox"}, From 3c908b2e13400a910e12bcc62420d1253cece086 Mon Sep 17 00:00:00 2001 From: elena-shostak <165678770+elena-shostak@users.noreply.github.com> Date: Thu, 16 May 2024 19:16:00 +0200 Subject: [PATCH 18/71] [Roles] Support read-only remote index & cluster sections with read_security access (#183126) ## Summary Changed privilege check from `manage_security` to `read_security`, so user with `read_security` can view remote indexes/remote clusters sections for role.
[Screenshot BEFORE] Role for viewer user with read security before change Screenshot 2024-05-07 at 17 30 22
[Screenshot AFTER] Role for viewer user with read security after change Screenshot 2024-05-07 at 17 29 30
### 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 ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) __Fixes: https://github.com/elastic/kibana/issues/182847__ ## Release note Changed privilege check from `manage_security` to `read_security`, so user with `read_security` can view remote indexes/remote clusters sections for role. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../edit_role_mapping_page.test.tsx | 73 +++++++++++-------- .../edit_role_mapping_page.tsx | 15 ++-- .../role_mappings_api_client.mock.ts | 1 - .../role_mappings/role_mappings_api_client.ts | 13 ---- .../role_mappings_grid_page.test.tsx | 73 +++++++++++-------- .../role_mappings_grid_page.tsx | 12 +-- .../role_mappings_management_app.test.tsx | 10 +-- .../role_mappings_management_app.tsx | 5 ++ .../roles/edit_role/edit_role_page.test.tsx | 2 +- .../roles/edit_role/edit_role_page.tsx | 12 ++- .../management/security_features/index.ts | 8 ++ .../security_features_api_client.mock.ts | 12 +++ .../security_features_api_client.ts | 25 +++++++ .../feature_check.test.ts | 32 ++++---- .../feature_check.ts | 26 +++---- .../server/routes/feature_check/index.ts | 13 ++++ .../plugins/security/server/routes/index.ts | 2 + .../server/routes/role_mapping/index.ts | 2 - .../common/platform_security/feature_check.ts | 22 ++++++ .../common/platform_security/index.ts | 1 + .../common/platform_security/role_mappings.ts | 7 -- 21 files changed, 226 insertions(+), 140 deletions(-) create mode 100644 x-pack/plugins/security/public/management/security_features/index.ts create mode 100644 x-pack/plugins/security/public/management/security_features/security_features_api_client.mock.ts create mode 100644 x-pack/plugins/security/public/management/security_features/security_features_api_client.ts rename x-pack/plugins/security/server/routes/{role_mapping => feature_check}/feature_check.test.ts (89%) rename x-pack/plugins/security/server/routes/{role_mapping => feature_check}/feature_check.ts (80%) create mode 100644 x-pack/plugins/security/server/routes/feature_check/index.ts create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/platform_security/feature_check.ts diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx index 226106a5eb09d..8fdd6dce1e3db 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx @@ -25,6 +25,7 @@ import type { Role } from '../../../../common'; import { RoleComboBox } from '../../role_combo_box'; import type { RolesAPIClient } from '../../roles'; import { rolesAPIClientMock } from '../../roles/roles_api_client.mock'; +import { securityFeaturesAPIClientMock } from '../../security_features/security_features_api_client.mock'; import { NoCompatibleRealms, PermissionDenied, SectionLoading } from '../components'; import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock'; @@ -34,6 +35,7 @@ describe('EditRoleMappingPage', () => { const renderView = ( roleMappingsAPI: ReturnType, + securityFeaturesAPI: ReturnType, name?: string, readOnly: boolean = false ) => { @@ -51,6 +53,7 @@ describe('EditRoleMappingPage', () => { action="edit" name={name} roleMappingsAPI={roleMappingsAPI} + securityFeaturesAPI={securityFeaturesAPI} rolesAPIClient={rolesAPI} notifications={coreStart.notifications} docLinks={coreStart.docLinks} @@ -72,15 +75,16 @@ describe('EditRoleMappingPage', () => { it('allows a role mapping to be created', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.saveRoleMapping.mockResolvedValue(null); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, canUseInlineScripts: true, canUseStoredScripts: true, }); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); await nextTick(); wrapper.update(); @@ -108,6 +112,7 @@ describe('EditRoleMappingPage', () => { it('allows a role mapping to be updated', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.saveRoleMapping.mockResolvedValue(null); roleMappingsAPI.getRoleMapping.mockResolvedValue({ name: 'foo', @@ -125,14 +130,14 @@ describe('EditRoleMappingPage', () => { bar: 'baz', }, }); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, canUseInlineScripts: true, canUseStoredScripts: true, }); - const wrapper = renderView(roleMappingsAPI, 'foo'); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, 'foo'); await nextTick(); wrapper.update(); @@ -165,12 +170,13 @@ describe('EditRoleMappingPage', () => { it('renders a permission denied message when unauthorized to manage role mappings', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: false, + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: false, hasCompatibleRealms: true, }); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(PermissionDenied)).toHaveLength(0); @@ -184,12 +190,13 @@ describe('EditRoleMappingPage', () => { it('renders a warning when there are no compatible realms enabled', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: false, }); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0); @@ -202,6 +209,7 @@ describe('EditRoleMappingPage', () => { it('renders a message when editing a mapping with deprecated roles assigned', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMapping.mockResolvedValue({ name: 'foo', roles: ['some-deprecated-role'], @@ -210,14 +218,14 @@ describe('EditRoleMappingPage', () => { field: { username: '*' }, }, }); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, canUseInlineScripts: true, canUseStoredScripts: true, }); - const wrapper = renderView(roleMappingsAPI, 'foo'); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, 'foo'); expect(findTestSubject(wrapper, 'deprecatedRolesAssigned')).toHaveLength(0); await nextTick(); @@ -228,6 +236,7 @@ describe('EditRoleMappingPage', () => { it('renders a warning when editing a mapping with a stored role template, when stored scripts are disabled', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMapping.mockResolvedValue({ name: 'foo', role_templates: [ @@ -240,14 +249,14 @@ describe('EditRoleMappingPage', () => { field: { username: '*' }, }, }); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, canUseInlineScripts: true, canUseStoredScripts: false, }); - const wrapper = renderView(roleMappingsAPI, 'foo'); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, 'foo'); expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0); expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0); @@ -260,6 +269,7 @@ describe('EditRoleMappingPage', () => { it('renders a warning when editing a mapping with an inline role template, when inline scripts are disabled', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMapping.mockResolvedValue({ name: 'foo', role_templates: [ @@ -272,14 +282,14 @@ describe('EditRoleMappingPage', () => { field: { username: '*' }, }, }); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, canUseInlineScripts: false, canUseStoredScripts: true, }); - const wrapper = renderView(roleMappingsAPI, 'foo'); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, 'foo'); expect(findTestSubject(wrapper, 'roleMappingInlineScriptsDisabled')).toHaveLength(0); expect(findTestSubject(wrapper, 'roleMappingStoredScriptsDisabled')).toHaveLength(0); @@ -292,6 +302,7 @@ describe('EditRoleMappingPage', () => { it('renders the visual editor by default for simple rule sets', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMapping.mockResolvedValue({ name: 'foo', roles: ['superuser'], @@ -316,14 +327,14 @@ describe('EditRoleMappingPage', () => { ], }, }); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, canUseInlineScripts: true, canUseStoredScripts: true, }); - const wrapper = renderView(roleMappingsAPI, 'foo'); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, 'foo'); await nextTick(); wrapper.update(); @@ -355,20 +366,21 @@ describe('EditRoleMappingPage', () => { }; const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMapping.mockResolvedValue({ name: 'foo', roles: ['superuser'], enabled: true, rules: createRule(10), }); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, canUseInlineScripts: true, canUseStoredScripts: true, }); - const wrapper = renderView(roleMappingsAPI, 'foo'); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, 'foo'); await nextTick(); wrapper.update(); @@ -378,6 +390,7 @@ describe('EditRoleMappingPage', () => { it('renders a readonly view when not enough privileges', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.saveRoleMapping.mockResolvedValue(null); roleMappingsAPI.getRoleMapping.mockResolvedValue({ name: 'foo', @@ -395,14 +408,14 @@ describe('EditRoleMappingPage', () => { bar: 'baz', }, }); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, canUseInlineScripts: true, canUseStoredScripts: true, }); - const wrapper = renderView(roleMappingsAPI, 'foo', true); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, 'foo', true); await nextTick(); wrapper.update(); diff --git a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx index c3dc778643dea..647cbd27b3921 100644 --- a/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx @@ -28,6 +28,7 @@ import { RuleEditorPanel } from './rule_editor_panel'; import { validateRoleMappingForSave } from './services/role_mapping_validation'; import type { RoleMapping } from '../../../../common'; import type { RolesAPIClient } from '../../roles'; +import type { SecurityFeaturesAPIClient } from '../../security_features'; import { DeleteProvider, NoCompatibleRealms, @@ -55,6 +56,7 @@ interface Props { name?: string; roleMappingsAPI: PublicMethodsOf; rolesAPIClient: PublicMethodsOf; + securityFeaturesAPI: PublicMethodsOf; notifications: NotificationsStart; docLinks: DocLinksStart; history: ScopedHistory; @@ -361,7 +363,7 @@ export class EditRoleMappingPage extends Component { private async loadAppData() { try { const [features, roleMapping] = await Promise.all([ - this.props.roleMappingsAPI.checkRoleMappingFeatures(), + this.props.securityFeaturesAPI.checkFeatures(), this.editingExistingRoleMapping() || this.cloningExistingRoleMapping() ? this.props.roleMappingsAPI.getRoleMapping(this.props.name!) : Promise.resolve({ @@ -374,15 +376,10 @@ export class EditRoleMappingPage extends Component { }), ]); - const { - canManageRoleMappings, - canUseStoredScripts, - canUseInlineScripts, - hasCompatibleRealms, - } = features; + const { canReadSecurity, canUseStoredScripts, canUseInlineScripts, hasCompatibleRealms } = + features; - const canLoad = canManageRoleMappings || this.props.readOnly; - const loadState: State['loadState'] = canLoad ? 'ready' : 'permissionDenied'; + const loadState: State['loadState'] = canReadSecurity ? 'ready' : 'permissionDenied'; this.setState({ loadState, diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.mock.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.mock.ts index b0aefea248cf0..e004edbe1d854 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.mock.ts +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.mock.ts @@ -7,7 +7,6 @@ export const roleMappingsAPIClientMock = { create: () => ({ - checkRoleMappingFeatures: jest.fn(), getRoleMappings: jest.fn(), getRoleMapping: jest.fn(), saveRoleMapping: jest.fn(), diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts index c2b1f08d90319..5aa16fde32a24 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_api_client.ts @@ -9,15 +9,6 @@ import type { HttpStart } from '@kbn/core/public'; import type { RoleMapping } from '../../../common'; -export interface CheckRoleMappingFeaturesResponse { - canManageRoleMappings: boolean; - canUseInlineScripts: boolean; - canUseStoredScripts: boolean; - hasCompatibleRealms: boolean; - canUseRemoteIndices: boolean; - canUseRemoteClusters: boolean; -} - type DeleteRoleMappingsResponse = Array<{ name: string; success: boolean; @@ -27,10 +18,6 @@ type DeleteRoleMappingsResponse = Array<{ export class RoleMappingsAPIClient { constructor(private readonly http: HttpStart) {} - public async checkRoleMappingFeatures(): Promise { - return this.http.get(`/internal/security/_check_role_mapping_features`); - } - public async getRoleMappings(): Promise { return this.http.get(`/internal/security/role_mapping`); } diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx index 7f2182e1a7e67..a9d770c50a1dd 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx @@ -17,6 +17,7 @@ import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test-jest-helpers import { EmptyPrompt } from './empty_prompt'; import { RoleMappingsGridPage } from './role_mappings_grid_page'; import { rolesAPIClientMock } from '../../roles/index.mock'; +import { securityFeaturesAPIClientMock } from '../../security_features/security_features_api_client.mock'; import { NoCompatibleRealms, PermissionDenied, SectionLoading } from '../components'; import { roleMappingsAPIClientMock } from '../role_mappings_api_client.mock'; @@ -26,6 +27,7 @@ describe('RoleMappingsGridPage', () => { const renderView = ( roleMappingsAPI: ReturnType, + securityFeaturesAPI: ReturnType, rolesAPI: ReturnType = rolesAPIClientMock.create(), readOnly: boolean = false ) => { @@ -40,6 +42,7 @@ describe('RoleMappingsGridPage', () => { { it('renders a create prompt when no role mappings exist', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, }); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(EmptyPrompt)).toHaveLength(0); @@ -88,12 +92,13 @@ describe('RoleMappingsGridPage', () => { it('renders a permission denied message when unauthorized to manage role mappings', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: false, + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: false, hasCompatibleRealms: true, }); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(PermissionDenied)).toHaveLength(0); @@ -107,6 +112,7 @@ describe('RoleMappingsGridPage', () => { it('renders a warning when there are no compatible realms enabled', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([ { name: 'some realm', @@ -115,12 +121,12 @@ describe('RoleMappingsGridPage', () => { rules: { field: { username: '*' } }, }, ]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: false, }); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(NoCompatibleRealms)).toHaveLength(0); @@ -133,6 +139,7 @@ describe('RoleMappingsGridPage', () => { it('renders links to mapped roles, even if the roles API call returns nothing', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([ { name: 'some realm', @@ -141,12 +148,12 @@ describe('RoleMappingsGridPage', () => { rules: { field: { username: '*' } }, }, ]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, }); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); await nextTick(); wrapper.update(); @@ -157,6 +164,7 @@ describe('RoleMappingsGridPage', () => { it('describes the number of mapped role templates', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([ { name: 'some realm', @@ -165,12 +173,12 @@ describe('RoleMappingsGridPage', () => { rules: { field: { username: '*' } }, }, ]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, }); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); await nextTick(); wrapper.update(); @@ -181,6 +189,7 @@ describe('RoleMappingsGridPage', () => { it('allows role mappings to be deleted, refreshing the grid after', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([ { name: 'some-realm', @@ -189,8 +198,8 @@ describe('RoleMappingsGridPage', () => { rules: { field: { username: '*' } }, }, ]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, }); roleMappingsAPI.deleteRoleMappings.mockResolvedValue([ @@ -200,7 +209,7 @@ describe('RoleMappingsGridPage', () => { }, ]); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); await nextTick(); wrapper.update(); @@ -224,6 +233,7 @@ describe('RoleMappingsGridPage', () => { it('renders a warning when a mapping is assigned a deprecated role', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([ { name: 'some-realm', @@ -232,8 +242,8 @@ describe('RoleMappingsGridPage', () => { rules: { field: { username: '*' } }, }, ]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, }); roleMappingsAPI.deleteRoleMappings.mockResolvedValue([ @@ -254,7 +264,7 @@ describe('RoleMappingsGridPage', () => { }, ]); - const wrapper = renderView(roleMappingsAPI, roleAPIClient); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, roleAPIClient); await nextTick(); wrapper.update(); @@ -269,6 +279,7 @@ describe('RoleMappingsGridPage', () => { it('renders role mapping actions as appropriate', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([ { name: 'some-realm', @@ -277,8 +288,8 @@ describe('RoleMappingsGridPage', () => { rules: { field: { username: '*' } }, }, ]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, }); roleMappingsAPI.deleteRoleMappings.mockResolvedValue([ @@ -288,7 +299,7 @@ describe('RoleMappingsGridPage', () => { }, ]); - const wrapper = renderView(roleMappingsAPI); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); await nextTick(); wrapper.update(); @@ -315,13 +326,14 @@ describe('RoleMappingsGridPage', () => { describe('read-only', () => { it('renders an empty prompt when no role mappings exist', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, }); - const wrapper = renderView(roleMappingsAPI, undefined, true); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, undefined, true); expect(wrapper.find(SectionLoading)).toHaveLength(1); expect(wrapper.find(EmptyPrompt)).toHaveLength(0); @@ -342,6 +354,7 @@ describe('RoleMappingsGridPage', () => { it('hides controls when `readOnly` is enabled', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); roleMappingsAPI.getRoleMappings.mockResolvedValue([ { name: 'some-realm', @@ -350,8 +363,8 @@ describe('RoleMappingsGridPage', () => { rules: { field: { username: '*' } }, }, ]); - roleMappingsAPI.checkRoleMappingFeatures.mockResolvedValue({ - canManageRoleMappings: true, + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, hasCompatibleRealms: true, }); roleMappingsAPI.deleteRoleMappings.mockResolvedValue([ @@ -361,7 +374,7 @@ describe('RoleMappingsGridPage', () => { }, ]); - const wrapper = renderView(roleMappingsAPI, undefined, true); + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, undefined, true); await nextTick(); wrapper.update(); diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx index 4ddcb83abf7f3..9539fce203372 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx @@ -39,6 +39,7 @@ import { } from '../../management_urls'; import { RoleTableDisplay } from '../../role_table_display'; import type { RolesAPIClient } from '../../roles'; +import type { SecurityFeaturesAPIClient } from '../../security_features'; import { DeleteProvider, NoCompatibleRealms, @@ -50,6 +51,7 @@ import type { RoleMappingsAPIClient } from '../role_mappings_api_client'; interface Props { rolesAPIClient: PublicMethodsOf; roleMappingsAPI: PublicMethodsOf; + securityFeaturesAPI: PublicMethodsOf; notifications: NotificationsStart; docLinks: DocLinksStart; history: ScopedHistory; @@ -447,17 +449,15 @@ export class RoleMappingsGridPage extends Component { private async checkPrivileges() { try { - const { canManageRoleMappings, hasCompatibleRealms } = - await this.props.roleMappingsAPI.checkRoleMappingFeatures(); - - const canLoad = canManageRoleMappings || this.props.readOnly; + const { canReadSecurity, hasCompatibleRealms } = + await this.props.securityFeaturesAPI.checkFeatures(); this.setState({ - loadState: canLoad ? this.state.loadState : 'permissionDenied', + loadState: canReadSecurity ? this.state.loadState : 'permissionDenied', hasCompatibleRealms, }); - if (canLoad) { + if (canReadSecurity) { this.performInitialLoad(); } } catch (e) { diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx index 2f0ad8f240fa3..94fb0de42543f 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.test.tsx @@ -90,7 +90,7 @@ describe('roleMappingsManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Mappings Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"readOnly":false} + Role Mappings Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"securityFeaturesAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"readOnly":false}
`); @@ -112,7 +112,7 @@ describe('roleMappingsManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Mappings Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"readOnly":true} + Role Mappings Page: {"notifications":{"toasts":{}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"securityFeaturesAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"readOnly":true}
`); @@ -137,7 +137,7 @@ describe('roleMappingsManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Mapping Edit Page: {"action":"edit","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}},"readOnly":false} + Role Mapping Edit Page: {"action":"edit","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"securityFeaturesAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}},"readOnly":false}
`); @@ -167,7 +167,7 @@ describe('roleMappingsManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Mapping Edit Page: {"action":"edit","name":"role@mapping","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@mapping","search":"","hash":""}},"readOnly":false} + Role Mapping Edit Page: {"action":"edit","name":"role@mapping","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"securityFeaturesAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@mapping","search":"","hash":""}},"readOnly":false}
`); @@ -198,7 +198,7 @@ describe('roleMappingsManagementApp', () => { expect(docTitle.reset).not.toHaveBeenCalled(); expect(container).toMatchInlineSnapshot(`
- Role Mapping Edit Page: {"action":"edit","name":"role@mapping","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@mapping","search":"","hash":""}},"readOnly":true} + Role Mapping Edit Page: {"action":"edit","name":"role@mapping","roleMappingsAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"securityFeaturesAPI":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":"","assetsHrefBase":""},"anonymousPaths":{},"externalUrl":{},"staticAssets":{}}},"notifications":{"toasts":{}},"docLinks":{},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@mapping","search":"","hash":""}},"readOnly":true}
`); diff --git a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx index b3fc8ec71b890..d81fd1498417c 100644 --- a/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx +++ b/x-pack/plugins/security/public/management/role_mappings/role_mappings_management_app.tsx @@ -47,17 +47,20 @@ export const roleMappingsManagementApp = Object.freeze({ { EditRoleMappingPage }, { RoleMappingsAPIClient }, { RolesAPIClient }, + { SecurityFeaturesAPIClient }, ] = await Promise.all([ getStartServices(), import('./role_mappings_grid'), import('./edit_role_mapping'), import('./role_mappings_api_client'), import('../roles'), + import('../security_features'), ]); core.chrome.docTitle.change(title); const roleMappingsAPIClient = new RoleMappingsAPIClient(core.http); + const securityFeaturesAPIClient = new SecurityFeaturesAPIClient(core.http); const EditRoleMappingsPageWithBreadcrumbs = ({ action }: { action: 'edit' | 'clone' }) => { const { name } = useParams<{ name?: string }>(); @@ -81,6 +84,7 @@ export const roleMappingsManagementApp = Object.freeze({ action={action} name={decodedName} roleMappingsAPI={roleMappingsAPIClient} + securityFeaturesAPI={securityFeaturesAPIClient} rolesAPIClient={new RolesAPIClient(core.http)} notifications={core.notifications} docLinks={core.docLinks} @@ -114,6 +118,7 @@ export const roleMappingsManagementApp = Object.freeze({ notifications={core.notifications} rolesAPIClient={new RolesAPIClient(core.http)} roleMappingsAPI={roleMappingsAPIClient} + securityFeaturesAPI={securityFeaturesAPIClient} docLinks={core.docLinks} history={history} navigateToApp={core.application.navigateToApp} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx index 93d8fec9e15a0..5f345020d6d8f 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.test.tsx @@ -186,7 +186,7 @@ function getProps({ } return buildSpaces(); } - if (path === '/internal/security/_check_role_mapping_features') { + if (path === '/internal/security/_check_security_features') { return { canUseRemoteIndices }; } if (path === REMOTE_CLUSTERS_PATH) { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx index 697b85feb9edb..060dc5a347fc4 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/edit_role_page.tsx @@ -69,7 +69,7 @@ import { prepareRoleClone, } from '../../../../common/model'; import { useCapabilities } from '../../../components/use_capabilities'; -import type { CheckRoleMappingFeaturesResponse } from '../../role_mappings/role_mappings_api_client'; +import type { CheckSecurityFeaturesResponse } from '../../security_features'; import type { UserAPIClient } from '../../users'; import type { IndicesAPIClient } from '../indices_api_client'; import { KibanaPrivileges } from '../model'; @@ -101,25 +101,23 @@ function useRemoteClusters(http: HttpStart) { return useAsync(() => http.get(REMOTE_CLUSTERS_PATH)); } -interface CheckRoleMappingFeaturesResponseWhenServerless { +interface CheckSecurityFeaturesResponseWhenServerless { value: boolean; } function useFeatureCheck( http: HttpStart, buildFlavor: 'serverless' -): AsyncState; +): AsyncState; function useFeatureCheck( http: HttpStart, buildFlavor: BuildFlavor -): AsyncState; +): AsyncState; function useFeatureCheck(http: HttpStart, buildFlavor?: BuildFlavor) { return useAsync(async () => { if (buildFlavor !== 'serverless') { - return http.get( - '/internal/security/_check_role_mapping_features' - ); + return http.get('/internal/security/_check_security_features'); } return { value: true }; }, [http, buildFlavor]); diff --git a/x-pack/plugins/security/public/management/security_features/index.ts b/x-pack/plugins/security/public/management/security_features/index.ts new file mode 100644 index 0000000000000..e1e65c9216275 --- /dev/null +++ b/x-pack/plugins/security/public/management/security_features/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export { SecurityFeaturesAPIClient } from './security_features_api_client'; +export type { CheckSecurityFeaturesResponse } from './security_features_api_client'; diff --git a/x-pack/plugins/security/public/management/security_features/security_features_api_client.mock.ts b/x-pack/plugins/security/public/management/security_features/security_features_api_client.mock.ts new file mode 100644 index 0000000000000..41b234adc10f8 --- /dev/null +++ b/x-pack/plugins/security/public/management/security_features/security_features_api_client.mock.ts @@ -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. + */ + +export const securityFeaturesAPIClientMock = { + create: () => ({ + checkFeatures: jest.fn(), + }), +}; diff --git a/x-pack/plugins/security/public/management/security_features/security_features_api_client.ts b/x-pack/plugins/security/public/management/security_features/security_features_api_client.ts new file mode 100644 index 0000000000000..ededbdcc251ea --- /dev/null +++ b/x-pack/plugins/security/public/management/security_features/security_features_api_client.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { HttpStart } from '@kbn/core/public'; + +export interface CheckSecurityFeaturesResponse { + canReadSecurity: boolean; + canUseInlineScripts: boolean; + canUseStoredScripts: boolean; + hasCompatibleRealms: boolean; + canUseRemoteIndices: boolean; + canUseRemoteClusters: boolean; +} + +export class SecurityFeaturesAPIClient { + constructor(private readonly http: HttpStart) {} + + public async checkFeatures(): Promise { + return this.http.get(`/internal/security/_check_security_features`); + } +} diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts b/x-pack/plugins/security/server/routes/feature_check/feature_check.test.ts similarity index 89% rename from x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts rename to x-pack/plugins/security/server/routes/feature_check/feature_check.test.ts index c0bf0041ed0ac..953f65afa9a7c 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts +++ b/x-pack/plugins/security/server/routes/feature_check/feature_check.test.ts @@ -9,12 +9,12 @@ import { kibanaResponseFactory } from '@kbn/core/server'; import { coreMock, httpServerMock } from '@kbn/core/server/mocks'; import type { LicenseCheck } from '@kbn/licensing-plugin/server'; -import { defineRoleMappingFeatureCheckRoute } from './feature_check'; +import { defineSecurityFeatureCheckRoute } from './feature_check'; import { routeDefinitionParamsMock } from '../index.mock'; interface TestOptions { licenseCheckResult?: LicenseCheck; - canManageRoleMappings?: boolean; + canReadSecurity?: boolean; nodeSettingsResponse?: () => Record; xpackUsageResponse?: () => Record; asserts: { statusCode: number; result?: Record }; @@ -43,7 +43,7 @@ describe('GET role mappings feature check', () => { description: string, { licenseCheckResult = { state: 'valid' }, - canManageRoleMappings = true, + canReadSecurity = true, nodeSettingsResponse = () => ({}), xpackUsageResponse = () => defaultXpackUsageResponse, asserts, @@ -68,16 +68,16 @@ describe('GET role mappings feature check', () => { (async () => xpackUsageResponse()) as any ); mockCoreContext.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ - has_all_requested: canManageRoleMappings, + has_all_requested: canReadSecurity, } as any); - defineRoleMappingFeatureCheckRoute(mockRouteDefinitionParams); + defineSecurityFeatureCheckRoute(mockRouteDefinitionParams); const [[, handler]] = mockRouteDefinitionParams.router.get.mock.calls; const headers = { authorization: 'foo' }; const mockRequest = httpServerMock.createKibanaRequest({ method: 'get', - path: `/internal/security/_check_role_mapping_features`, + path: `/internal/security/_check_security_features`, headers, }); @@ -93,7 +93,7 @@ describe('GET role mappings feature check', () => { asserts: { statusCode: 200, result: { - canManageRoleMappings: true, + canReadSecurity: true, canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: true, @@ -118,7 +118,7 @@ describe('GET role mappings feature check', () => { asserts: { statusCode: 200, result: { - canManageRoleMappings: true, + canReadSecurity: true, canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: true, @@ -138,7 +138,7 @@ describe('GET role mappings feature check', () => { asserts: { statusCode: 200, result: { - canManageRoleMappings: true, + canReadSecurity: true, canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: true, @@ -164,7 +164,7 @@ describe('GET role mappings feature check', () => { asserts: { statusCode: 200, result: { - canManageRoleMappings: true, + canReadSecurity: true, canUseInlineScripts: true, canUseStoredScripts: false, hasCompatibleRealms: true, @@ -189,7 +189,7 @@ describe('GET role mappings feature check', () => { asserts: { statusCode: 200, result: { - canManageRoleMappings: true, + canReadSecurity: true, canUseInlineScripts: false, canUseStoredScripts: true, hasCompatibleRealms: true, @@ -218,7 +218,7 @@ describe('GET role mappings feature check', () => { asserts: { statusCode: 200, result: { - canManageRoleMappings: true, + canReadSecurity: true, canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: false, @@ -228,12 +228,12 @@ describe('GET role mappings feature check', () => { }, }); - getFeatureCheckTest('indicates canManageRoleMappings=false for users without `manage_security`', { - canManageRoleMappings: false, + getFeatureCheckTest('indicates canReadSecurity=false for users without `read_security`', { + canReadSecurity: false, asserts: { statusCode: 200, result: { - canManageRoleMappings: false, + canReadSecurity: false, }, }, }); @@ -250,7 +250,7 @@ describe('GET role mappings feature check', () => { asserts: { statusCode: 200, result: { - canManageRoleMappings: true, + canReadSecurity: true, canUseInlineScripts: true, canUseStoredScripts: true, hasCompatibleRealms: false, diff --git a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts b/x-pack/plugins/security/server/routes/feature_check/feature_check.ts similarity index 80% rename from x-pack/plugins/security/server/routes/role_mapping/feature_check.ts rename to x-pack/plugins/security/server/routes/feature_check/feature_check.ts index 6aca083dab1e2..442f040398962 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/feature_check.ts +++ b/x-pack/plugins/security/server/routes/feature_check/feature_check.ts @@ -39,49 +39,49 @@ interface XPackUsageResponse { const INCOMPATIBLE_REALMS = ['file', 'native']; -export function defineRoleMappingFeatureCheckRoute({ router, logger }: RouteDefinitionParams) { +export function defineSecurityFeatureCheckRoute({ router, logger }: RouteDefinitionParams) { router.get( { - path: '/internal/security/_check_role_mapping_features', + path: '/internal/security/_check_security_features', validate: false, }, createLicensedRouteHandler(async (context, request, response) => { const esClient = (await context.core).elasticsearch.client; - const { has_all_requested: canManageRoleMappings } = + const { has_all_requested: canReadSecurity } = await esClient.asCurrentUser.security.hasPrivileges({ - body: { cluster: ['manage_security'] }, + body: { cluster: ['read_security'] }, }); - if (!canManageRoleMappings) { + if (!canReadSecurity) { return response.ok({ body: { - canManageRoleMappings, + canReadSecurity, }, }); } - const enabledFeatures = await getEnabledRoleMappingsFeatures(esClient.asInternalUser, logger); + const enabledFeatures = await getEnabledSecurityFeatures(esClient.asInternalUser, logger); return response.ok({ body: { ...enabledFeatures, - canManageRoleMappings, + canReadSecurity, }, }); }) ); } -async function getEnabledRoleMappingsFeatures(esClient: ElasticsearchClient, logger: Logger) { - logger.debug(`Retrieving role mappings features`); +async function getEnabledSecurityFeatures(esClient: ElasticsearchClient, logger: Logger) { + logger.debug(`Retrieving security features`); const nodeScriptSettingsPromise = esClient.nodes .info({ filter_path: 'nodes.*.settings.script' }) .catch((error) => { // fall back to assuming that node settings are unset/at their default values. - // this will allow the role mappings UI to permit both role template script types, + // this will allow the UI to permit both role template script types, // even if ES will disallow it at mapping evaluation time. - logger.error(`Error retrieving node settings for role mappings: ${error}`); + logger.error(`Error retrieving node settings for security feature check: ${error}`); return {}; }); @@ -94,7 +94,7 @@ async function getEnabledRoleMappingsFeatures(esClient: ElasticsearchClient, log // fall back to no external realms configured. // this will cause a warning in the UI about no compatible realms being enabled, but will otherwise allow // the mappings screen to function correctly. - logger.error(`Error retrieving XPack usage info for role mappings: ${error}`); + logger.error(`Error retrieving XPack usage info for security feature check: ${error}`); return { security: { realms: {}, diff --git a/x-pack/plugins/security/server/routes/feature_check/index.ts b/x-pack/plugins/security/server/routes/feature_check/index.ts new file mode 100644 index 0000000000000..b482139fd83d5 --- /dev/null +++ b/x-pack/plugins/security/server/routes/feature_check/index.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { defineSecurityFeatureCheckRoute } from './feature_check'; +import type { RouteDefinitionParams } from '..'; + +export function defineSecurityFeatureRoutes(params: RouteDefinitionParams) { + defineSecurityFeatureCheckRoute(params); +} diff --git a/x-pack/plugins/security/server/routes/index.ts b/x-pack/plugins/security/server/routes/index.ts index 99739208e6b7a..212cf6768d1aa 100644 --- a/x-pack/plugins/security/server/routes/index.ts +++ b/x-pack/plugins/security/server/routes/index.ts @@ -18,6 +18,7 @@ import { defineApiKeysRoutes } from './api_keys'; import { defineAuthenticationRoutes } from './authentication'; import { defineAuthorizationRoutes } from './authorization'; import { defineDeprecationsRoutes } from './deprecations'; +import { defineSecurityFeatureRoutes } from './feature_check'; import { defineIndicesRoutes } from './indices'; import { defineRoleMappingRoutes } from './role_mapping'; import { defineSecurityCheckupGetStateRoutes } from './security_checkup'; @@ -74,6 +75,7 @@ export function defineRoutes(params: RouteDefinitionParams) { defineDeprecationsRoutes(params); // deprecated kibana user roles are not applicable, these HTTP APIs are not needed defineIndicesRoutes(params); // the ES privileges form used to help define roles (only consumer) is disabled, so there is no need for these HTTP APIs defineRoleMappingRoutes(params); // role mappings are managed internally, based on configurations in control plane, these HTTP APIs are not needed + defineSecurityFeatureRoutes(params); defineSecurityCheckupGetStateRoutes(params); // security checkup is not applicable, these HTTP APIs are not needed // defineUsersRoutes(params); // the native realm is not enabled (there is only Elastic cloud SAML), no user HTTP API routes are needed } diff --git a/x-pack/plugins/security/server/routes/role_mapping/index.ts b/x-pack/plugins/security/server/routes/role_mapping/index.ts index 49ad928c0b7e4..46db773ded991 100644 --- a/x-pack/plugins/security/server/routes/role_mapping/index.ts +++ b/x-pack/plugins/security/server/routes/role_mapping/index.ts @@ -6,13 +6,11 @@ */ import { defineRoleMappingDeleteRoutes } from './delete'; -import { defineRoleMappingFeatureCheckRoute } from './feature_check'; import { defineRoleMappingGetRoutes } from './get'; import { defineRoleMappingPostRoutes } from './post'; import type { RouteDefinitionParams } from '..'; export function defineRoleMappingRoutes(params: RouteDefinitionParams) { - defineRoleMappingFeatureCheckRoute(params); defineRoleMappingGetRoutes(params); defineRoleMappingPostRoutes(params); defineRoleMappingDeleteRoutes(params); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/feature_check.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/feature_check.ts new file mode 100644 index 0000000000000..e42cb88eec0f8 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/feature_check.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const svlCommonApi = getService('svlCommonApi'); + const supertest = getService('supertest'); + + describe('security/features', function () { + it('route access disabled', async () => { + const { body, status } = await supertest + .get('/internal/security/_check_security_features') + .set(svlCommonApi.getInternalRequestHeader()); + svlCommonApi.assertApiNotFound(body, status); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts index 86d4ad05cfc35..fab6e47e87969 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/index.ts @@ -25,5 +25,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./request_as_viewer')); loadTestFile(require.resolve('./user_profiles')); loadTestFile(require.resolve('./views')); + loadTestFile(require.resolve('./feature_check')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/role_mappings.ts b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/role_mappings.ts index 4d1f25cfd772f..bfddf5b4a3267 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/platform_security/role_mappings.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/platform_security/role_mappings.ts @@ -42,13 +42,6 @@ export default function ({ getService }: FtrProviderContext) { .set(svlCommonApi.getInternalRequestHeader()); svlCommonApi.assertApiNotFound(body, status); }); - - it('role mapping feature check', async () => { - const { body, status } = await supertest - .get('/internal/security/_check_role_mapping_features') - .set(svlCommonApi.getInternalRequestHeader()); - svlCommonApi.assertApiNotFound(body, status); - }); }); }); }); From a72ddc095903bcdc6441e17706be627c661da870 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Thu, 16 May 2024 19:22:42 +0200 Subject: [PATCH 19/71] [Security Solution] Using Chrome on CI for Cypress (#183515) ## Summary Fixes: https://github.com/elastic/kibana/issues/183098 Lately we have experienced some issues with our Cypress tests, where we are seeing that tests are passing but the output is marked as a failure. @patrykkopycinski has done a great job trying to fix the issue but lately we have seen that is still reproducible. The issue seems to be related directly with electron, so in this PR we reenabling chrome on CI to try to fix it. In order to do so, some adjustments has been introduced to woraround the chrome out of memory crashes that we faced before and was the main reason to move to electron. --------- Co-authored-by: Patryk Kopycinski --- .../management/cypress/cypress_base.config.ts | 17 +++++++++++++++-- .../scripts/run_cypress/parallel.ts | 12 ++++++------ .../scripts/run_cypress/parallel_serverless.ts | 3 ++- .../cypress/cypress.config.ts | 17 +++++++++++++++-- .../cypress/cypress_ci.config.ts | 17 +++++++++++++++-- .../cypress/cypress_ci_serverless.config.ts | 17 +++++++++++++++-- .../cypress/cypress_ci_serverless_qa.config.ts | 17 +++++++++++++++-- .../cypress/cypress_serverless.config.ts | 17 +++++++++++++++-- .../cypress/screens/create_new_rule.ts | 2 ++ .../cypress/tasks/timeline.ts | 6 +++++- 10 files changed, 105 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/cypress/cypress_base.config.ts b/x-pack/plugins/security_solution/public/management/cypress/cypress_base.config.ts index 6d8136fe2f22d..a2755f360d833 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/cypress_base.config.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/cypress_base.config.ts @@ -27,6 +27,7 @@ export const getCypressBaseConfig = ( configFile: './public/management/reporter_config.json', }, + chromeWebSecurity: false, defaultCommandTimeout: 60000, execTimeout: 120000, pageLoadTimeout: 12000, @@ -42,8 +43,8 @@ export const getCypressBaseConfig = ( video: true, videoCompression: 15, videosFolder: '../../../target/kibana-security-solution/public/management/cypress/videos', - viewportHeight: 900, - viewportWidth: 1440, + viewportHeight: 1200, + viewportWidth: 1920, experimentalStudio: true, env: { @@ -80,6 +81,18 @@ export const getCypressBaseConfig = ( experimentalMemoryManagement: true, experimentalInteractiveRunEvents: true, setupNodeEvents: (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => { + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'chrome' && browser.isHeadless) { + launchOptions.args.push('--window-size=1920,1200'); + return launchOptions; + } + if (browser.family === 'chromium') { + launchOptions.args.push( + '--js-flags="--max_old_space_size=4096 --max_semi_space_size=1024"' + ); + } + return launchOptions; + }); registerDataSession(on, config); // IMPORTANT: setting the log level should happen before any tooling is called setupToolingLogLevel(config); diff --git a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts index e99e382598039..e12e70cb0aa9c 100644 --- a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts +++ b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts @@ -432,7 +432,7 @@ ${JSON.stringify(cyCustomEnv, null, 2)} } else { try { result = await cypress.run({ - browser: 'electron', + browser: 'chrome', spec: filePath, configFile: cypressConfigFilePath, reporter: argv.reporter as string, @@ -445,6 +445,7 @@ ${JSON.stringify(cyCustomEnv, null, 2)} numTestsKeptInMemory: 0, env: cyCustomEnv, }, + runnerUi: !process.env.CI, }); if (!(result as CypressCommandLine.CypressRunResult)?.totalFailed) { _.pull(failedSpecFilePaths, filePath); @@ -509,12 +510,11 @@ ${JSON.stringify(cyCustomEnv, null, 2)} const hasFailedInitialTests = hasFailedTests(initialResults); const hasFailedRetryTests = hasFailedTests(retryResults); - if (finalResults.length !== files.length) { - throw createFailError('Cypress crashed', { exitCode: -1 }); - } - // If the initialResults had failures and failedSpecFilePaths was not populated properly return errors - if (hasFailedRetryTests || (hasFailedInitialTests && !retryResults.length)) { + if ( + (hasFailedRetryTests && failedSpecFilePaths.length) || + (hasFailedInitialTests && !retryResults.length) + ) { throw createFailError('Not all tests passed'); } }, diff --git a/x-pack/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts b/x-pack/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts index 57a5e9705e7cf..e9d2f3ba52b24 100644 --- a/x-pack/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts +++ b/x-pack/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts @@ -541,7 +541,7 @@ ${JSON.stringify(cypressConfigFile, null, 2)} } else { try { result = await cypress.run({ - browser: 'electron', + browser: 'chrome', spec: filePath, configFile: cypressConfigFilePath, reporter: argv.reporter as string, @@ -554,6 +554,7 @@ ${JSON.stringify(cypressConfigFile, null, 2)} numTestsKeptInMemory: 0, env: cyCustomEnv, }, + runnerUi: !process.env.CI, }); if ((result as CypressCommandLine.CypressRunResult)?.totalFailed) { failedSpecFilePaths.push(filePath); diff --git a/x-pack/test/security_solution_cypress/cypress/cypress.config.ts b/x-pack/test/security_solution_cypress/cypress/cypress.config.ts index 387f3b7aec118..ef9a5b6130966 100644 --- a/x-pack/test/security_solution_cypress/cypress/cypress.config.ts +++ b/x-pack/test/security_solution_cypress/cypress/cypress.config.ts @@ -9,6 +9,7 @@ import { defineCypressConfig } from '@kbn/cypress-config'; import { esArchiver } from './support/es_archiver'; export default defineCypressConfig({ + chromeWebSecurity: false, defaultCommandTimeout: 60000, env: { grepFilterSpecs: true, @@ -21,8 +22,8 @@ export default defineCypressConfig({ trashAssetsBeforeRuns: false, video: false, videosFolder: '../../../target/kibana-security-solution/cypress/videos', - viewportHeight: 946, - viewportWidth: 1680, + viewportHeight: 1200, + viewportWidth: 1920, numTestsKeptInMemory: 10, e2e: { experimentalRunAllSpecs: true, @@ -30,6 +31,18 @@ export default defineCypressConfig({ experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem'], setupNodeEvents(on, config) { esArchiver(on, config); + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'chrome' && browser.isHeadless) { + launchOptions.args.push('--window-size=1920,1200'); + return launchOptions; + } + if (browser.family === 'chromium') { + launchOptions.args.push( + '--js-flags="--max_old_space_size=4096 --max_semi_space_size=1024"' + ); + } + return launchOptions; + }); // eslint-disable-next-line @typescript-eslint/no-var-requires require('@cypress/grep/src/plugin')(config); return config; diff --git a/x-pack/test/security_solution_cypress/cypress/cypress_ci.config.ts b/x-pack/test/security_solution_cypress/cypress/cypress_ci.config.ts index bb632fb237c9d..3fae578ab1145 100644 --- a/x-pack/test/security_solution_cypress/cypress/cypress_ci.config.ts +++ b/x-pack/test/security_solution_cypress/cypress/cypress_ci.config.ts @@ -14,6 +14,7 @@ export default defineCypressConfig({ reporterOptions: { configFile: './cypress/reporter_config.json', }, + chromeWebSecurity: false, defaultCommandTimeout: 150000, env: { grepFilterSpecs: true, @@ -30,8 +31,8 @@ export default defineCypressConfig({ trashAssetsBeforeRuns: false, video: false, videosFolder: '../../../target/kibana-security-solution/cypress/videos', - viewportHeight: 946, - viewportWidth: 1680, + viewportHeight: 1200, + viewportWidth: 1920, e2e: { baseUrl: 'http://localhost:5601', experimentalMemoryManagement: true, @@ -39,6 +40,18 @@ export default defineCypressConfig({ specPattern: './cypress/e2e/**/*.cy.ts', setupNodeEvents(on, config) { esArchiver(on, config); + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'chrome' && browser.isHeadless) { + launchOptions.args.push('--window-size=1920,1200'); + return launchOptions; + } + if (browser.family === 'chromium') { + launchOptions.args.push( + '--js-flags="--max_old_space_size=4096 --max_semi_space_size=1024"' + ); + } + return launchOptions; + }); // eslint-disable-next-line @typescript-eslint/no-var-requires require('@cypress/grep/src/plugin')(config); return config; diff --git a/x-pack/test/security_solution_cypress/cypress/cypress_ci_serverless.config.ts b/x-pack/test/security_solution_cypress/cypress/cypress_ci_serverless.config.ts index 80afa64fbb7a5..a5d59ca4dd4c6 100644 --- a/x-pack/test/security_solution_cypress/cypress/cypress_ci_serverless.config.ts +++ b/x-pack/test/security_solution_cypress/cypress/cypress_ci_serverless.config.ts @@ -15,6 +15,7 @@ export default defineCypressConfig({ reporterOptions: { configFile: './cypress/reporter_config.json', }, + chromeWebSecurity: false, defaultCommandTimeout: 150000, env: { grepFilterSpecs: true, @@ -31,8 +32,8 @@ export default defineCypressConfig({ trashAssetsBeforeRuns: false, video: false, videosFolder: '../../../../target/kibana-security-solution/cypress/videos', - viewportHeight: 946, - viewportWidth: 1680, + viewportHeight: 1200, + viewportWidth: 1920, e2e: { baseUrl: 'http://localhost:5601', experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem'], @@ -40,6 +41,18 @@ export default defineCypressConfig({ specPattern: './cypress/e2e/**/*.cy.ts', setupNodeEvents(on, config) { esArchiver(on, config); + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'chrome' && browser.isHeadless) { + launchOptions.args.push('--window-size=1920,1200'); + return launchOptions; + } + if (browser.family === 'chromium') { + launchOptions.args.push( + '--js-flags="--max_old_space_size=4096 --max_semi_space_size=1024"' + ); + } + return launchOptions; + }); samlAuthentication(on, config); // eslint-disable-next-line @typescript-eslint/no-var-requires require('@cypress/grep/src/plugin')(config); diff --git a/x-pack/test/security_solution_cypress/cypress/cypress_ci_serverless_qa.config.ts b/x-pack/test/security_solution_cypress/cypress/cypress_ci_serverless_qa.config.ts index d0d5dd3f00154..c2ad97b6ddb05 100644 --- a/x-pack/test/security_solution_cypress/cypress/cypress_ci_serverless_qa.config.ts +++ b/x-pack/test/security_solution_cypress/cypress/cypress_ci_serverless_qa.config.ts @@ -15,6 +15,7 @@ export default defineCypressConfig({ reporterOptions: { configFile: './cypress/reporter_config.json', }, + chromeWebSecurity: false, defaultCommandTimeout: 300000, env: { grepFilterSpecs: true, @@ -33,8 +34,8 @@ export default defineCypressConfig({ trashAssetsBeforeRuns: false, video: false, videosFolder: '../../../../target/kibana-security-solution/cypress/videos', - viewportHeight: 946, - viewportWidth: 1680, + viewportHeight: 1200, + viewportWidth: 1920, e2e: { baseUrl: 'http://localhost:5601', experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem'], @@ -42,6 +43,18 @@ export default defineCypressConfig({ specPattern: './cypress/e2e/**/*.cy.ts', setupNodeEvents(on, config) { esArchiver(on, config); + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'chrome' && browser.isHeadless) { + launchOptions.args.push('--window-size=1920,1200'); + return launchOptions; + } + if (browser.family === 'chromium') { + launchOptions.args.push( + '--js-flags="--max_old_space_size=4096 --max_semi_space_size=1024"' + ); + } + return launchOptions; + }); samlAuthentication(on, config); process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // eslint-disable-next-line @typescript-eslint/no-var-requires diff --git a/x-pack/test/security_solution_cypress/cypress/cypress_serverless.config.ts b/x-pack/test/security_solution_cypress/cypress/cypress_serverless.config.ts index 8f66e7f1a4173..5ca79319c8c32 100644 --- a/x-pack/test/security_solution_cypress/cypress/cypress_serverless.config.ts +++ b/x-pack/test/security_solution_cypress/cypress/cypress_serverless.config.ts @@ -11,6 +11,7 @@ import { samlAuthentication } from './support/saml_auth'; // eslint-disable-next-line import/no-default-export export default defineCypressConfig({ + chromeWebSecurity: false, defaultCommandTimeout: 60000, execTimeout: 60000, pageLoadTimeout: 60000, @@ -19,8 +20,8 @@ export default defineCypressConfig({ trashAssetsBeforeRuns: false, video: false, videosFolder: '../../../target/kibana-security-solution/cypress/videos', - viewportHeight: 946, - viewportWidth: 1680, + viewportHeight: 1200, + viewportWidth: 1920, numTestsKeptInMemory: 10, env: { grepFilterSpecs: true, @@ -32,6 +33,18 @@ export default defineCypressConfig({ experimentalMemoryManagement: true, setupNodeEvents(on, config) { esArchiver(on, config); + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'chrome' && browser.isHeadless) { + launchOptions.args.push('--window-size=1920,1200'); + return launchOptions; + } + if (browser.family === 'chromium') { + launchOptions.args.push( + '--js-flags="--max_old_space_size=4096 --max_semi_space_size=1024"' + ); + } + return launchOptions; + }); samlAuthentication(on, config); // eslint-disable-next-line @typescript-eslint/no-var-requires require('@cypress/grep/src/plugin')(config); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts index 88b8c2192d1bf..d8feace6af1d0 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts @@ -115,6 +115,8 @@ export const EQL_QUERY_INPUT = '[data-test-subj="eqlQueryBarTextInput"]'; export const EQL_QUERY_VALIDATION_SPINNER = '[data-test-subj="eql-validation-loading"]'; +export const EQL_QUERY_VALIDATION_LABEL = '.euiFormLabel-isInvalid'; + export const EQL_QUERY_VALIDATION_ERROR = '[data-test-subj="eql-validation-errors-popover-button"]'; export const EQL_OPTIONS_POPOVER_TRIGGER = '[data-test-subj="eql-settings-trigger"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts index 0b6fb3c31cb6a..75c78c24e4dc6 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -10,7 +10,10 @@ import type { Timeline, TimelineFilter } from '../objects/timeline'; import { ALL_CASES_CREATE_NEW_CASE_TABLE_BTN } from '../screens/all_cases'; import { FIELDS_BROWSER_CHECKBOX } from '../screens/fields_browser'; -import { EQL_QUERY_VALIDATION_SPINNER } from '../screens/create_new_rule'; +import { + EQL_QUERY_VALIDATION_LABEL, + EQL_QUERY_VALIDATION_SPINNER, +} from '../screens/create_new_rule'; import { ADD_FILTER, @@ -188,6 +191,7 @@ export const clearEqlInTimeline = () => { cy.get(TIMELINE_CORRELATION_INPUT).type('{selectAll} {del}'); cy.get(TIMELINE_CORRELATION_INPUT).clear(); cy.get(EQL_QUERY_VALIDATION_SPINNER).should('not.exist'); + cy.get(EQL_QUERY_VALIDATION_LABEL).should('not.exist'); }; export const addFilter = (filter: TimelineFilter): Cypress.Chainable> => { From b697d8fde29403b8f1371e6d7f1f3ee61805f628 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 16 May 2024 11:27:40 -0600 Subject: [PATCH 20/71] [ES|QL] Remove OPTIONS clause in FROM command (#183656) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Before: Screenshot 2024-05-16 at 9 07 22 AM After: Screenshot 2024-05-16 at 9 05 27 AM Elasticsearch PR: https://github.com/elastic/elasticsearch/pull/108692 --- packages/kbn-esql-ast/src/antlr/esql_lexer.g4 | 3 +- .../kbn-esql-ast/src/antlr/esql_lexer.interp | 5 +- .../kbn-esql-ast/src/antlr/esql_lexer.tokens | 92 +- packages/kbn-esql-ast/src/antlr/esql_lexer.ts | 869 ++++++----- .../kbn-esql-ast/src/antlr/esql_parser.g4 | 18 +- .../kbn-esql-ast/src/antlr/esql_parser.interp | 6 +- .../kbn-esql-ast/src/antlr/esql_parser.tokens | 92 +- .../kbn-esql-ast/src/antlr/esql_parser.ts | 1362 ++++++++--------- .../src/antlr/esql_parser_listener.ts | 22 - packages/kbn-esql-ast/src/ast_helpers.ts | 2 +- 10 files changed, 1124 insertions(+), 1347 deletions(-) diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 b/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 index 39c21a950f278..a4e0492ce1c61 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.g4 @@ -199,7 +199,6 @@ FROM_COMMA : COMMA -> type(COMMA); FROM_ASSIGN : ASSIGN -> type(ASSIGN); FROM_QUOTED_STRING : QUOTED_STRING -> type(QUOTED_STRING); -OPTIONS : 'options'; METADATA : 'metadata'; fragment FROM_UNQUOTED_IDENTIFIER_PART @@ -432,4 +431,4 @@ SETTTING_MULTILINE_COMMENT SETTING_WS : WS -> channel(HIDDEN) - ; \ No newline at end of file + ; diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.interp b/packages/kbn-esql-ast/src/antlr/esql_lexer.interp index 0fdcdb1939bd1..fad6acad9ab58 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.interp +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.interp @@ -71,7 +71,6 @@ null null null null -'options' 'metadata' null null @@ -184,7 +183,6 @@ QUOTED_IDENTIFIER EXPR_LINE_COMMENT EXPR_MULTILINE_COMMENT EXPR_WS -OPTIONS METADATA FROM_UNQUOTED_IDENTIFIER FROM_LINE_COMMENT @@ -315,7 +313,6 @@ FROM_CLOSING_BRACKET FROM_COMMA FROM_ASSIGN FROM_QUOTED_STRING -OPTIONS METADATA FROM_UNQUOTED_IDENTIFIER_PART FROM_UNQUOTED_IDENTIFIER @@ -404,4 +401,4 @@ META_MODE SETTING_MODE atn: -[4, 0, 110, 1197, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 4, 18, 482, 8, 18, 11, 18, 12, 18, 483, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 492, 8, 19, 10, 19, 12, 19, 495, 9, 19, 1, 19, 3, 19, 498, 8, 19, 1, 19, 3, 19, 501, 8, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 5, 20, 510, 8, 20, 10, 20, 12, 20, 513, 9, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 4, 21, 521, 8, 21, 11, 21, 12, 21, 522, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 3, 32, 564, 8, 32, 1, 32, 4, 32, 567, 8, 32, 11, 32, 12, 32, 568, 1, 33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 3, 35, 578, 8, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 3, 37, 585, 8, 37, 1, 38, 1, 38, 1, 38, 5, 38, 590, 8, 38, 10, 38, 12, 38, 593, 9, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 601, 8, 38, 10, 38, 12, 38, 604, 9, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 611, 8, 38, 1, 38, 3, 38, 614, 8, 38, 3, 38, 616, 8, 38, 1, 39, 4, 39, 619, 8, 39, 11, 39, 12, 39, 620, 1, 40, 4, 40, 624, 8, 40, 11, 40, 12, 40, 625, 1, 40, 1, 40, 5, 40, 630, 8, 40, 10, 40, 12, 40, 633, 9, 40, 1, 40, 1, 40, 4, 40, 637, 8, 40, 11, 40, 12, 40, 638, 1, 40, 4, 40, 642, 8, 40, 11, 40, 12, 40, 643, 1, 40, 1, 40, 5, 40, 648, 8, 40, 10, 40, 12, 40, 651, 9, 40, 3, 40, 653, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 4, 40, 659, 8, 40, 11, 40, 12, 40, 660, 1, 40, 1, 40, 3, 40, 665, 8, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 5, 78, 796, 8, 78, 10, 78, 12, 78, 799, 9, 78, 1, 78, 1, 78, 3, 78, 803, 8, 78, 1, 78, 4, 78, 806, 8, 78, 11, 78, 12, 78, 807, 3, 78, 810, 8, 78, 1, 79, 1, 79, 4, 79, 814, 8, 79, 11, 79, 12, 79, 815, 1, 79, 1, 79, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 3, 92, 879, 8, 92, 1, 93, 4, 93, 882, 8, 93, 11, 93, 12, 93, 883, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 3, 100, 915, 8, 100, 1, 101, 1, 101, 3, 101, 919, 8, 101, 1, 101, 5, 101, 922, 8, 101, 10, 101, 12, 101, 925, 9, 101, 1, 101, 1, 101, 3, 101, 929, 8, 101, 1, 101, 4, 101, 932, 8, 101, 11, 101, 12, 101, 933, 3, 101, 936, 8, 101, 1, 102, 1, 102, 4, 102, 940, 8, 102, 11, 102, 12, 102, 941, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 120, 4, 120, 1017, 8, 120, 11, 120, 12, 120, 1018, 1, 120, 1, 120, 3, 120, 1023, 8, 120, 1, 120, 4, 120, 1026, 8, 120, 11, 120, 12, 120, 1027, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 155, 4, 155, 1182, 8, 155, 11, 155, 12, 155, 1183, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 2, 511, 602, 0, 159, 12, 1, 14, 2, 16, 3, 18, 4, 20, 5, 22, 6, 24, 7, 26, 8, 28, 9, 30, 10, 32, 11, 34, 12, 36, 13, 38, 14, 40, 15, 42, 16, 44, 17, 46, 18, 48, 19, 50, 20, 52, 21, 54, 22, 56, 0, 58, 0, 60, 23, 62, 24, 64, 25, 66, 26, 68, 0, 70, 0, 72, 0, 74, 0, 76, 0, 78, 0, 80, 0, 82, 0, 84, 0, 86, 0, 88, 27, 90, 28, 92, 29, 94, 30, 96, 31, 98, 32, 100, 33, 102, 34, 104, 35, 106, 36, 108, 37, 110, 38, 112, 39, 114, 40, 116, 41, 118, 42, 120, 43, 122, 44, 124, 45, 126, 46, 128, 47, 130, 48, 132, 49, 134, 50, 136, 51, 138, 52, 140, 53, 142, 54, 144, 55, 146, 56, 148, 57, 150, 58, 152, 59, 154, 60, 156, 61, 158, 62, 160, 63, 162, 64, 164, 65, 166, 66, 168, 67, 170, 0, 172, 68, 174, 69, 176, 70, 178, 71, 180, 0, 182, 0, 184, 0, 186, 0, 188, 0, 190, 0, 192, 72, 194, 73, 196, 0, 198, 74, 200, 75, 202, 76, 204, 77, 206, 0, 208, 0, 210, 0, 212, 0, 214, 0, 216, 78, 218, 79, 220, 80, 222, 81, 224, 0, 226, 0, 228, 0, 230, 0, 232, 82, 234, 0, 236, 83, 238, 84, 240, 85, 242, 0, 244, 0, 246, 86, 248, 87, 250, 0, 252, 88, 254, 0, 256, 0, 258, 89, 260, 90, 262, 91, 264, 0, 266, 0, 268, 0, 270, 0, 272, 0, 274, 0, 276, 0, 278, 92, 280, 93, 282, 94, 284, 0, 286, 0, 288, 0, 290, 0, 292, 95, 294, 96, 296, 97, 298, 0, 300, 98, 302, 99, 304, 100, 306, 101, 308, 0, 310, 102, 312, 103, 314, 104, 316, 105, 318, 0, 320, 106, 322, 107, 324, 108, 326, 109, 328, 110, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 35, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 10, 0, 9, 10, 13, 13, 32, 32, 44, 44, 47, 47, 61, 61, 91, 91, 93, 93, 96, 96, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1224, 0, 12, 1, 0, 0, 0, 0, 14, 1, 0, 0, 0, 0, 16, 1, 0, 0, 0, 0, 18, 1, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 24, 1, 0, 0, 0, 0, 26, 1, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 34, 1, 0, 0, 0, 0, 36, 1, 0, 0, 0, 0, 38, 1, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 44, 1, 0, 0, 0, 0, 46, 1, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 50, 1, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 54, 1, 0, 0, 0, 1, 56, 1, 0, 0, 0, 1, 58, 1, 0, 0, 0, 1, 60, 1, 0, 0, 0, 1, 62, 1, 0, 0, 0, 1, 64, 1, 0, 0, 0, 2, 66, 1, 0, 0, 0, 2, 88, 1, 0, 0, 0, 2, 90, 1, 0, 0, 0, 2, 92, 1, 0, 0, 0, 2, 94, 1, 0, 0, 0, 2, 96, 1, 0, 0, 0, 2, 98, 1, 0, 0, 0, 2, 100, 1, 0, 0, 0, 2, 102, 1, 0, 0, 0, 2, 104, 1, 0, 0, 0, 2, 106, 1, 0, 0, 0, 2, 108, 1, 0, 0, 0, 2, 110, 1, 0, 0, 0, 2, 112, 1, 0, 0, 0, 2, 114, 1, 0, 0, 0, 2, 116, 1, 0, 0, 0, 2, 118, 1, 0, 0, 0, 2, 120, 1, 0, 0, 0, 2, 122, 1, 0, 0, 0, 2, 124, 1, 0, 0, 0, 2, 126, 1, 0, 0, 0, 2, 128, 1, 0, 0, 0, 2, 130, 1, 0, 0, 0, 2, 132, 1, 0, 0, 0, 2, 134, 1, 0, 0, 0, 2, 136, 1, 0, 0, 0, 2, 138, 1, 0, 0, 0, 2, 140, 1, 0, 0, 0, 2, 142, 1, 0, 0, 0, 2, 144, 1, 0, 0, 0, 2, 146, 1, 0, 0, 0, 2, 148, 1, 0, 0, 0, 2, 150, 1, 0, 0, 0, 2, 152, 1, 0, 0, 0, 2, 154, 1, 0, 0, 0, 2, 156, 1, 0, 0, 0, 2, 158, 1, 0, 0, 0, 2, 160, 1, 0, 0, 0, 2, 162, 1, 0, 0, 0, 2, 164, 1, 0, 0, 0, 2, 166, 1, 0, 0, 0, 2, 168, 1, 0, 0, 0, 2, 172, 1, 0, 0, 0, 2, 174, 1, 0, 0, 0, 2, 176, 1, 0, 0, 0, 2, 178, 1, 0, 0, 0, 3, 180, 1, 0, 0, 0, 3, 182, 1, 0, 0, 0, 3, 184, 1, 0, 0, 0, 3, 186, 1, 0, 0, 0, 3, 188, 1, 0, 0, 0, 3, 190, 1, 0, 0, 0, 3, 192, 1, 0, 0, 0, 3, 194, 1, 0, 0, 0, 3, 198, 1, 0, 0, 0, 3, 200, 1, 0, 0, 0, 3, 202, 1, 0, 0, 0, 3, 204, 1, 0, 0, 0, 4, 206, 1, 0, 0, 0, 4, 208, 1, 0, 0, 0, 4, 210, 1, 0, 0, 0, 4, 216, 1, 0, 0, 0, 4, 218, 1, 0, 0, 0, 4, 220, 1, 0, 0, 0, 4, 222, 1, 0, 0, 0, 5, 224, 1, 0, 0, 0, 5, 226, 1, 0, 0, 0, 5, 228, 1, 0, 0, 0, 5, 230, 1, 0, 0, 0, 5, 232, 1, 0, 0, 0, 5, 234, 1, 0, 0, 0, 5, 236, 1, 0, 0, 0, 5, 238, 1, 0, 0, 0, 5, 240, 1, 0, 0, 0, 6, 242, 1, 0, 0, 0, 6, 244, 1, 0, 0, 0, 6, 246, 1, 0, 0, 0, 6, 248, 1, 0, 0, 0, 6, 252, 1, 0, 0, 0, 6, 254, 1, 0, 0, 0, 6, 256, 1, 0, 0, 0, 6, 258, 1, 0, 0, 0, 6, 260, 1, 0, 0, 0, 6, 262, 1, 0, 0, 0, 7, 264, 1, 0, 0, 0, 7, 266, 1, 0, 0, 0, 7, 268, 1, 0, 0, 0, 7, 270, 1, 0, 0, 0, 7, 272, 1, 0, 0, 0, 7, 274, 1, 0, 0, 0, 7, 276, 1, 0, 0, 0, 7, 278, 1, 0, 0, 0, 7, 280, 1, 0, 0, 0, 7, 282, 1, 0, 0, 0, 8, 284, 1, 0, 0, 0, 8, 286, 1, 0, 0, 0, 8, 288, 1, 0, 0, 0, 8, 290, 1, 0, 0, 0, 8, 292, 1, 0, 0, 0, 8, 294, 1, 0, 0, 0, 8, 296, 1, 0, 0, 0, 9, 298, 1, 0, 0, 0, 9, 300, 1, 0, 0, 0, 9, 302, 1, 0, 0, 0, 9, 304, 1, 0, 0, 0, 9, 306, 1, 0, 0, 0, 10, 308, 1, 0, 0, 0, 10, 310, 1, 0, 0, 0, 10, 312, 1, 0, 0, 0, 10, 314, 1, 0, 0, 0, 10, 316, 1, 0, 0, 0, 11, 318, 1, 0, 0, 0, 11, 320, 1, 0, 0, 0, 11, 322, 1, 0, 0, 0, 11, 324, 1, 0, 0, 0, 11, 326, 1, 0, 0, 0, 11, 328, 1, 0, 0, 0, 12, 330, 1, 0, 0, 0, 14, 340, 1, 0, 0, 0, 16, 347, 1, 0, 0, 0, 18, 356, 1, 0, 0, 0, 20, 363, 1, 0, 0, 0, 22, 373, 1, 0, 0, 0, 24, 380, 1, 0, 0, 0, 26, 387, 1, 0, 0, 0, 28, 401, 1, 0, 0, 0, 30, 408, 1, 0, 0, 0, 32, 416, 1, 0, 0, 0, 34, 423, 1, 0, 0, 0, 36, 435, 1, 0, 0, 0, 38, 444, 1, 0, 0, 0, 40, 450, 1, 0, 0, 0, 42, 457, 1, 0, 0, 0, 44, 464, 1, 0, 0, 0, 46, 472, 1, 0, 0, 0, 48, 481, 1, 0, 0, 0, 50, 487, 1, 0, 0, 0, 52, 504, 1, 0, 0, 0, 54, 520, 1, 0, 0, 0, 56, 526, 1, 0, 0, 0, 58, 531, 1, 0, 0, 0, 60, 536, 1, 0, 0, 0, 62, 540, 1, 0, 0, 0, 64, 544, 1, 0, 0, 0, 66, 548, 1, 0, 0, 0, 68, 552, 1, 0, 0, 0, 70, 554, 1, 0, 0, 0, 72, 556, 1, 0, 0, 0, 74, 559, 1, 0, 0, 0, 76, 561, 1, 0, 0, 0, 78, 570, 1, 0, 0, 0, 80, 572, 1, 0, 0, 0, 82, 577, 1, 0, 0, 0, 84, 579, 1, 0, 0, 0, 86, 584, 1, 0, 0, 0, 88, 615, 1, 0, 0, 0, 90, 618, 1, 0, 0, 0, 92, 664, 1, 0, 0, 0, 94, 666, 1, 0, 0, 0, 96, 669, 1, 0, 0, 0, 98, 673, 1, 0, 0, 0, 100, 677, 1, 0, 0, 0, 102, 679, 1, 0, 0, 0, 104, 682, 1, 0, 0, 0, 106, 684, 1, 0, 0, 0, 108, 689, 1, 0, 0, 0, 110, 691, 1, 0, 0, 0, 112, 697, 1, 0, 0, 0, 114, 703, 1, 0, 0, 0, 116, 708, 1, 0, 0, 0, 118, 710, 1, 0, 0, 0, 120, 713, 1, 0, 0, 0, 122, 716, 1, 0, 0, 0, 124, 721, 1, 0, 0, 0, 126, 725, 1, 0, 0, 0, 128, 730, 1, 0, 0, 0, 130, 736, 1, 0, 0, 0, 132, 739, 1, 0, 0, 0, 134, 741, 1, 0, 0, 0, 136, 747, 1, 0, 0, 0, 138, 749, 1, 0, 0, 0, 140, 754, 1, 0, 0, 0, 142, 757, 1, 0, 0, 0, 144, 760, 1, 0, 0, 0, 146, 763, 1, 0, 0, 0, 148, 765, 1, 0, 0, 0, 150, 768, 1, 0, 0, 0, 152, 770, 1, 0, 0, 0, 154, 773, 1, 0, 0, 0, 156, 775, 1, 0, 0, 0, 158, 777, 1, 0, 0, 0, 160, 779, 1, 0, 0, 0, 162, 781, 1, 0, 0, 0, 164, 783, 1, 0, 0, 0, 166, 788, 1, 0, 0, 0, 168, 809, 1, 0, 0, 0, 170, 811, 1, 0, 0, 0, 172, 819, 1, 0, 0, 0, 174, 821, 1, 0, 0, 0, 176, 825, 1, 0, 0, 0, 178, 829, 1, 0, 0, 0, 180, 833, 1, 0, 0, 0, 182, 838, 1, 0, 0, 0, 184, 842, 1, 0, 0, 0, 186, 846, 1, 0, 0, 0, 188, 850, 1, 0, 0, 0, 190, 854, 1, 0, 0, 0, 192, 858, 1, 0, 0, 0, 194, 866, 1, 0, 0, 0, 196, 878, 1, 0, 0, 0, 198, 881, 1, 0, 0, 0, 200, 885, 1, 0, 0, 0, 202, 889, 1, 0, 0, 0, 204, 893, 1, 0, 0, 0, 206, 897, 1, 0, 0, 0, 208, 902, 1, 0, 0, 0, 210, 906, 1, 0, 0, 0, 212, 914, 1, 0, 0, 0, 214, 935, 1, 0, 0, 0, 216, 939, 1, 0, 0, 0, 218, 943, 1, 0, 0, 0, 220, 947, 1, 0, 0, 0, 222, 951, 1, 0, 0, 0, 224, 955, 1, 0, 0, 0, 226, 960, 1, 0, 0, 0, 228, 964, 1, 0, 0, 0, 230, 968, 1, 0, 0, 0, 232, 972, 1, 0, 0, 0, 234, 975, 1, 0, 0, 0, 236, 979, 1, 0, 0, 0, 238, 983, 1, 0, 0, 0, 240, 987, 1, 0, 0, 0, 242, 991, 1, 0, 0, 0, 244, 996, 1, 0, 0, 0, 246, 1001, 1, 0, 0, 0, 248, 1006, 1, 0, 0, 0, 250, 1013, 1, 0, 0, 0, 252, 1022, 1, 0, 0, 0, 254, 1029, 1, 0, 0, 0, 256, 1033, 1, 0, 0, 0, 258, 1037, 1, 0, 0, 0, 260, 1041, 1, 0, 0, 0, 262, 1045, 1, 0, 0, 0, 264, 1049, 1, 0, 0, 0, 266, 1055, 1, 0, 0, 0, 268, 1059, 1, 0, 0, 0, 270, 1063, 1, 0, 0, 0, 272, 1067, 1, 0, 0, 0, 274, 1071, 1, 0, 0, 0, 276, 1075, 1, 0, 0, 0, 278, 1079, 1, 0, 0, 0, 280, 1083, 1, 0, 0, 0, 282, 1087, 1, 0, 0, 0, 284, 1091, 1, 0, 0, 0, 286, 1096, 1, 0, 0, 0, 288, 1100, 1, 0, 0, 0, 290, 1104, 1, 0, 0, 0, 292, 1108, 1, 0, 0, 0, 294, 1112, 1, 0, 0, 0, 296, 1116, 1, 0, 0, 0, 298, 1120, 1, 0, 0, 0, 300, 1125, 1, 0, 0, 0, 302, 1130, 1, 0, 0, 0, 304, 1134, 1, 0, 0, 0, 306, 1138, 1, 0, 0, 0, 308, 1142, 1, 0, 0, 0, 310, 1147, 1, 0, 0, 0, 312, 1157, 1, 0, 0, 0, 314, 1161, 1, 0, 0, 0, 316, 1165, 1, 0, 0, 0, 318, 1169, 1, 0, 0, 0, 320, 1174, 1, 0, 0, 0, 322, 1181, 1, 0, 0, 0, 324, 1185, 1, 0, 0, 0, 326, 1189, 1, 0, 0, 0, 328, 1193, 1, 0, 0, 0, 330, 331, 7, 0, 0, 0, 331, 332, 7, 1, 0, 0, 332, 333, 7, 2, 0, 0, 333, 334, 7, 2, 0, 0, 334, 335, 7, 3, 0, 0, 335, 336, 7, 4, 0, 0, 336, 337, 7, 5, 0, 0, 337, 338, 1, 0, 0, 0, 338, 339, 6, 0, 0, 0, 339, 13, 1, 0, 0, 0, 340, 341, 7, 0, 0, 0, 341, 342, 7, 6, 0, 0, 342, 343, 7, 7, 0, 0, 343, 344, 7, 8, 0, 0, 344, 345, 1, 0, 0, 0, 345, 346, 6, 1, 1, 0, 346, 15, 1, 0, 0, 0, 347, 348, 7, 3, 0, 0, 348, 349, 7, 9, 0, 0, 349, 350, 7, 6, 0, 0, 350, 351, 7, 1, 0, 0, 351, 352, 7, 4, 0, 0, 352, 353, 7, 10, 0, 0, 353, 354, 1, 0, 0, 0, 354, 355, 6, 2, 2, 0, 355, 17, 1, 0, 0, 0, 356, 357, 7, 3, 0, 0, 357, 358, 7, 11, 0, 0, 358, 359, 7, 12, 0, 0, 359, 360, 7, 13, 0, 0, 360, 361, 1, 0, 0, 0, 361, 362, 6, 3, 0, 0, 362, 19, 1, 0, 0, 0, 363, 364, 7, 3, 0, 0, 364, 365, 7, 14, 0, 0, 365, 366, 7, 8, 0, 0, 366, 367, 7, 13, 0, 0, 367, 368, 7, 12, 0, 0, 368, 369, 7, 1, 0, 0, 369, 370, 7, 9, 0, 0, 370, 371, 1, 0, 0, 0, 371, 372, 6, 4, 3, 0, 372, 21, 1, 0, 0, 0, 373, 374, 7, 15, 0, 0, 374, 375, 7, 6, 0, 0, 375, 376, 7, 7, 0, 0, 376, 377, 7, 16, 0, 0, 377, 378, 1, 0, 0, 0, 378, 379, 6, 5, 4, 0, 379, 23, 1, 0, 0, 0, 380, 381, 7, 17, 0, 0, 381, 382, 7, 6, 0, 0, 382, 383, 7, 7, 0, 0, 383, 384, 7, 18, 0, 0, 384, 385, 1, 0, 0, 0, 385, 386, 6, 6, 0, 0, 386, 25, 1, 0, 0, 0, 387, 388, 7, 1, 0, 0, 388, 389, 7, 9, 0, 0, 389, 390, 7, 13, 0, 0, 390, 391, 7, 1, 0, 0, 391, 392, 7, 9, 0, 0, 392, 393, 7, 3, 0, 0, 393, 394, 7, 2, 0, 0, 394, 395, 7, 5, 0, 0, 395, 396, 7, 12, 0, 0, 396, 397, 7, 5, 0, 0, 397, 398, 7, 2, 0, 0, 398, 399, 1, 0, 0, 0, 399, 400, 6, 7, 0, 0, 400, 27, 1, 0, 0, 0, 401, 402, 7, 18, 0, 0, 402, 403, 7, 3, 0, 0, 403, 404, 7, 3, 0, 0, 404, 405, 7, 8, 0, 0, 405, 406, 1, 0, 0, 0, 406, 407, 6, 8, 1, 0, 407, 29, 1, 0, 0, 0, 408, 409, 7, 13, 0, 0, 409, 410, 7, 1, 0, 0, 410, 411, 7, 16, 0, 0, 411, 412, 7, 1, 0, 0, 412, 413, 7, 5, 0, 0, 413, 414, 1, 0, 0, 0, 414, 415, 6, 9, 0, 0, 415, 31, 1, 0, 0, 0, 416, 417, 7, 16, 0, 0, 417, 418, 7, 3, 0, 0, 418, 419, 7, 5, 0, 0, 419, 420, 7, 12, 0, 0, 420, 421, 1, 0, 0, 0, 421, 422, 6, 10, 5, 0, 422, 33, 1, 0, 0, 0, 423, 424, 7, 16, 0, 0, 424, 425, 7, 11, 0, 0, 425, 426, 5, 95, 0, 0, 426, 427, 7, 3, 0, 0, 427, 428, 7, 14, 0, 0, 428, 429, 7, 8, 0, 0, 429, 430, 7, 12, 0, 0, 430, 431, 7, 9, 0, 0, 431, 432, 7, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 434, 6, 11, 6, 0, 434, 35, 1, 0, 0, 0, 435, 436, 7, 6, 0, 0, 436, 437, 7, 3, 0, 0, 437, 438, 7, 9, 0, 0, 438, 439, 7, 12, 0, 0, 439, 440, 7, 16, 0, 0, 440, 441, 7, 3, 0, 0, 441, 442, 1, 0, 0, 0, 442, 443, 6, 12, 7, 0, 443, 37, 1, 0, 0, 0, 444, 445, 7, 6, 0, 0, 445, 446, 7, 7, 0, 0, 446, 447, 7, 19, 0, 0, 447, 448, 1, 0, 0, 0, 448, 449, 6, 13, 0, 0, 449, 39, 1, 0, 0, 0, 450, 451, 7, 2, 0, 0, 451, 452, 7, 10, 0, 0, 452, 453, 7, 7, 0, 0, 453, 454, 7, 19, 0, 0, 454, 455, 1, 0, 0, 0, 455, 456, 6, 14, 8, 0, 456, 41, 1, 0, 0, 0, 457, 458, 7, 2, 0, 0, 458, 459, 7, 7, 0, 0, 459, 460, 7, 6, 0, 0, 460, 461, 7, 5, 0, 0, 461, 462, 1, 0, 0, 0, 462, 463, 6, 15, 0, 0, 463, 43, 1, 0, 0, 0, 464, 465, 7, 2, 0, 0, 465, 466, 7, 5, 0, 0, 466, 467, 7, 12, 0, 0, 467, 468, 7, 5, 0, 0, 468, 469, 7, 2, 0, 0, 469, 470, 1, 0, 0, 0, 470, 471, 6, 16, 0, 0, 471, 45, 1, 0, 0, 0, 472, 473, 7, 19, 0, 0, 473, 474, 7, 10, 0, 0, 474, 475, 7, 3, 0, 0, 475, 476, 7, 6, 0, 0, 476, 477, 7, 3, 0, 0, 477, 478, 1, 0, 0, 0, 478, 479, 6, 17, 0, 0, 479, 47, 1, 0, 0, 0, 480, 482, 8, 20, 0, 0, 481, 480, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 481, 1, 0, 0, 0, 483, 484, 1, 0, 0, 0, 484, 485, 1, 0, 0, 0, 485, 486, 6, 18, 0, 0, 486, 49, 1, 0, 0, 0, 487, 488, 5, 47, 0, 0, 488, 489, 5, 47, 0, 0, 489, 493, 1, 0, 0, 0, 490, 492, 8, 21, 0, 0, 491, 490, 1, 0, 0, 0, 492, 495, 1, 0, 0, 0, 493, 491, 1, 0, 0, 0, 493, 494, 1, 0, 0, 0, 494, 497, 1, 0, 0, 0, 495, 493, 1, 0, 0, 0, 496, 498, 5, 13, 0, 0, 497, 496, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 500, 1, 0, 0, 0, 499, 501, 5, 10, 0, 0, 500, 499, 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 502, 1, 0, 0, 0, 502, 503, 6, 19, 9, 0, 503, 51, 1, 0, 0, 0, 504, 505, 5, 47, 0, 0, 505, 506, 5, 42, 0, 0, 506, 511, 1, 0, 0, 0, 507, 510, 3, 52, 20, 0, 508, 510, 9, 0, 0, 0, 509, 507, 1, 0, 0, 0, 509, 508, 1, 0, 0, 0, 510, 513, 1, 0, 0, 0, 511, 512, 1, 0, 0, 0, 511, 509, 1, 0, 0, 0, 512, 514, 1, 0, 0, 0, 513, 511, 1, 0, 0, 0, 514, 515, 5, 42, 0, 0, 515, 516, 5, 47, 0, 0, 516, 517, 1, 0, 0, 0, 517, 518, 6, 20, 9, 0, 518, 53, 1, 0, 0, 0, 519, 521, 7, 22, 0, 0, 520, 519, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 520, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 525, 6, 21, 9, 0, 525, 55, 1, 0, 0, 0, 526, 527, 3, 164, 76, 0, 527, 528, 1, 0, 0, 0, 528, 529, 6, 22, 10, 0, 529, 530, 6, 22, 11, 0, 530, 57, 1, 0, 0, 0, 531, 532, 3, 66, 27, 0, 532, 533, 1, 0, 0, 0, 533, 534, 6, 23, 12, 0, 534, 535, 6, 23, 13, 0, 535, 59, 1, 0, 0, 0, 536, 537, 3, 54, 21, 0, 537, 538, 1, 0, 0, 0, 538, 539, 6, 24, 9, 0, 539, 61, 1, 0, 0, 0, 540, 541, 3, 50, 19, 0, 541, 542, 1, 0, 0, 0, 542, 543, 6, 25, 9, 0, 543, 63, 1, 0, 0, 0, 544, 545, 3, 52, 20, 0, 545, 546, 1, 0, 0, 0, 546, 547, 6, 26, 9, 0, 547, 65, 1, 0, 0, 0, 548, 549, 5, 124, 0, 0, 549, 550, 1, 0, 0, 0, 550, 551, 6, 27, 13, 0, 551, 67, 1, 0, 0, 0, 552, 553, 7, 23, 0, 0, 553, 69, 1, 0, 0, 0, 554, 555, 7, 24, 0, 0, 555, 71, 1, 0, 0, 0, 556, 557, 5, 92, 0, 0, 557, 558, 7, 25, 0, 0, 558, 73, 1, 0, 0, 0, 559, 560, 8, 26, 0, 0, 560, 75, 1, 0, 0, 0, 561, 563, 7, 3, 0, 0, 562, 564, 7, 27, 0, 0, 563, 562, 1, 0, 0, 0, 563, 564, 1, 0, 0, 0, 564, 566, 1, 0, 0, 0, 565, 567, 3, 68, 28, 0, 566, 565, 1, 0, 0, 0, 567, 568, 1, 0, 0, 0, 568, 566, 1, 0, 0, 0, 568, 569, 1, 0, 0, 0, 569, 77, 1, 0, 0, 0, 570, 571, 5, 64, 0, 0, 571, 79, 1, 0, 0, 0, 572, 573, 5, 96, 0, 0, 573, 81, 1, 0, 0, 0, 574, 578, 8, 28, 0, 0, 575, 576, 5, 96, 0, 0, 576, 578, 5, 96, 0, 0, 577, 574, 1, 0, 0, 0, 577, 575, 1, 0, 0, 0, 578, 83, 1, 0, 0, 0, 579, 580, 5, 95, 0, 0, 580, 85, 1, 0, 0, 0, 581, 585, 3, 70, 29, 0, 582, 585, 3, 68, 28, 0, 583, 585, 3, 84, 36, 0, 584, 581, 1, 0, 0, 0, 584, 582, 1, 0, 0, 0, 584, 583, 1, 0, 0, 0, 585, 87, 1, 0, 0, 0, 586, 591, 5, 34, 0, 0, 587, 590, 3, 72, 30, 0, 588, 590, 3, 74, 31, 0, 589, 587, 1, 0, 0, 0, 589, 588, 1, 0, 0, 0, 590, 593, 1, 0, 0, 0, 591, 589, 1, 0, 0, 0, 591, 592, 1, 0, 0, 0, 592, 594, 1, 0, 0, 0, 593, 591, 1, 0, 0, 0, 594, 616, 5, 34, 0, 0, 595, 596, 5, 34, 0, 0, 596, 597, 5, 34, 0, 0, 597, 598, 5, 34, 0, 0, 598, 602, 1, 0, 0, 0, 599, 601, 8, 21, 0, 0, 600, 599, 1, 0, 0, 0, 601, 604, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 602, 600, 1, 0, 0, 0, 603, 605, 1, 0, 0, 0, 604, 602, 1, 0, 0, 0, 605, 606, 5, 34, 0, 0, 606, 607, 5, 34, 0, 0, 607, 608, 5, 34, 0, 0, 608, 610, 1, 0, 0, 0, 609, 611, 5, 34, 0, 0, 610, 609, 1, 0, 0, 0, 610, 611, 1, 0, 0, 0, 611, 613, 1, 0, 0, 0, 612, 614, 5, 34, 0, 0, 613, 612, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 616, 1, 0, 0, 0, 615, 586, 1, 0, 0, 0, 615, 595, 1, 0, 0, 0, 616, 89, 1, 0, 0, 0, 617, 619, 3, 68, 28, 0, 618, 617, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 618, 1, 0, 0, 0, 620, 621, 1, 0, 0, 0, 621, 91, 1, 0, 0, 0, 622, 624, 3, 68, 28, 0, 623, 622, 1, 0, 0, 0, 624, 625, 1, 0, 0, 0, 625, 623, 1, 0, 0, 0, 625, 626, 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 631, 3, 108, 48, 0, 628, 630, 3, 68, 28, 0, 629, 628, 1, 0, 0, 0, 630, 633, 1, 0, 0, 0, 631, 629, 1, 0, 0, 0, 631, 632, 1, 0, 0, 0, 632, 665, 1, 0, 0, 0, 633, 631, 1, 0, 0, 0, 634, 636, 3, 108, 48, 0, 635, 637, 3, 68, 28, 0, 636, 635, 1, 0, 0, 0, 637, 638, 1, 0, 0, 0, 638, 636, 1, 0, 0, 0, 638, 639, 1, 0, 0, 0, 639, 665, 1, 0, 0, 0, 640, 642, 3, 68, 28, 0, 641, 640, 1, 0, 0, 0, 642, 643, 1, 0, 0, 0, 643, 641, 1, 0, 0, 0, 643, 644, 1, 0, 0, 0, 644, 652, 1, 0, 0, 0, 645, 649, 3, 108, 48, 0, 646, 648, 3, 68, 28, 0, 647, 646, 1, 0, 0, 0, 648, 651, 1, 0, 0, 0, 649, 647, 1, 0, 0, 0, 649, 650, 1, 0, 0, 0, 650, 653, 1, 0, 0, 0, 651, 649, 1, 0, 0, 0, 652, 645, 1, 0, 0, 0, 652, 653, 1, 0, 0, 0, 653, 654, 1, 0, 0, 0, 654, 655, 3, 76, 32, 0, 655, 665, 1, 0, 0, 0, 656, 658, 3, 108, 48, 0, 657, 659, 3, 68, 28, 0, 658, 657, 1, 0, 0, 0, 659, 660, 1, 0, 0, 0, 660, 658, 1, 0, 0, 0, 660, 661, 1, 0, 0, 0, 661, 662, 1, 0, 0, 0, 662, 663, 3, 76, 32, 0, 663, 665, 1, 0, 0, 0, 664, 623, 1, 0, 0, 0, 664, 634, 1, 0, 0, 0, 664, 641, 1, 0, 0, 0, 664, 656, 1, 0, 0, 0, 665, 93, 1, 0, 0, 0, 666, 667, 7, 29, 0, 0, 667, 668, 7, 30, 0, 0, 668, 95, 1, 0, 0, 0, 669, 670, 7, 12, 0, 0, 670, 671, 7, 9, 0, 0, 671, 672, 7, 0, 0, 0, 672, 97, 1, 0, 0, 0, 673, 674, 7, 12, 0, 0, 674, 675, 7, 2, 0, 0, 675, 676, 7, 4, 0, 0, 676, 99, 1, 0, 0, 0, 677, 678, 5, 61, 0, 0, 678, 101, 1, 0, 0, 0, 679, 680, 5, 58, 0, 0, 680, 681, 5, 58, 0, 0, 681, 103, 1, 0, 0, 0, 682, 683, 5, 44, 0, 0, 683, 105, 1, 0, 0, 0, 684, 685, 7, 0, 0, 0, 685, 686, 7, 3, 0, 0, 686, 687, 7, 2, 0, 0, 687, 688, 7, 4, 0, 0, 688, 107, 1, 0, 0, 0, 689, 690, 5, 46, 0, 0, 690, 109, 1, 0, 0, 0, 691, 692, 7, 15, 0, 0, 692, 693, 7, 12, 0, 0, 693, 694, 7, 13, 0, 0, 694, 695, 7, 2, 0, 0, 695, 696, 7, 3, 0, 0, 696, 111, 1, 0, 0, 0, 697, 698, 7, 15, 0, 0, 698, 699, 7, 1, 0, 0, 699, 700, 7, 6, 0, 0, 700, 701, 7, 2, 0, 0, 701, 702, 7, 5, 0, 0, 702, 113, 1, 0, 0, 0, 703, 704, 7, 13, 0, 0, 704, 705, 7, 12, 0, 0, 705, 706, 7, 2, 0, 0, 706, 707, 7, 5, 0, 0, 707, 115, 1, 0, 0, 0, 708, 709, 5, 40, 0, 0, 709, 117, 1, 0, 0, 0, 710, 711, 7, 1, 0, 0, 711, 712, 7, 9, 0, 0, 712, 119, 1, 0, 0, 0, 713, 714, 7, 1, 0, 0, 714, 715, 7, 2, 0, 0, 715, 121, 1, 0, 0, 0, 716, 717, 7, 13, 0, 0, 717, 718, 7, 1, 0, 0, 718, 719, 7, 18, 0, 0, 719, 720, 7, 3, 0, 0, 720, 123, 1, 0, 0, 0, 721, 722, 7, 9, 0, 0, 722, 723, 7, 7, 0, 0, 723, 724, 7, 5, 0, 0, 724, 125, 1, 0, 0, 0, 725, 726, 7, 9, 0, 0, 726, 727, 7, 31, 0, 0, 727, 728, 7, 13, 0, 0, 728, 729, 7, 13, 0, 0, 729, 127, 1, 0, 0, 0, 730, 731, 7, 9, 0, 0, 731, 732, 7, 31, 0, 0, 732, 733, 7, 13, 0, 0, 733, 734, 7, 13, 0, 0, 734, 735, 7, 2, 0, 0, 735, 129, 1, 0, 0, 0, 736, 737, 7, 7, 0, 0, 737, 738, 7, 6, 0, 0, 738, 131, 1, 0, 0, 0, 739, 740, 5, 63, 0, 0, 740, 133, 1, 0, 0, 0, 741, 742, 7, 6, 0, 0, 742, 743, 7, 13, 0, 0, 743, 744, 7, 1, 0, 0, 744, 745, 7, 18, 0, 0, 745, 746, 7, 3, 0, 0, 746, 135, 1, 0, 0, 0, 747, 748, 5, 41, 0, 0, 748, 137, 1, 0, 0, 0, 749, 750, 7, 5, 0, 0, 750, 751, 7, 6, 0, 0, 751, 752, 7, 31, 0, 0, 752, 753, 7, 3, 0, 0, 753, 139, 1, 0, 0, 0, 754, 755, 5, 61, 0, 0, 755, 756, 5, 61, 0, 0, 756, 141, 1, 0, 0, 0, 757, 758, 5, 61, 0, 0, 758, 759, 5, 126, 0, 0, 759, 143, 1, 0, 0, 0, 760, 761, 5, 33, 0, 0, 761, 762, 5, 61, 0, 0, 762, 145, 1, 0, 0, 0, 763, 764, 5, 60, 0, 0, 764, 147, 1, 0, 0, 0, 765, 766, 5, 60, 0, 0, 766, 767, 5, 61, 0, 0, 767, 149, 1, 0, 0, 0, 768, 769, 5, 62, 0, 0, 769, 151, 1, 0, 0, 0, 770, 771, 5, 62, 0, 0, 771, 772, 5, 61, 0, 0, 772, 153, 1, 0, 0, 0, 773, 774, 5, 43, 0, 0, 774, 155, 1, 0, 0, 0, 775, 776, 5, 45, 0, 0, 776, 157, 1, 0, 0, 0, 777, 778, 5, 42, 0, 0, 778, 159, 1, 0, 0, 0, 779, 780, 5, 47, 0, 0, 780, 161, 1, 0, 0, 0, 781, 782, 5, 37, 0, 0, 782, 163, 1, 0, 0, 0, 783, 784, 5, 91, 0, 0, 784, 785, 1, 0, 0, 0, 785, 786, 6, 76, 0, 0, 786, 787, 6, 76, 0, 0, 787, 165, 1, 0, 0, 0, 788, 789, 5, 93, 0, 0, 789, 790, 1, 0, 0, 0, 790, 791, 6, 77, 13, 0, 791, 792, 6, 77, 13, 0, 792, 167, 1, 0, 0, 0, 793, 797, 3, 70, 29, 0, 794, 796, 3, 86, 37, 0, 795, 794, 1, 0, 0, 0, 796, 799, 1, 0, 0, 0, 797, 795, 1, 0, 0, 0, 797, 798, 1, 0, 0, 0, 798, 810, 1, 0, 0, 0, 799, 797, 1, 0, 0, 0, 800, 803, 3, 84, 36, 0, 801, 803, 3, 78, 33, 0, 802, 800, 1, 0, 0, 0, 802, 801, 1, 0, 0, 0, 803, 805, 1, 0, 0, 0, 804, 806, 3, 86, 37, 0, 805, 804, 1, 0, 0, 0, 806, 807, 1, 0, 0, 0, 807, 805, 1, 0, 0, 0, 807, 808, 1, 0, 0, 0, 808, 810, 1, 0, 0, 0, 809, 793, 1, 0, 0, 0, 809, 802, 1, 0, 0, 0, 810, 169, 1, 0, 0, 0, 811, 813, 3, 80, 34, 0, 812, 814, 3, 82, 35, 0, 813, 812, 1, 0, 0, 0, 814, 815, 1, 0, 0, 0, 815, 813, 1, 0, 0, 0, 815, 816, 1, 0, 0, 0, 816, 817, 1, 0, 0, 0, 817, 818, 3, 80, 34, 0, 818, 171, 1, 0, 0, 0, 819, 820, 3, 170, 79, 0, 820, 173, 1, 0, 0, 0, 821, 822, 3, 50, 19, 0, 822, 823, 1, 0, 0, 0, 823, 824, 6, 81, 9, 0, 824, 175, 1, 0, 0, 0, 825, 826, 3, 52, 20, 0, 826, 827, 1, 0, 0, 0, 827, 828, 6, 82, 9, 0, 828, 177, 1, 0, 0, 0, 829, 830, 3, 54, 21, 0, 830, 831, 1, 0, 0, 0, 831, 832, 6, 83, 9, 0, 832, 179, 1, 0, 0, 0, 833, 834, 3, 66, 27, 0, 834, 835, 1, 0, 0, 0, 835, 836, 6, 84, 12, 0, 836, 837, 6, 84, 13, 0, 837, 181, 1, 0, 0, 0, 838, 839, 3, 164, 76, 0, 839, 840, 1, 0, 0, 0, 840, 841, 6, 85, 10, 0, 841, 183, 1, 0, 0, 0, 842, 843, 3, 166, 77, 0, 843, 844, 1, 0, 0, 0, 844, 845, 6, 86, 14, 0, 845, 185, 1, 0, 0, 0, 846, 847, 3, 104, 46, 0, 847, 848, 1, 0, 0, 0, 848, 849, 6, 87, 15, 0, 849, 187, 1, 0, 0, 0, 850, 851, 3, 100, 44, 0, 851, 852, 1, 0, 0, 0, 852, 853, 6, 88, 16, 0, 853, 189, 1, 0, 0, 0, 854, 855, 3, 88, 38, 0, 855, 856, 1, 0, 0, 0, 856, 857, 6, 89, 17, 0, 857, 191, 1, 0, 0, 0, 858, 859, 7, 7, 0, 0, 859, 860, 7, 8, 0, 0, 860, 861, 7, 5, 0, 0, 861, 862, 7, 1, 0, 0, 862, 863, 7, 7, 0, 0, 863, 864, 7, 9, 0, 0, 864, 865, 7, 2, 0, 0, 865, 193, 1, 0, 0, 0, 866, 867, 7, 16, 0, 0, 867, 868, 7, 3, 0, 0, 868, 869, 7, 5, 0, 0, 869, 870, 7, 12, 0, 0, 870, 871, 7, 0, 0, 0, 871, 872, 7, 12, 0, 0, 872, 873, 7, 5, 0, 0, 873, 874, 7, 12, 0, 0, 874, 195, 1, 0, 0, 0, 875, 879, 8, 32, 0, 0, 876, 877, 5, 47, 0, 0, 877, 879, 8, 33, 0, 0, 878, 875, 1, 0, 0, 0, 878, 876, 1, 0, 0, 0, 879, 197, 1, 0, 0, 0, 880, 882, 3, 196, 92, 0, 881, 880, 1, 0, 0, 0, 882, 883, 1, 0, 0, 0, 883, 881, 1, 0, 0, 0, 883, 884, 1, 0, 0, 0, 884, 199, 1, 0, 0, 0, 885, 886, 3, 50, 19, 0, 886, 887, 1, 0, 0, 0, 887, 888, 6, 94, 9, 0, 888, 201, 1, 0, 0, 0, 889, 890, 3, 52, 20, 0, 890, 891, 1, 0, 0, 0, 891, 892, 6, 95, 9, 0, 892, 203, 1, 0, 0, 0, 893, 894, 3, 54, 21, 0, 894, 895, 1, 0, 0, 0, 895, 896, 6, 96, 9, 0, 896, 205, 1, 0, 0, 0, 897, 898, 3, 66, 27, 0, 898, 899, 1, 0, 0, 0, 899, 900, 6, 97, 12, 0, 900, 901, 6, 97, 13, 0, 901, 207, 1, 0, 0, 0, 902, 903, 3, 108, 48, 0, 903, 904, 1, 0, 0, 0, 904, 905, 6, 98, 18, 0, 905, 209, 1, 0, 0, 0, 906, 907, 3, 104, 46, 0, 907, 908, 1, 0, 0, 0, 908, 909, 6, 99, 15, 0, 909, 211, 1, 0, 0, 0, 910, 915, 3, 70, 29, 0, 911, 915, 3, 68, 28, 0, 912, 915, 3, 84, 36, 0, 913, 915, 3, 158, 73, 0, 914, 910, 1, 0, 0, 0, 914, 911, 1, 0, 0, 0, 914, 912, 1, 0, 0, 0, 914, 913, 1, 0, 0, 0, 915, 213, 1, 0, 0, 0, 916, 919, 3, 70, 29, 0, 917, 919, 3, 158, 73, 0, 918, 916, 1, 0, 0, 0, 918, 917, 1, 0, 0, 0, 919, 923, 1, 0, 0, 0, 920, 922, 3, 212, 100, 0, 921, 920, 1, 0, 0, 0, 922, 925, 1, 0, 0, 0, 923, 921, 1, 0, 0, 0, 923, 924, 1, 0, 0, 0, 924, 936, 1, 0, 0, 0, 925, 923, 1, 0, 0, 0, 926, 929, 3, 84, 36, 0, 927, 929, 3, 78, 33, 0, 928, 926, 1, 0, 0, 0, 928, 927, 1, 0, 0, 0, 929, 931, 1, 0, 0, 0, 930, 932, 3, 212, 100, 0, 931, 930, 1, 0, 0, 0, 932, 933, 1, 0, 0, 0, 933, 931, 1, 0, 0, 0, 933, 934, 1, 0, 0, 0, 934, 936, 1, 0, 0, 0, 935, 918, 1, 0, 0, 0, 935, 928, 1, 0, 0, 0, 936, 215, 1, 0, 0, 0, 937, 940, 3, 214, 101, 0, 938, 940, 3, 170, 79, 0, 939, 937, 1, 0, 0, 0, 939, 938, 1, 0, 0, 0, 940, 941, 1, 0, 0, 0, 941, 939, 1, 0, 0, 0, 941, 942, 1, 0, 0, 0, 942, 217, 1, 0, 0, 0, 943, 944, 3, 50, 19, 0, 944, 945, 1, 0, 0, 0, 945, 946, 6, 103, 9, 0, 946, 219, 1, 0, 0, 0, 947, 948, 3, 52, 20, 0, 948, 949, 1, 0, 0, 0, 949, 950, 6, 104, 9, 0, 950, 221, 1, 0, 0, 0, 951, 952, 3, 54, 21, 0, 952, 953, 1, 0, 0, 0, 953, 954, 6, 105, 9, 0, 954, 223, 1, 0, 0, 0, 955, 956, 3, 66, 27, 0, 956, 957, 1, 0, 0, 0, 957, 958, 6, 106, 12, 0, 958, 959, 6, 106, 13, 0, 959, 225, 1, 0, 0, 0, 960, 961, 3, 100, 44, 0, 961, 962, 1, 0, 0, 0, 962, 963, 6, 107, 16, 0, 963, 227, 1, 0, 0, 0, 964, 965, 3, 104, 46, 0, 965, 966, 1, 0, 0, 0, 966, 967, 6, 108, 15, 0, 967, 229, 1, 0, 0, 0, 968, 969, 3, 108, 48, 0, 969, 970, 1, 0, 0, 0, 970, 971, 6, 109, 18, 0, 971, 231, 1, 0, 0, 0, 972, 973, 7, 12, 0, 0, 973, 974, 7, 2, 0, 0, 974, 233, 1, 0, 0, 0, 975, 976, 3, 216, 102, 0, 976, 977, 1, 0, 0, 0, 977, 978, 6, 111, 19, 0, 978, 235, 1, 0, 0, 0, 979, 980, 3, 50, 19, 0, 980, 981, 1, 0, 0, 0, 981, 982, 6, 112, 9, 0, 982, 237, 1, 0, 0, 0, 983, 984, 3, 52, 20, 0, 984, 985, 1, 0, 0, 0, 985, 986, 6, 113, 9, 0, 986, 239, 1, 0, 0, 0, 987, 988, 3, 54, 21, 0, 988, 989, 1, 0, 0, 0, 989, 990, 6, 114, 9, 0, 990, 241, 1, 0, 0, 0, 991, 992, 3, 66, 27, 0, 992, 993, 1, 0, 0, 0, 993, 994, 6, 115, 12, 0, 994, 995, 6, 115, 13, 0, 995, 243, 1, 0, 0, 0, 996, 997, 3, 164, 76, 0, 997, 998, 1, 0, 0, 0, 998, 999, 6, 116, 10, 0, 999, 1000, 6, 116, 20, 0, 1000, 245, 1, 0, 0, 0, 1001, 1002, 7, 7, 0, 0, 1002, 1003, 7, 9, 0, 0, 1003, 1004, 1, 0, 0, 0, 1004, 1005, 6, 117, 21, 0, 1005, 247, 1, 0, 0, 0, 1006, 1007, 7, 19, 0, 0, 1007, 1008, 7, 1, 0, 0, 1008, 1009, 7, 5, 0, 0, 1009, 1010, 7, 10, 0, 0, 1010, 1011, 1, 0, 0, 0, 1011, 1012, 6, 118, 21, 0, 1012, 249, 1, 0, 0, 0, 1013, 1014, 8, 34, 0, 0, 1014, 251, 1, 0, 0, 0, 1015, 1017, 3, 250, 119, 0, 1016, 1015, 1, 0, 0, 0, 1017, 1018, 1, 0, 0, 0, 1018, 1016, 1, 0, 0, 0, 1018, 1019, 1, 0, 0, 0, 1019, 1020, 1, 0, 0, 0, 1020, 1021, 3, 320, 154, 0, 1021, 1023, 1, 0, 0, 0, 1022, 1016, 1, 0, 0, 0, 1022, 1023, 1, 0, 0, 0, 1023, 1025, 1, 0, 0, 0, 1024, 1026, 3, 250, 119, 0, 1025, 1024, 1, 0, 0, 0, 1026, 1027, 1, 0, 0, 0, 1027, 1025, 1, 0, 0, 0, 1027, 1028, 1, 0, 0, 0, 1028, 253, 1, 0, 0, 0, 1029, 1030, 3, 172, 80, 0, 1030, 1031, 1, 0, 0, 0, 1031, 1032, 6, 121, 22, 0, 1032, 255, 1, 0, 0, 0, 1033, 1034, 3, 252, 120, 0, 1034, 1035, 1, 0, 0, 0, 1035, 1036, 6, 122, 23, 0, 1036, 257, 1, 0, 0, 0, 1037, 1038, 3, 50, 19, 0, 1038, 1039, 1, 0, 0, 0, 1039, 1040, 6, 123, 9, 0, 1040, 259, 1, 0, 0, 0, 1041, 1042, 3, 52, 20, 0, 1042, 1043, 1, 0, 0, 0, 1043, 1044, 6, 124, 9, 0, 1044, 261, 1, 0, 0, 0, 1045, 1046, 3, 54, 21, 0, 1046, 1047, 1, 0, 0, 0, 1047, 1048, 6, 125, 9, 0, 1048, 263, 1, 0, 0, 0, 1049, 1050, 3, 66, 27, 0, 1050, 1051, 1, 0, 0, 0, 1051, 1052, 6, 126, 12, 0, 1052, 1053, 6, 126, 13, 0, 1053, 1054, 6, 126, 13, 0, 1054, 265, 1, 0, 0, 0, 1055, 1056, 3, 100, 44, 0, 1056, 1057, 1, 0, 0, 0, 1057, 1058, 6, 127, 16, 0, 1058, 267, 1, 0, 0, 0, 1059, 1060, 3, 104, 46, 0, 1060, 1061, 1, 0, 0, 0, 1061, 1062, 6, 128, 15, 0, 1062, 269, 1, 0, 0, 0, 1063, 1064, 3, 108, 48, 0, 1064, 1065, 1, 0, 0, 0, 1065, 1066, 6, 129, 18, 0, 1066, 271, 1, 0, 0, 0, 1067, 1068, 3, 248, 118, 0, 1068, 1069, 1, 0, 0, 0, 1069, 1070, 6, 130, 24, 0, 1070, 273, 1, 0, 0, 0, 1071, 1072, 3, 216, 102, 0, 1072, 1073, 1, 0, 0, 0, 1073, 1074, 6, 131, 19, 0, 1074, 275, 1, 0, 0, 0, 1075, 1076, 3, 172, 80, 0, 1076, 1077, 1, 0, 0, 0, 1077, 1078, 6, 132, 22, 0, 1078, 277, 1, 0, 0, 0, 1079, 1080, 3, 50, 19, 0, 1080, 1081, 1, 0, 0, 0, 1081, 1082, 6, 133, 9, 0, 1082, 279, 1, 0, 0, 0, 1083, 1084, 3, 52, 20, 0, 1084, 1085, 1, 0, 0, 0, 1085, 1086, 6, 134, 9, 0, 1086, 281, 1, 0, 0, 0, 1087, 1088, 3, 54, 21, 0, 1088, 1089, 1, 0, 0, 0, 1089, 1090, 6, 135, 9, 0, 1090, 283, 1, 0, 0, 0, 1091, 1092, 3, 66, 27, 0, 1092, 1093, 1, 0, 0, 0, 1093, 1094, 6, 136, 12, 0, 1094, 1095, 6, 136, 13, 0, 1095, 285, 1, 0, 0, 0, 1096, 1097, 3, 108, 48, 0, 1097, 1098, 1, 0, 0, 0, 1098, 1099, 6, 137, 18, 0, 1099, 287, 1, 0, 0, 0, 1100, 1101, 3, 172, 80, 0, 1101, 1102, 1, 0, 0, 0, 1102, 1103, 6, 138, 22, 0, 1103, 289, 1, 0, 0, 0, 1104, 1105, 3, 168, 78, 0, 1105, 1106, 1, 0, 0, 0, 1106, 1107, 6, 139, 25, 0, 1107, 291, 1, 0, 0, 0, 1108, 1109, 3, 50, 19, 0, 1109, 1110, 1, 0, 0, 0, 1110, 1111, 6, 140, 9, 0, 1111, 293, 1, 0, 0, 0, 1112, 1113, 3, 52, 20, 0, 1113, 1114, 1, 0, 0, 0, 1114, 1115, 6, 141, 9, 0, 1115, 295, 1, 0, 0, 0, 1116, 1117, 3, 54, 21, 0, 1117, 1118, 1, 0, 0, 0, 1118, 1119, 6, 142, 9, 0, 1119, 297, 1, 0, 0, 0, 1120, 1121, 3, 66, 27, 0, 1121, 1122, 1, 0, 0, 0, 1122, 1123, 6, 143, 12, 0, 1123, 1124, 6, 143, 13, 0, 1124, 299, 1, 0, 0, 0, 1125, 1126, 7, 1, 0, 0, 1126, 1127, 7, 9, 0, 0, 1127, 1128, 7, 15, 0, 0, 1128, 1129, 7, 7, 0, 0, 1129, 301, 1, 0, 0, 0, 1130, 1131, 3, 50, 19, 0, 1131, 1132, 1, 0, 0, 0, 1132, 1133, 6, 145, 9, 0, 1133, 303, 1, 0, 0, 0, 1134, 1135, 3, 52, 20, 0, 1135, 1136, 1, 0, 0, 0, 1136, 1137, 6, 146, 9, 0, 1137, 305, 1, 0, 0, 0, 1138, 1139, 3, 54, 21, 0, 1139, 1140, 1, 0, 0, 0, 1140, 1141, 6, 147, 9, 0, 1141, 307, 1, 0, 0, 0, 1142, 1143, 3, 66, 27, 0, 1143, 1144, 1, 0, 0, 0, 1144, 1145, 6, 148, 12, 0, 1145, 1146, 6, 148, 13, 0, 1146, 309, 1, 0, 0, 0, 1147, 1148, 7, 15, 0, 0, 1148, 1149, 7, 31, 0, 0, 1149, 1150, 7, 9, 0, 0, 1150, 1151, 7, 4, 0, 0, 1151, 1152, 7, 5, 0, 0, 1152, 1153, 7, 1, 0, 0, 1153, 1154, 7, 7, 0, 0, 1154, 1155, 7, 9, 0, 0, 1155, 1156, 7, 2, 0, 0, 1156, 311, 1, 0, 0, 0, 1157, 1158, 3, 50, 19, 0, 1158, 1159, 1, 0, 0, 0, 1159, 1160, 6, 150, 9, 0, 1160, 313, 1, 0, 0, 0, 1161, 1162, 3, 52, 20, 0, 1162, 1163, 1, 0, 0, 0, 1163, 1164, 6, 151, 9, 0, 1164, 315, 1, 0, 0, 0, 1165, 1166, 3, 54, 21, 0, 1166, 1167, 1, 0, 0, 0, 1167, 1168, 6, 152, 9, 0, 1168, 317, 1, 0, 0, 0, 1169, 1170, 3, 166, 77, 0, 1170, 1171, 1, 0, 0, 0, 1171, 1172, 6, 153, 14, 0, 1172, 1173, 6, 153, 13, 0, 1173, 319, 1, 0, 0, 0, 1174, 1175, 5, 58, 0, 0, 1175, 321, 1, 0, 0, 0, 1176, 1182, 3, 78, 33, 0, 1177, 1182, 3, 68, 28, 0, 1178, 1182, 3, 108, 48, 0, 1179, 1182, 3, 70, 29, 0, 1180, 1182, 3, 84, 36, 0, 1181, 1176, 1, 0, 0, 0, 1181, 1177, 1, 0, 0, 0, 1181, 1178, 1, 0, 0, 0, 1181, 1179, 1, 0, 0, 0, 1181, 1180, 1, 0, 0, 0, 1182, 1183, 1, 0, 0, 0, 1183, 1181, 1, 0, 0, 0, 1183, 1184, 1, 0, 0, 0, 1184, 323, 1, 0, 0, 0, 1185, 1186, 3, 50, 19, 0, 1186, 1187, 1, 0, 0, 0, 1187, 1188, 6, 156, 9, 0, 1188, 325, 1, 0, 0, 0, 1189, 1190, 3, 52, 20, 0, 1190, 1191, 1, 0, 0, 0, 1191, 1192, 6, 157, 9, 0, 1192, 327, 1, 0, 0, 0, 1193, 1194, 3, 54, 21, 0, 1194, 1195, 1, 0, 0, 0, 1195, 1196, 6, 158, 9, 0, 1196, 329, 1, 0, 0, 0, 58, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 483, 493, 497, 500, 509, 511, 522, 563, 568, 577, 584, 589, 591, 602, 610, 613, 615, 620, 625, 631, 638, 643, 649, 652, 660, 664, 797, 802, 807, 809, 815, 878, 883, 914, 918, 923, 928, 933, 935, 939, 941, 1018, 1022, 1027, 1181, 1183, 26, 5, 2, 0, 5, 4, 0, 5, 6, 0, 5, 1, 0, 5, 3, 0, 5, 10, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 0, 1, 0, 7, 65, 0, 5, 0, 0, 7, 26, 0, 4, 0, 0, 7, 66, 0, 7, 35, 0, 7, 33, 0, 7, 27, 0, 7, 37, 0, 7, 78, 0, 5, 11, 0, 5, 7, 0, 7, 68, 0, 7, 88, 0, 7, 87, 0, 7, 67, 0] \ No newline at end of file +[4, 0, 109, 1187, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 4, 18, 480, 8, 18, 11, 18, 12, 18, 481, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 490, 8, 19, 10, 19, 12, 19, 493, 9, 19, 1, 19, 3, 19, 496, 8, 19, 1, 19, 3, 19, 499, 8, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 5, 20, 508, 8, 20, 10, 20, 12, 20, 511, 9, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 4, 21, 519, 8, 21, 11, 21, 12, 21, 520, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 3, 32, 562, 8, 32, 1, 32, 4, 32, 565, 8, 32, 11, 32, 12, 32, 566, 1, 33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 3, 35, 576, 8, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 3, 37, 583, 8, 37, 1, 38, 1, 38, 1, 38, 5, 38, 588, 8, 38, 10, 38, 12, 38, 591, 9, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 599, 8, 38, 10, 38, 12, 38, 602, 9, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 609, 8, 38, 1, 38, 3, 38, 612, 8, 38, 3, 38, 614, 8, 38, 1, 39, 4, 39, 617, 8, 39, 11, 39, 12, 39, 618, 1, 40, 4, 40, 622, 8, 40, 11, 40, 12, 40, 623, 1, 40, 1, 40, 5, 40, 628, 8, 40, 10, 40, 12, 40, 631, 9, 40, 1, 40, 1, 40, 4, 40, 635, 8, 40, 11, 40, 12, 40, 636, 1, 40, 4, 40, 640, 8, 40, 11, 40, 12, 40, 641, 1, 40, 1, 40, 5, 40, 646, 8, 40, 10, 40, 12, 40, 649, 9, 40, 3, 40, 651, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 4, 40, 657, 8, 40, 11, 40, 12, 40, 658, 1, 40, 1, 40, 3, 40, 663, 8, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 5, 78, 794, 8, 78, 10, 78, 12, 78, 797, 9, 78, 1, 78, 1, 78, 3, 78, 801, 8, 78, 1, 78, 4, 78, 804, 8, 78, 11, 78, 12, 78, 805, 3, 78, 808, 8, 78, 1, 79, 1, 79, 4, 79, 812, 8, 79, 11, 79, 12, 79, 813, 1, 79, 1, 79, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 3, 91, 869, 8, 91, 1, 92, 4, 92, 872, 8, 92, 11, 92, 12, 92, 873, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 3, 99, 905, 8, 99, 1, 100, 1, 100, 3, 100, 909, 8, 100, 1, 100, 5, 100, 912, 8, 100, 10, 100, 12, 100, 915, 9, 100, 1, 100, 1, 100, 3, 100, 919, 8, 100, 1, 100, 4, 100, 922, 8, 100, 11, 100, 12, 100, 923, 3, 100, 926, 8, 100, 1, 101, 1, 101, 4, 101, 930, 8, 101, 11, 101, 12, 101, 931, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 119, 4, 119, 1007, 8, 119, 11, 119, 12, 119, 1008, 1, 119, 1, 119, 3, 119, 1013, 8, 119, 1, 119, 4, 119, 1016, 8, 119, 11, 119, 12, 119, 1017, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 154, 4, 154, 1172, 8, 154, 11, 154, 12, 154, 1173, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 2, 509, 600, 0, 158, 12, 1, 14, 2, 16, 3, 18, 4, 20, 5, 22, 6, 24, 7, 26, 8, 28, 9, 30, 10, 32, 11, 34, 12, 36, 13, 38, 14, 40, 15, 42, 16, 44, 17, 46, 18, 48, 19, 50, 20, 52, 21, 54, 22, 56, 0, 58, 0, 60, 23, 62, 24, 64, 25, 66, 26, 68, 0, 70, 0, 72, 0, 74, 0, 76, 0, 78, 0, 80, 0, 82, 0, 84, 0, 86, 0, 88, 27, 90, 28, 92, 29, 94, 30, 96, 31, 98, 32, 100, 33, 102, 34, 104, 35, 106, 36, 108, 37, 110, 38, 112, 39, 114, 40, 116, 41, 118, 42, 120, 43, 122, 44, 124, 45, 126, 46, 128, 47, 130, 48, 132, 49, 134, 50, 136, 51, 138, 52, 140, 53, 142, 54, 144, 55, 146, 56, 148, 57, 150, 58, 152, 59, 154, 60, 156, 61, 158, 62, 160, 63, 162, 64, 164, 65, 166, 66, 168, 67, 170, 0, 172, 68, 174, 69, 176, 70, 178, 71, 180, 0, 182, 0, 184, 0, 186, 0, 188, 0, 190, 0, 192, 72, 194, 0, 196, 73, 198, 74, 200, 75, 202, 76, 204, 0, 206, 0, 208, 0, 210, 0, 212, 0, 214, 77, 216, 78, 218, 79, 220, 80, 222, 0, 224, 0, 226, 0, 228, 0, 230, 81, 232, 0, 234, 82, 236, 83, 238, 84, 240, 0, 242, 0, 244, 85, 246, 86, 248, 0, 250, 87, 252, 0, 254, 0, 256, 88, 258, 89, 260, 90, 262, 0, 264, 0, 266, 0, 268, 0, 270, 0, 272, 0, 274, 0, 276, 91, 278, 92, 280, 93, 282, 0, 284, 0, 286, 0, 288, 0, 290, 94, 292, 95, 294, 96, 296, 0, 298, 97, 300, 98, 302, 99, 304, 100, 306, 0, 308, 101, 310, 102, 312, 103, 314, 104, 316, 0, 318, 105, 320, 106, 322, 107, 324, 108, 326, 109, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 35, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 10, 0, 9, 10, 13, 13, 32, 32, 44, 44, 47, 47, 61, 61, 91, 91, 93, 93, 96, 96, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1214, 0, 12, 1, 0, 0, 0, 0, 14, 1, 0, 0, 0, 0, 16, 1, 0, 0, 0, 0, 18, 1, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 24, 1, 0, 0, 0, 0, 26, 1, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 34, 1, 0, 0, 0, 0, 36, 1, 0, 0, 0, 0, 38, 1, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 44, 1, 0, 0, 0, 0, 46, 1, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 50, 1, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 54, 1, 0, 0, 0, 1, 56, 1, 0, 0, 0, 1, 58, 1, 0, 0, 0, 1, 60, 1, 0, 0, 0, 1, 62, 1, 0, 0, 0, 1, 64, 1, 0, 0, 0, 2, 66, 1, 0, 0, 0, 2, 88, 1, 0, 0, 0, 2, 90, 1, 0, 0, 0, 2, 92, 1, 0, 0, 0, 2, 94, 1, 0, 0, 0, 2, 96, 1, 0, 0, 0, 2, 98, 1, 0, 0, 0, 2, 100, 1, 0, 0, 0, 2, 102, 1, 0, 0, 0, 2, 104, 1, 0, 0, 0, 2, 106, 1, 0, 0, 0, 2, 108, 1, 0, 0, 0, 2, 110, 1, 0, 0, 0, 2, 112, 1, 0, 0, 0, 2, 114, 1, 0, 0, 0, 2, 116, 1, 0, 0, 0, 2, 118, 1, 0, 0, 0, 2, 120, 1, 0, 0, 0, 2, 122, 1, 0, 0, 0, 2, 124, 1, 0, 0, 0, 2, 126, 1, 0, 0, 0, 2, 128, 1, 0, 0, 0, 2, 130, 1, 0, 0, 0, 2, 132, 1, 0, 0, 0, 2, 134, 1, 0, 0, 0, 2, 136, 1, 0, 0, 0, 2, 138, 1, 0, 0, 0, 2, 140, 1, 0, 0, 0, 2, 142, 1, 0, 0, 0, 2, 144, 1, 0, 0, 0, 2, 146, 1, 0, 0, 0, 2, 148, 1, 0, 0, 0, 2, 150, 1, 0, 0, 0, 2, 152, 1, 0, 0, 0, 2, 154, 1, 0, 0, 0, 2, 156, 1, 0, 0, 0, 2, 158, 1, 0, 0, 0, 2, 160, 1, 0, 0, 0, 2, 162, 1, 0, 0, 0, 2, 164, 1, 0, 0, 0, 2, 166, 1, 0, 0, 0, 2, 168, 1, 0, 0, 0, 2, 172, 1, 0, 0, 0, 2, 174, 1, 0, 0, 0, 2, 176, 1, 0, 0, 0, 2, 178, 1, 0, 0, 0, 3, 180, 1, 0, 0, 0, 3, 182, 1, 0, 0, 0, 3, 184, 1, 0, 0, 0, 3, 186, 1, 0, 0, 0, 3, 188, 1, 0, 0, 0, 3, 190, 1, 0, 0, 0, 3, 192, 1, 0, 0, 0, 3, 196, 1, 0, 0, 0, 3, 198, 1, 0, 0, 0, 3, 200, 1, 0, 0, 0, 3, 202, 1, 0, 0, 0, 4, 204, 1, 0, 0, 0, 4, 206, 1, 0, 0, 0, 4, 208, 1, 0, 0, 0, 4, 214, 1, 0, 0, 0, 4, 216, 1, 0, 0, 0, 4, 218, 1, 0, 0, 0, 4, 220, 1, 0, 0, 0, 5, 222, 1, 0, 0, 0, 5, 224, 1, 0, 0, 0, 5, 226, 1, 0, 0, 0, 5, 228, 1, 0, 0, 0, 5, 230, 1, 0, 0, 0, 5, 232, 1, 0, 0, 0, 5, 234, 1, 0, 0, 0, 5, 236, 1, 0, 0, 0, 5, 238, 1, 0, 0, 0, 6, 240, 1, 0, 0, 0, 6, 242, 1, 0, 0, 0, 6, 244, 1, 0, 0, 0, 6, 246, 1, 0, 0, 0, 6, 250, 1, 0, 0, 0, 6, 252, 1, 0, 0, 0, 6, 254, 1, 0, 0, 0, 6, 256, 1, 0, 0, 0, 6, 258, 1, 0, 0, 0, 6, 260, 1, 0, 0, 0, 7, 262, 1, 0, 0, 0, 7, 264, 1, 0, 0, 0, 7, 266, 1, 0, 0, 0, 7, 268, 1, 0, 0, 0, 7, 270, 1, 0, 0, 0, 7, 272, 1, 0, 0, 0, 7, 274, 1, 0, 0, 0, 7, 276, 1, 0, 0, 0, 7, 278, 1, 0, 0, 0, 7, 280, 1, 0, 0, 0, 8, 282, 1, 0, 0, 0, 8, 284, 1, 0, 0, 0, 8, 286, 1, 0, 0, 0, 8, 288, 1, 0, 0, 0, 8, 290, 1, 0, 0, 0, 8, 292, 1, 0, 0, 0, 8, 294, 1, 0, 0, 0, 9, 296, 1, 0, 0, 0, 9, 298, 1, 0, 0, 0, 9, 300, 1, 0, 0, 0, 9, 302, 1, 0, 0, 0, 9, 304, 1, 0, 0, 0, 10, 306, 1, 0, 0, 0, 10, 308, 1, 0, 0, 0, 10, 310, 1, 0, 0, 0, 10, 312, 1, 0, 0, 0, 10, 314, 1, 0, 0, 0, 11, 316, 1, 0, 0, 0, 11, 318, 1, 0, 0, 0, 11, 320, 1, 0, 0, 0, 11, 322, 1, 0, 0, 0, 11, 324, 1, 0, 0, 0, 11, 326, 1, 0, 0, 0, 12, 328, 1, 0, 0, 0, 14, 338, 1, 0, 0, 0, 16, 345, 1, 0, 0, 0, 18, 354, 1, 0, 0, 0, 20, 361, 1, 0, 0, 0, 22, 371, 1, 0, 0, 0, 24, 378, 1, 0, 0, 0, 26, 385, 1, 0, 0, 0, 28, 399, 1, 0, 0, 0, 30, 406, 1, 0, 0, 0, 32, 414, 1, 0, 0, 0, 34, 421, 1, 0, 0, 0, 36, 433, 1, 0, 0, 0, 38, 442, 1, 0, 0, 0, 40, 448, 1, 0, 0, 0, 42, 455, 1, 0, 0, 0, 44, 462, 1, 0, 0, 0, 46, 470, 1, 0, 0, 0, 48, 479, 1, 0, 0, 0, 50, 485, 1, 0, 0, 0, 52, 502, 1, 0, 0, 0, 54, 518, 1, 0, 0, 0, 56, 524, 1, 0, 0, 0, 58, 529, 1, 0, 0, 0, 60, 534, 1, 0, 0, 0, 62, 538, 1, 0, 0, 0, 64, 542, 1, 0, 0, 0, 66, 546, 1, 0, 0, 0, 68, 550, 1, 0, 0, 0, 70, 552, 1, 0, 0, 0, 72, 554, 1, 0, 0, 0, 74, 557, 1, 0, 0, 0, 76, 559, 1, 0, 0, 0, 78, 568, 1, 0, 0, 0, 80, 570, 1, 0, 0, 0, 82, 575, 1, 0, 0, 0, 84, 577, 1, 0, 0, 0, 86, 582, 1, 0, 0, 0, 88, 613, 1, 0, 0, 0, 90, 616, 1, 0, 0, 0, 92, 662, 1, 0, 0, 0, 94, 664, 1, 0, 0, 0, 96, 667, 1, 0, 0, 0, 98, 671, 1, 0, 0, 0, 100, 675, 1, 0, 0, 0, 102, 677, 1, 0, 0, 0, 104, 680, 1, 0, 0, 0, 106, 682, 1, 0, 0, 0, 108, 687, 1, 0, 0, 0, 110, 689, 1, 0, 0, 0, 112, 695, 1, 0, 0, 0, 114, 701, 1, 0, 0, 0, 116, 706, 1, 0, 0, 0, 118, 708, 1, 0, 0, 0, 120, 711, 1, 0, 0, 0, 122, 714, 1, 0, 0, 0, 124, 719, 1, 0, 0, 0, 126, 723, 1, 0, 0, 0, 128, 728, 1, 0, 0, 0, 130, 734, 1, 0, 0, 0, 132, 737, 1, 0, 0, 0, 134, 739, 1, 0, 0, 0, 136, 745, 1, 0, 0, 0, 138, 747, 1, 0, 0, 0, 140, 752, 1, 0, 0, 0, 142, 755, 1, 0, 0, 0, 144, 758, 1, 0, 0, 0, 146, 761, 1, 0, 0, 0, 148, 763, 1, 0, 0, 0, 150, 766, 1, 0, 0, 0, 152, 768, 1, 0, 0, 0, 154, 771, 1, 0, 0, 0, 156, 773, 1, 0, 0, 0, 158, 775, 1, 0, 0, 0, 160, 777, 1, 0, 0, 0, 162, 779, 1, 0, 0, 0, 164, 781, 1, 0, 0, 0, 166, 786, 1, 0, 0, 0, 168, 807, 1, 0, 0, 0, 170, 809, 1, 0, 0, 0, 172, 817, 1, 0, 0, 0, 174, 819, 1, 0, 0, 0, 176, 823, 1, 0, 0, 0, 178, 827, 1, 0, 0, 0, 180, 831, 1, 0, 0, 0, 182, 836, 1, 0, 0, 0, 184, 840, 1, 0, 0, 0, 186, 844, 1, 0, 0, 0, 188, 848, 1, 0, 0, 0, 190, 852, 1, 0, 0, 0, 192, 856, 1, 0, 0, 0, 194, 868, 1, 0, 0, 0, 196, 871, 1, 0, 0, 0, 198, 875, 1, 0, 0, 0, 200, 879, 1, 0, 0, 0, 202, 883, 1, 0, 0, 0, 204, 887, 1, 0, 0, 0, 206, 892, 1, 0, 0, 0, 208, 896, 1, 0, 0, 0, 210, 904, 1, 0, 0, 0, 212, 925, 1, 0, 0, 0, 214, 929, 1, 0, 0, 0, 216, 933, 1, 0, 0, 0, 218, 937, 1, 0, 0, 0, 220, 941, 1, 0, 0, 0, 222, 945, 1, 0, 0, 0, 224, 950, 1, 0, 0, 0, 226, 954, 1, 0, 0, 0, 228, 958, 1, 0, 0, 0, 230, 962, 1, 0, 0, 0, 232, 965, 1, 0, 0, 0, 234, 969, 1, 0, 0, 0, 236, 973, 1, 0, 0, 0, 238, 977, 1, 0, 0, 0, 240, 981, 1, 0, 0, 0, 242, 986, 1, 0, 0, 0, 244, 991, 1, 0, 0, 0, 246, 996, 1, 0, 0, 0, 248, 1003, 1, 0, 0, 0, 250, 1012, 1, 0, 0, 0, 252, 1019, 1, 0, 0, 0, 254, 1023, 1, 0, 0, 0, 256, 1027, 1, 0, 0, 0, 258, 1031, 1, 0, 0, 0, 260, 1035, 1, 0, 0, 0, 262, 1039, 1, 0, 0, 0, 264, 1045, 1, 0, 0, 0, 266, 1049, 1, 0, 0, 0, 268, 1053, 1, 0, 0, 0, 270, 1057, 1, 0, 0, 0, 272, 1061, 1, 0, 0, 0, 274, 1065, 1, 0, 0, 0, 276, 1069, 1, 0, 0, 0, 278, 1073, 1, 0, 0, 0, 280, 1077, 1, 0, 0, 0, 282, 1081, 1, 0, 0, 0, 284, 1086, 1, 0, 0, 0, 286, 1090, 1, 0, 0, 0, 288, 1094, 1, 0, 0, 0, 290, 1098, 1, 0, 0, 0, 292, 1102, 1, 0, 0, 0, 294, 1106, 1, 0, 0, 0, 296, 1110, 1, 0, 0, 0, 298, 1115, 1, 0, 0, 0, 300, 1120, 1, 0, 0, 0, 302, 1124, 1, 0, 0, 0, 304, 1128, 1, 0, 0, 0, 306, 1132, 1, 0, 0, 0, 308, 1137, 1, 0, 0, 0, 310, 1147, 1, 0, 0, 0, 312, 1151, 1, 0, 0, 0, 314, 1155, 1, 0, 0, 0, 316, 1159, 1, 0, 0, 0, 318, 1164, 1, 0, 0, 0, 320, 1171, 1, 0, 0, 0, 322, 1175, 1, 0, 0, 0, 324, 1179, 1, 0, 0, 0, 326, 1183, 1, 0, 0, 0, 328, 329, 7, 0, 0, 0, 329, 330, 7, 1, 0, 0, 330, 331, 7, 2, 0, 0, 331, 332, 7, 2, 0, 0, 332, 333, 7, 3, 0, 0, 333, 334, 7, 4, 0, 0, 334, 335, 7, 5, 0, 0, 335, 336, 1, 0, 0, 0, 336, 337, 6, 0, 0, 0, 337, 13, 1, 0, 0, 0, 338, 339, 7, 0, 0, 0, 339, 340, 7, 6, 0, 0, 340, 341, 7, 7, 0, 0, 341, 342, 7, 8, 0, 0, 342, 343, 1, 0, 0, 0, 343, 344, 6, 1, 1, 0, 344, 15, 1, 0, 0, 0, 345, 346, 7, 3, 0, 0, 346, 347, 7, 9, 0, 0, 347, 348, 7, 6, 0, 0, 348, 349, 7, 1, 0, 0, 349, 350, 7, 4, 0, 0, 350, 351, 7, 10, 0, 0, 351, 352, 1, 0, 0, 0, 352, 353, 6, 2, 2, 0, 353, 17, 1, 0, 0, 0, 354, 355, 7, 3, 0, 0, 355, 356, 7, 11, 0, 0, 356, 357, 7, 12, 0, 0, 357, 358, 7, 13, 0, 0, 358, 359, 1, 0, 0, 0, 359, 360, 6, 3, 0, 0, 360, 19, 1, 0, 0, 0, 361, 362, 7, 3, 0, 0, 362, 363, 7, 14, 0, 0, 363, 364, 7, 8, 0, 0, 364, 365, 7, 13, 0, 0, 365, 366, 7, 12, 0, 0, 366, 367, 7, 1, 0, 0, 367, 368, 7, 9, 0, 0, 368, 369, 1, 0, 0, 0, 369, 370, 6, 4, 3, 0, 370, 21, 1, 0, 0, 0, 371, 372, 7, 15, 0, 0, 372, 373, 7, 6, 0, 0, 373, 374, 7, 7, 0, 0, 374, 375, 7, 16, 0, 0, 375, 376, 1, 0, 0, 0, 376, 377, 6, 5, 4, 0, 377, 23, 1, 0, 0, 0, 378, 379, 7, 17, 0, 0, 379, 380, 7, 6, 0, 0, 380, 381, 7, 7, 0, 0, 381, 382, 7, 18, 0, 0, 382, 383, 1, 0, 0, 0, 383, 384, 6, 6, 0, 0, 384, 25, 1, 0, 0, 0, 385, 386, 7, 1, 0, 0, 386, 387, 7, 9, 0, 0, 387, 388, 7, 13, 0, 0, 388, 389, 7, 1, 0, 0, 389, 390, 7, 9, 0, 0, 390, 391, 7, 3, 0, 0, 391, 392, 7, 2, 0, 0, 392, 393, 7, 5, 0, 0, 393, 394, 7, 12, 0, 0, 394, 395, 7, 5, 0, 0, 395, 396, 7, 2, 0, 0, 396, 397, 1, 0, 0, 0, 397, 398, 6, 7, 0, 0, 398, 27, 1, 0, 0, 0, 399, 400, 7, 18, 0, 0, 400, 401, 7, 3, 0, 0, 401, 402, 7, 3, 0, 0, 402, 403, 7, 8, 0, 0, 403, 404, 1, 0, 0, 0, 404, 405, 6, 8, 1, 0, 405, 29, 1, 0, 0, 0, 406, 407, 7, 13, 0, 0, 407, 408, 7, 1, 0, 0, 408, 409, 7, 16, 0, 0, 409, 410, 7, 1, 0, 0, 410, 411, 7, 5, 0, 0, 411, 412, 1, 0, 0, 0, 412, 413, 6, 9, 0, 0, 413, 31, 1, 0, 0, 0, 414, 415, 7, 16, 0, 0, 415, 416, 7, 3, 0, 0, 416, 417, 7, 5, 0, 0, 417, 418, 7, 12, 0, 0, 418, 419, 1, 0, 0, 0, 419, 420, 6, 10, 5, 0, 420, 33, 1, 0, 0, 0, 421, 422, 7, 16, 0, 0, 422, 423, 7, 11, 0, 0, 423, 424, 5, 95, 0, 0, 424, 425, 7, 3, 0, 0, 425, 426, 7, 14, 0, 0, 426, 427, 7, 8, 0, 0, 427, 428, 7, 12, 0, 0, 428, 429, 7, 9, 0, 0, 429, 430, 7, 0, 0, 0, 430, 431, 1, 0, 0, 0, 431, 432, 6, 11, 6, 0, 432, 35, 1, 0, 0, 0, 433, 434, 7, 6, 0, 0, 434, 435, 7, 3, 0, 0, 435, 436, 7, 9, 0, 0, 436, 437, 7, 12, 0, 0, 437, 438, 7, 16, 0, 0, 438, 439, 7, 3, 0, 0, 439, 440, 1, 0, 0, 0, 440, 441, 6, 12, 7, 0, 441, 37, 1, 0, 0, 0, 442, 443, 7, 6, 0, 0, 443, 444, 7, 7, 0, 0, 444, 445, 7, 19, 0, 0, 445, 446, 1, 0, 0, 0, 446, 447, 6, 13, 0, 0, 447, 39, 1, 0, 0, 0, 448, 449, 7, 2, 0, 0, 449, 450, 7, 10, 0, 0, 450, 451, 7, 7, 0, 0, 451, 452, 7, 19, 0, 0, 452, 453, 1, 0, 0, 0, 453, 454, 6, 14, 8, 0, 454, 41, 1, 0, 0, 0, 455, 456, 7, 2, 0, 0, 456, 457, 7, 7, 0, 0, 457, 458, 7, 6, 0, 0, 458, 459, 7, 5, 0, 0, 459, 460, 1, 0, 0, 0, 460, 461, 6, 15, 0, 0, 461, 43, 1, 0, 0, 0, 462, 463, 7, 2, 0, 0, 463, 464, 7, 5, 0, 0, 464, 465, 7, 12, 0, 0, 465, 466, 7, 5, 0, 0, 466, 467, 7, 2, 0, 0, 467, 468, 1, 0, 0, 0, 468, 469, 6, 16, 0, 0, 469, 45, 1, 0, 0, 0, 470, 471, 7, 19, 0, 0, 471, 472, 7, 10, 0, 0, 472, 473, 7, 3, 0, 0, 473, 474, 7, 6, 0, 0, 474, 475, 7, 3, 0, 0, 475, 476, 1, 0, 0, 0, 476, 477, 6, 17, 0, 0, 477, 47, 1, 0, 0, 0, 478, 480, 8, 20, 0, 0, 479, 478, 1, 0, 0, 0, 480, 481, 1, 0, 0, 0, 481, 479, 1, 0, 0, 0, 481, 482, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 484, 6, 18, 0, 0, 484, 49, 1, 0, 0, 0, 485, 486, 5, 47, 0, 0, 486, 487, 5, 47, 0, 0, 487, 491, 1, 0, 0, 0, 488, 490, 8, 21, 0, 0, 489, 488, 1, 0, 0, 0, 490, 493, 1, 0, 0, 0, 491, 489, 1, 0, 0, 0, 491, 492, 1, 0, 0, 0, 492, 495, 1, 0, 0, 0, 493, 491, 1, 0, 0, 0, 494, 496, 5, 13, 0, 0, 495, 494, 1, 0, 0, 0, 495, 496, 1, 0, 0, 0, 496, 498, 1, 0, 0, 0, 497, 499, 5, 10, 0, 0, 498, 497, 1, 0, 0, 0, 498, 499, 1, 0, 0, 0, 499, 500, 1, 0, 0, 0, 500, 501, 6, 19, 9, 0, 501, 51, 1, 0, 0, 0, 502, 503, 5, 47, 0, 0, 503, 504, 5, 42, 0, 0, 504, 509, 1, 0, 0, 0, 505, 508, 3, 52, 20, 0, 506, 508, 9, 0, 0, 0, 507, 505, 1, 0, 0, 0, 507, 506, 1, 0, 0, 0, 508, 511, 1, 0, 0, 0, 509, 510, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 512, 1, 0, 0, 0, 511, 509, 1, 0, 0, 0, 512, 513, 5, 42, 0, 0, 513, 514, 5, 47, 0, 0, 514, 515, 1, 0, 0, 0, 515, 516, 6, 20, 9, 0, 516, 53, 1, 0, 0, 0, 517, 519, 7, 22, 0, 0, 518, 517, 1, 0, 0, 0, 519, 520, 1, 0, 0, 0, 520, 518, 1, 0, 0, 0, 520, 521, 1, 0, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 6, 21, 9, 0, 523, 55, 1, 0, 0, 0, 524, 525, 3, 164, 76, 0, 525, 526, 1, 0, 0, 0, 526, 527, 6, 22, 10, 0, 527, 528, 6, 22, 11, 0, 528, 57, 1, 0, 0, 0, 529, 530, 3, 66, 27, 0, 530, 531, 1, 0, 0, 0, 531, 532, 6, 23, 12, 0, 532, 533, 6, 23, 13, 0, 533, 59, 1, 0, 0, 0, 534, 535, 3, 54, 21, 0, 535, 536, 1, 0, 0, 0, 536, 537, 6, 24, 9, 0, 537, 61, 1, 0, 0, 0, 538, 539, 3, 50, 19, 0, 539, 540, 1, 0, 0, 0, 540, 541, 6, 25, 9, 0, 541, 63, 1, 0, 0, 0, 542, 543, 3, 52, 20, 0, 543, 544, 1, 0, 0, 0, 544, 545, 6, 26, 9, 0, 545, 65, 1, 0, 0, 0, 546, 547, 5, 124, 0, 0, 547, 548, 1, 0, 0, 0, 548, 549, 6, 27, 13, 0, 549, 67, 1, 0, 0, 0, 550, 551, 7, 23, 0, 0, 551, 69, 1, 0, 0, 0, 552, 553, 7, 24, 0, 0, 553, 71, 1, 0, 0, 0, 554, 555, 5, 92, 0, 0, 555, 556, 7, 25, 0, 0, 556, 73, 1, 0, 0, 0, 557, 558, 8, 26, 0, 0, 558, 75, 1, 0, 0, 0, 559, 561, 7, 3, 0, 0, 560, 562, 7, 27, 0, 0, 561, 560, 1, 0, 0, 0, 561, 562, 1, 0, 0, 0, 562, 564, 1, 0, 0, 0, 563, 565, 3, 68, 28, 0, 564, 563, 1, 0, 0, 0, 565, 566, 1, 0, 0, 0, 566, 564, 1, 0, 0, 0, 566, 567, 1, 0, 0, 0, 567, 77, 1, 0, 0, 0, 568, 569, 5, 64, 0, 0, 569, 79, 1, 0, 0, 0, 570, 571, 5, 96, 0, 0, 571, 81, 1, 0, 0, 0, 572, 576, 8, 28, 0, 0, 573, 574, 5, 96, 0, 0, 574, 576, 5, 96, 0, 0, 575, 572, 1, 0, 0, 0, 575, 573, 1, 0, 0, 0, 576, 83, 1, 0, 0, 0, 577, 578, 5, 95, 0, 0, 578, 85, 1, 0, 0, 0, 579, 583, 3, 70, 29, 0, 580, 583, 3, 68, 28, 0, 581, 583, 3, 84, 36, 0, 582, 579, 1, 0, 0, 0, 582, 580, 1, 0, 0, 0, 582, 581, 1, 0, 0, 0, 583, 87, 1, 0, 0, 0, 584, 589, 5, 34, 0, 0, 585, 588, 3, 72, 30, 0, 586, 588, 3, 74, 31, 0, 587, 585, 1, 0, 0, 0, 587, 586, 1, 0, 0, 0, 588, 591, 1, 0, 0, 0, 589, 587, 1, 0, 0, 0, 589, 590, 1, 0, 0, 0, 590, 592, 1, 0, 0, 0, 591, 589, 1, 0, 0, 0, 592, 614, 5, 34, 0, 0, 593, 594, 5, 34, 0, 0, 594, 595, 5, 34, 0, 0, 595, 596, 5, 34, 0, 0, 596, 600, 1, 0, 0, 0, 597, 599, 8, 21, 0, 0, 598, 597, 1, 0, 0, 0, 599, 602, 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, 600, 598, 1, 0, 0, 0, 601, 603, 1, 0, 0, 0, 602, 600, 1, 0, 0, 0, 603, 604, 5, 34, 0, 0, 604, 605, 5, 34, 0, 0, 605, 606, 5, 34, 0, 0, 606, 608, 1, 0, 0, 0, 607, 609, 5, 34, 0, 0, 608, 607, 1, 0, 0, 0, 608, 609, 1, 0, 0, 0, 609, 611, 1, 0, 0, 0, 610, 612, 5, 34, 0, 0, 611, 610, 1, 0, 0, 0, 611, 612, 1, 0, 0, 0, 612, 614, 1, 0, 0, 0, 613, 584, 1, 0, 0, 0, 613, 593, 1, 0, 0, 0, 614, 89, 1, 0, 0, 0, 615, 617, 3, 68, 28, 0, 616, 615, 1, 0, 0, 0, 617, 618, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 91, 1, 0, 0, 0, 620, 622, 3, 68, 28, 0, 621, 620, 1, 0, 0, 0, 622, 623, 1, 0, 0, 0, 623, 621, 1, 0, 0, 0, 623, 624, 1, 0, 0, 0, 624, 625, 1, 0, 0, 0, 625, 629, 3, 108, 48, 0, 626, 628, 3, 68, 28, 0, 627, 626, 1, 0, 0, 0, 628, 631, 1, 0, 0, 0, 629, 627, 1, 0, 0, 0, 629, 630, 1, 0, 0, 0, 630, 663, 1, 0, 0, 0, 631, 629, 1, 0, 0, 0, 632, 634, 3, 108, 48, 0, 633, 635, 3, 68, 28, 0, 634, 633, 1, 0, 0, 0, 635, 636, 1, 0, 0, 0, 636, 634, 1, 0, 0, 0, 636, 637, 1, 0, 0, 0, 637, 663, 1, 0, 0, 0, 638, 640, 3, 68, 28, 0, 639, 638, 1, 0, 0, 0, 640, 641, 1, 0, 0, 0, 641, 639, 1, 0, 0, 0, 641, 642, 1, 0, 0, 0, 642, 650, 1, 0, 0, 0, 643, 647, 3, 108, 48, 0, 644, 646, 3, 68, 28, 0, 645, 644, 1, 0, 0, 0, 646, 649, 1, 0, 0, 0, 647, 645, 1, 0, 0, 0, 647, 648, 1, 0, 0, 0, 648, 651, 1, 0, 0, 0, 649, 647, 1, 0, 0, 0, 650, 643, 1, 0, 0, 0, 650, 651, 1, 0, 0, 0, 651, 652, 1, 0, 0, 0, 652, 653, 3, 76, 32, 0, 653, 663, 1, 0, 0, 0, 654, 656, 3, 108, 48, 0, 655, 657, 3, 68, 28, 0, 656, 655, 1, 0, 0, 0, 657, 658, 1, 0, 0, 0, 658, 656, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 660, 1, 0, 0, 0, 660, 661, 3, 76, 32, 0, 661, 663, 1, 0, 0, 0, 662, 621, 1, 0, 0, 0, 662, 632, 1, 0, 0, 0, 662, 639, 1, 0, 0, 0, 662, 654, 1, 0, 0, 0, 663, 93, 1, 0, 0, 0, 664, 665, 7, 29, 0, 0, 665, 666, 7, 30, 0, 0, 666, 95, 1, 0, 0, 0, 667, 668, 7, 12, 0, 0, 668, 669, 7, 9, 0, 0, 669, 670, 7, 0, 0, 0, 670, 97, 1, 0, 0, 0, 671, 672, 7, 12, 0, 0, 672, 673, 7, 2, 0, 0, 673, 674, 7, 4, 0, 0, 674, 99, 1, 0, 0, 0, 675, 676, 5, 61, 0, 0, 676, 101, 1, 0, 0, 0, 677, 678, 5, 58, 0, 0, 678, 679, 5, 58, 0, 0, 679, 103, 1, 0, 0, 0, 680, 681, 5, 44, 0, 0, 681, 105, 1, 0, 0, 0, 682, 683, 7, 0, 0, 0, 683, 684, 7, 3, 0, 0, 684, 685, 7, 2, 0, 0, 685, 686, 7, 4, 0, 0, 686, 107, 1, 0, 0, 0, 687, 688, 5, 46, 0, 0, 688, 109, 1, 0, 0, 0, 689, 690, 7, 15, 0, 0, 690, 691, 7, 12, 0, 0, 691, 692, 7, 13, 0, 0, 692, 693, 7, 2, 0, 0, 693, 694, 7, 3, 0, 0, 694, 111, 1, 0, 0, 0, 695, 696, 7, 15, 0, 0, 696, 697, 7, 1, 0, 0, 697, 698, 7, 6, 0, 0, 698, 699, 7, 2, 0, 0, 699, 700, 7, 5, 0, 0, 700, 113, 1, 0, 0, 0, 701, 702, 7, 13, 0, 0, 702, 703, 7, 12, 0, 0, 703, 704, 7, 2, 0, 0, 704, 705, 7, 5, 0, 0, 705, 115, 1, 0, 0, 0, 706, 707, 5, 40, 0, 0, 707, 117, 1, 0, 0, 0, 708, 709, 7, 1, 0, 0, 709, 710, 7, 9, 0, 0, 710, 119, 1, 0, 0, 0, 711, 712, 7, 1, 0, 0, 712, 713, 7, 2, 0, 0, 713, 121, 1, 0, 0, 0, 714, 715, 7, 13, 0, 0, 715, 716, 7, 1, 0, 0, 716, 717, 7, 18, 0, 0, 717, 718, 7, 3, 0, 0, 718, 123, 1, 0, 0, 0, 719, 720, 7, 9, 0, 0, 720, 721, 7, 7, 0, 0, 721, 722, 7, 5, 0, 0, 722, 125, 1, 0, 0, 0, 723, 724, 7, 9, 0, 0, 724, 725, 7, 31, 0, 0, 725, 726, 7, 13, 0, 0, 726, 727, 7, 13, 0, 0, 727, 127, 1, 0, 0, 0, 728, 729, 7, 9, 0, 0, 729, 730, 7, 31, 0, 0, 730, 731, 7, 13, 0, 0, 731, 732, 7, 13, 0, 0, 732, 733, 7, 2, 0, 0, 733, 129, 1, 0, 0, 0, 734, 735, 7, 7, 0, 0, 735, 736, 7, 6, 0, 0, 736, 131, 1, 0, 0, 0, 737, 738, 5, 63, 0, 0, 738, 133, 1, 0, 0, 0, 739, 740, 7, 6, 0, 0, 740, 741, 7, 13, 0, 0, 741, 742, 7, 1, 0, 0, 742, 743, 7, 18, 0, 0, 743, 744, 7, 3, 0, 0, 744, 135, 1, 0, 0, 0, 745, 746, 5, 41, 0, 0, 746, 137, 1, 0, 0, 0, 747, 748, 7, 5, 0, 0, 748, 749, 7, 6, 0, 0, 749, 750, 7, 31, 0, 0, 750, 751, 7, 3, 0, 0, 751, 139, 1, 0, 0, 0, 752, 753, 5, 61, 0, 0, 753, 754, 5, 61, 0, 0, 754, 141, 1, 0, 0, 0, 755, 756, 5, 61, 0, 0, 756, 757, 5, 126, 0, 0, 757, 143, 1, 0, 0, 0, 758, 759, 5, 33, 0, 0, 759, 760, 5, 61, 0, 0, 760, 145, 1, 0, 0, 0, 761, 762, 5, 60, 0, 0, 762, 147, 1, 0, 0, 0, 763, 764, 5, 60, 0, 0, 764, 765, 5, 61, 0, 0, 765, 149, 1, 0, 0, 0, 766, 767, 5, 62, 0, 0, 767, 151, 1, 0, 0, 0, 768, 769, 5, 62, 0, 0, 769, 770, 5, 61, 0, 0, 770, 153, 1, 0, 0, 0, 771, 772, 5, 43, 0, 0, 772, 155, 1, 0, 0, 0, 773, 774, 5, 45, 0, 0, 774, 157, 1, 0, 0, 0, 775, 776, 5, 42, 0, 0, 776, 159, 1, 0, 0, 0, 777, 778, 5, 47, 0, 0, 778, 161, 1, 0, 0, 0, 779, 780, 5, 37, 0, 0, 780, 163, 1, 0, 0, 0, 781, 782, 5, 91, 0, 0, 782, 783, 1, 0, 0, 0, 783, 784, 6, 76, 0, 0, 784, 785, 6, 76, 0, 0, 785, 165, 1, 0, 0, 0, 786, 787, 5, 93, 0, 0, 787, 788, 1, 0, 0, 0, 788, 789, 6, 77, 13, 0, 789, 790, 6, 77, 13, 0, 790, 167, 1, 0, 0, 0, 791, 795, 3, 70, 29, 0, 792, 794, 3, 86, 37, 0, 793, 792, 1, 0, 0, 0, 794, 797, 1, 0, 0, 0, 795, 793, 1, 0, 0, 0, 795, 796, 1, 0, 0, 0, 796, 808, 1, 0, 0, 0, 797, 795, 1, 0, 0, 0, 798, 801, 3, 84, 36, 0, 799, 801, 3, 78, 33, 0, 800, 798, 1, 0, 0, 0, 800, 799, 1, 0, 0, 0, 801, 803, 1, 0, 0, 0, 802, 804, 3, 86, 37, 0, 803, 802, 1, 0, 0, 0, 804, 805, 1, 0, 0, 0, 805, 803, 1, 0, 0, 0, 805, 806, 1, 0, 0, 0, 806, 808, 1, 0, 0, 0, 807, 791, 1, 0, 0, 0, 807, 800, 1, 0, 0, 0, 808, 169, 1, 0, 0, 0, 809, 811, 3, 80, 34, 0, 810, 812, 3, 82, 35, 0, 811, 810, 1, 0, 0, 0, 812, 813, 1, 0, 0, 0, 813, 811, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 815, 1, 0, 0, 0, 815, 816, 3, 80, 34, 0, 816, 171, 1, 0, 0, 0, 817, 818, 3, 170, 79, 0, 818, 173, 1, 0, 0, 0, 819, 820, 3, 50, 19, 0, 820, 821, 1, 0, 0, 0, 821, 822, 6, 81, 9, 0, 822, 175, 1, 0, 0, 0, 823, 824, 3, 52, 20, 0, 824, 825, 1, 0, 0, 0, 825, 826, 6, 82, 9, 0, 826, 177, 1, 0, 0, 0, 827, 828, 3, 54, 21, 0, 828, 829, 1, 0, 0, 0, 829, 830, 6, 83, 9, 0, 830, 179, 1, 0, 0, 0, 831, 832, 3, 66, 27, 0, 832, 833, 1, 0, 0, 0, 833, 834, 6, 84, 12, 0, 834, 835, 6, 84, 13, 0, 835, 181, 1, 0, 0, 0, 836, 837, 3, 164, 76, 0, 837, 838, 1, 0, 0, 0, 838, 839, 6, 85, 10, 0, 839, 183, 1, 0, 0, 0, 840, 841, 3, 166, 77, 0, 841, 842, 1, 0, 0, 0, 842, 843, 6, 86, 14, 0, 843, 185, 1, 0, 0, 0, 844, 845, 3, 104, 46, 0, 845, 846, 1, 0, 0, 0, 846, 847, 6, 87, 15, 0, 847, 187, 1, 0, 0, 0, 848, 849, 3, 100, 44, 0, 849, 850, 1, 0, 0, 0, 850, 851, 6, 88, 16, 0, 851, 189, 1, 0, 0, 0, 852, 853, 3, 88, 38, 0, 853, 854, 1, 0, 0, 0, 854, 855, 6, 89, 17, 0, 855, 191, 1, 0, 0, 0, 856, 857, 7, 16, 0, 0, 857, 858, 7, 3, 0, 0, 858, 859, 7, 5, 0, 0, 859, 860, 7, 12, 0, 0, 860, 861, 7, 0, 0, 0, 861, 862, 7, 12, 0, 0, 862, 863, 7, 5, 0, 0, 863, 864, 7, 12, 0, 0, 864, 193, 1, 0, 0, 0, 865, 869, 8, 32, 0, 0, 866, 867, 5, 47, 0, 0, 867, 869, 8, 33, 0, 0, 868, 865, 1, 0, 0, 0, 868, 866, 1, 0, 0, 0, 869, 195, 1, 0, 0, 0, 870, 872, 3, 194, 91, 0, 871, 870, 1, 0, 0, 0, 872, 873, 1, 0, 0, 0, 873, 871, 1, 0, 0, 0, 873, 874, 1, 0, 0, 0, 874, 197, 1, 0, 0, 0, 875, 876, 3, 50, 19, 0, 876, 877, 1, 0, 0, 0, 877, 878, 6, 93, 9, 0, 878, 199, 1, 0, 0, 0, 879, 880, 3, 52, 20, 0, 880, 881, 1, 0, 0, 0, 881, 882, 6, 94, 9, 0, 882, 201, 1, 0, 0, 0, 883, 884, 3, 54, 21, 0, 884, 885, 1, 0, 0, 0, 885, 886, 6, 95, 9, 0, 886, 203, 1, 0, 0, 0, 887, 888, 3, 66, 27, 0, 888, 889, 1, 0, 0, 0, 889, 890, 6, 96, 12, 0, 890, 891, 6, 96, 13, 0, 891, 205, 1, 0, 0, 0, 892, 893, 3, 108, 48, 0, 893, 894, 1, 0, 0, 0, 894, 895, 6, 97, 18, 0, 895, 207, 1, 0, 0, 0, 896, 897, 3, 104, 46, 0, 897, 898, 1, 0, 0, 0, 898, 899, 6, 98, 15, 0, 899, 209, 1, 0, 0, 0, 900, 905, 3, 70, 29, 0, 901, 905, 3, 68, 28, 0, 902, 905, 3, 84, 36, 0, 903, 905, 3, 158, 73, 0, 904, 900, 1, 0, 0, 0, 904, 901, 1, 0, 0, 0, 904, 902, 1, 0, 0, 0, 904, 903, 1, 0, 0, 0, 905, 211, 1, 0, 0, 0, 906, 909, 3, 70, 29, 0, 907, 909, 3, 158, 73, 0, 908, 906, 1, 0, 0, 0, 908, 907, 1, 0, 0, 0, 909, 913, 1, 0, 0, 0, 910, 912, 3, 210, 99, 0, 911, 910, 1, 0, 0, 0, 912, 915, 1, 0, 0, 0, 913, 911, 1, 0, 0, 0, 913, 914, 1, 0, 0, 0, 914, 926, 1, 0, 0, 0, 915, 913, 1, 0, 0, 0, 916, 919, 3, 84, 36, 0, 917, 919, 3, 78, 33, 0, 918, 916, 1, 0, 0, 0, 918, 917, 1, 0, 0, 0, 919, 921, 1, 0, 0, 0, 920, 922, 3, 210, 99, 0, 921, 920, 1, 0, 0, 0, 922, 923, 1, 0, 0, 0, 923, 921, 1, 0, 0, 0, 923, 924, 1, 0, 0, 0, 924, 926, 1, 0, 0, 0, 925, 908, 1, 0, 0, 0, 925, 918, 1, 0, 0, 0, 926, 213, 1, 0, 0, 0, 927, 930, 3, 212, 100, 0, 928, 930, 3, 170, 79, 0, 929, 927, 1, 0, 0, 0, 929, 928, 1, 0, 0, 0, 930, 931, 1, 0, 0, 0, 931, 929, 1, 0, 0, 0, 931, 932, 1, 0, 0, 0, 932, 215, 1, 0, 0, 0, 933, 934, 3, 50, 19, 0, 934, 935, 1, 0, 0, 0, 935, 936, 6, 102, 9, 0, 936, 217, 1, 0, 0, 0, 937, 938, 3, 52, 20, 0, 938, 939, 1, 0, 0, 0, 939, 940, 6, 103, 9, 0, 940, 219, 1, 0, 0, 0, 941, 942, 3, 54, 21, 0, 942, 943, 1, 0, 0, 0, 943, 944, 6, 104, 9, 0, 944, 221, 1, 0, 0, 0, 945, 946, 3, 66, 27, 0, 946, 947, 1, 0, 0, 0, 947, 948, 6, 105, 12, 0, 948, 949, 6, 105, 13, 0, 949, 223, 1, 0, 0, 0, 950, 951, 3, 100, 44, 0, 951, 952, 1, 0, 0, 0, 952, 953, 6, 106, 16, 0, 953, 225, 1, 0, 0, 0, 954, 955, 3, 104, 46, 0, 955, 956, 1, 0, 0, 0, 956, 957, 6, 107, 15, 0, 957, 227, 1, 0, 0, 0, 958, 959, 3, 108, 48, 0, 959, 960, 1, 0, 0, 0, 960, 961, 6, 108, 18, 0, 961, 229, 1, 0, 0, 0, 962, 963, 7, 12, 0, 0, 963, 964, 7, 2, 0, 0, 964, 231, 1, 0, 0, 0, 965, 966, 3, 214, 101, 0, 966, 967, 1, 0, 0, 0, 967, 968, 6, 110, 19, 0, 968, 233, 1, 0, 0, 0, 969, 970, 3, 50, 19, 0, 970, 971, 1, 0, 0, 0, 971, 972, 6, 111, 9, 0, 972, 235, 1, 0, 0, 0, 973, 974, 3, 52, 20, 0, 974, 975, 1, 0, 0, 0, 975, 976, 6, 112, 9, 0, 976, 237, 1, 0, 0, 0, 977, 978, 3, 54, 21, 0, 978, 979, 1, 0, 0, 0, 979, 980, 6, 113, 9, 0, 980, 239, 1, 0, 0, 0, 981, 982, 3, 66, 27, 0, 982, 983, 1, 0, 0, 0, 983, 984, 6, 114, 12, 0, 984, 985, 6, 114, 13, 0, 985, 241, 1, 0, 0, 0, 986, 987, 3, 164, 76, 0, 987, 988, 1, 0, 0, 0, 988, 989, 6, 115, 10, 0, 989, 990, 6, 115, 20, 0, 990, 243, 1, 0, 0, 0, 991, 992, 7, 7, 0, 0, 992, 993, 7, 9, 0, 0, 993, 994, 1, 0, 0, 0, 994, 995, 6, 116, 21, 0, 995, 245, 1, 0, 0, 0, 996, 997, 7, 19, 0, 0, 997, 998, 7, 1, 0, 0, 998, 999, 7, 5, 0, 0, 999, 1000, 7, 10, 0, 0, 1000, 1001, 1, 0, 0, 0, 1001, 1002, 6, 117, 21, 0, 1002, 247, 1, 0, 0, 0, 1003, 1004, 8, 34, 0, 0, 1004, 249, 1, 0, 0, 0, 1005, 1007, 3, 248, 118, 0, 1006, 1005, 1, 0, 0, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1006, 1, 0, 0, 0, 1008, 1009, 1, 0, 0, 0, 1009, 1010, 1, 0, 0, 0, 1010, 1011, 3, 318, 153, 0, 1011, 1013, 1, 0, 0, 0, 1012, 1006, 1, 0, 0, 0, 1012, 1013, 1, 0, 0, 0, 1013, 1015, 1, 0, 0, 0, 1014, 1016, 3, 248, 118, 0, 1015, 1014, 1, 0, 0, 0, 1016, 1017, 1, 0, 0, 0, 1017, 1015, 1, 0, 0, 0, 1017, 1018, 1, 0, 0, 0, 1018, 251, 1, 0, 0, 0, 1019, 1020, 3, 172, 80, 0, 1020, 1021, 1, 0, 0, 0, 1021, 1022, 6, 120, 22, 0, 1022, 253, 1, 0, 0, 0, 1023, 1024, 3, 250, 119, 0, 1024, 1025, 1, 0, 0, 0, 1025, 1026, 6, 121, 23, 0, 1026, 255, 1, 0, 0, 0, 1027, 1028, 3, 50, 19, 0, 1028, 1029, 1, 0, 0, 0, 1029, 1030, 6, 122, 9, 0, 1030, 257, 1, 0, 0, 0, 1031, 1032, 3, 52, 20, 0, 1032, 1033, 1, 0, 0, 0, 1033, 1034, 6, 123, 9, 0, 1034, 259, 1, 0, 0, 0, 1035, 1036, 3, 54, 21, 0, 1036, 1037, 1, 0, 0, 0, 1037, 1038, 6, 124, 9, 0, 1038, 261, 1, 0, 0, 0, 1039, 1040, 3, 66, 27, 0, 1040, 1041, 1, 0, 0, 0, 1041, 1042, 6, 125, 12, 0, 1042, 1043, 6, 125, 13, 0, 1043, 1044, 6, 125, 13, 0, 1044, 263, 1, 0, 0, 0, 1045, 1046, 3, 100, 44, 0, 1046, 1047, 1, 0, 0, 0, 1047, 1048, 6, 126, 16, 0, 1048, 265, 1, 0, 0, 0, 1049, 1050, 3, 104, 46, 0, 1050, 1051, 1, 0, 0, 0, 1051, 1052, 6, 127, 15, 0, 1052, 267, 1, 0, 0, 0, 1053, 1054, 3, 108, 48, 0, 1054, 1055, 1, 0, 0, 0, 1055, 1056, 6, 128, 18, 0, 1056, 269, 1, 0, 0, 0, 1057, 1058, 3, 246, 117, 0, 1058, 1059, 1, 0, 0, 0, 1059, 1060, 6, 129, 24, 0, 1060, 271, 1, 0, 0, 0, 1061, 1062, 3, 214, 101, 0, 1062, 1063, 1, 0, 0, 0, 1063, 1064, 6, 130, 19, 0, 1064, 273, 1, 0, 0, 0, 1065, 1066, 3, 172, 80, 0, 1066, 1067, 1, 0, 0, 0, 1067, 1068, 6, 131, 22, 0, 1068, 275, 1, 0, 0, 0, 1069, 1070, 3, 50, 19, 0, 1070, 1071, 1, 0, 0, 0, 1071, 1072, 6, 132, 9, 0, 1072, 277, 1, 0, 0, 0, 1073, 1074, 3, 52, 20, 0, 1074, 1075, 1, 0, 0, 0, 1075, 1076, 6, 133, 9, 0, 1076, 279, 1, 0, 0, 0, 1077, 1078, 3, 54, 21, 0, 1078, 1079, 1, 0, 0, 0, 1079, 1080, 6, 134, 9, 0, 1080, 281, 1, 0, 0, 0, 1081, 1082, 3, 66, 27, 0, 1082, 1083, 1, 0, 0, 0, 1083, 1084, 6, 135, 12, 0, 1084, 1085, 6, 135, 13, 0, 1085, 283, 1, 0, 0, 0, 1086, 1087, 3, 108, 48, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1089, 6, 136, 18, 0, 1089, 285, 1, 0, 0, 0, 1090, 1091, 3, 172, 80, 0, 1091, 1092, 1, 0, 0, 0, 1092, 1093, 6, 137, 22, 0, 1093, 287, 1, 0, 0, 0, 1094, 1095, 3, 168, 78, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1097, 6, 138, 25, 0, 1097, 289, 1, 0, 0, 0, 1098, 1099, 3, 50, 19, 0, 1099, 1100, 1, 0, 0, 0, 1100, 1101, 6, 139, 9, 0, 1101, 291, 1, 0, 0, 0, 1102, 1103, 3, 52, 20, 0, 1103, 1104, 1, 0, 0, 0, 1104, 1105, 6, 140, 9, 0, 1105, 293, 1, 0, 0, 0, 1106, 1107, 3, 54, 21, 0, 1107, 1108, 1, 0, 0, 0, 1108, 1109, 6, 141, 9, 0, 1109, 295, 1, 0, 0, 0, 1110, 1111, 3, 66, 27, 0, 1111, 1112, 1, 0, 0, 0, 1112, 1113, 6, 142, 12, 0, 1113, 1114, 6, 142, 13, 0, 1114, 297, 1, 0, 0, 0, 1115, 1116, 7, 1, 0, 0, 1116, 1117, 7, 9, 0, 0, 1117, 1118, 7, 15, 0, 0, 1118, 1119, 7, 7, 0, 0, 1119, 299, 1, 0, 0, 0, 1120, 1121, 3, 50, 19, 0, 1121, 1122, 1, 0, 0, 0, 1122, 1123, 6, 144, 9, 0, 1123, 301, 1, 0, 0, 0, 1124, 1125, 3, 52, 20, 0, 1125, 1126, 1, 0, 0, 0, 1126, 1127, 6, 145, 9, 0, 1127, 303, 1, 0, 0, 0, 1128, 1129, 3, 54, 21, 0, 1129, 1130, 1, 0, 0, 0, 1130, 1131, 6, 146, 9, 0, 1131, 305, 1, 0, 0, 0, 1132, 1133, 3, 66, 27, 0, 1133, 1134, 1, 0, 0, 0, 1134, 1135, 6, 147, 12, 0, 1135, 1136, 6, 147, 13, 0, 1136, 307, 1, 0, 0, 0, 1137, 1138, 7, 15, 0, 0, 1138, 1139, 7, 31, 0, 0, 1139, 1140, 7, 9, 0, 0, 1140, 1141, 7, 4, 0, 0, 1141, 1142, 7, 5, 0, 0, 1142, 1143, 7, 1, 0, 0, 1143, 1144, 7, 7, 0, 0, 1144, 1145, 7, 9, 0, 0, 1145, 1146, 7, 2, 0, 0, 1146, 309, 1, 0, 0, 0, 1147, 1148, 3, 50, 19, 0, 1148, 1149, 1, 0, 0, 0, 1149, 1150, 6, 149, 9, 0, 1150, 311, 1, 0, 0, 0, 1151, 1152, 3, 52, 20, 0, 1152, 1153, 1, 0, 0, 0, 1153, 1154, 6, 150, 9, 0, 1154, 313, 1, 0, 0, 0, 1155, 1156, 3, 54, 21, 0, 1156, 1157, 1, 0, 0, 0, 1157, 1158, 6, 151, 9, 0, 1158, 315, 1, 0, 0, 0, 1159, 1160, 3, 166, 77, 0, 1160, 1161, 1, 0, 0, 0, 1161, 1162, 6, 152, 14, 0, 1162, 1163, 6, 152, 13, 0, 1163, 317, 1, 0, 0, 0, 1164, 1165, 5, 58, 0, 0, 1165, 319, 1, 0, 0, 0, 1166, 1172, 3, 78, 33, 0, 1167, 1172, 3, 68, 28, 0, 1168, 1172, 3, 108, 48, 0, 1169, 1172, 3, 70, 29, 0, 1170, 1172, 3, 84, 36, 0, 1171, 1166, 1, 0, 0, 0, 1171, 1167, 1, 0, 0, 0, 1171, 1168, 1, 0, 0, 0, 1171, 1169, 1, 0, 0, 0, 1171, 1170, 1, 0, 0, 0, 1172, 1173, 1, 0, 0, 0, 1173, 1171, 1, 0, 0, 0, 1173, 1174, 1, 0, 0, 0, 1174, 321, 1, 0, 0, 0, 1175, 1176, 3, 50, 19, 0, 1176, 1177, 1, 0, 0, 0, 1177, 1178, 6, 155, 9, 0, 1178, 323, 1, 0, 0, 0, 1179, 1180, 3, 52, 20, 0, 1180, 1181, 1, 0, 0, 0, 1181, 1182, 6, 156, 9, 0, 1182, 325, 1, 0, 0, 0, 1183, 1184, 3, 54, 21, 0, 1184, 1185, 1, 0, 0, 0, 1185, 1186, 6, 157, 9, 0, 1186, 327, 1, 0, 0, 0, 58, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 481, 491, 495, 498, 507, 509, 520, 561, 566, 575, 582, 587, 589, 600, 608, 611, 613, 618, 623, 629, 636, 641, 647, 650, 658, 662, 795, 800, 805, 807, 813, 868, 873, 904, 908, 913, 918, 923, 925, 929, 931, 1008, 1012, 1017, 1171, 1173, 26, 5, 2, 0, 5, 4, 0, 5, 6, 0, 5, 1, 0, 5, 3, 0, 5, 10, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 0, 1, 0, 7, 65, 0, 5, 0, 0, 7, 26, 0, 4, 0, 0, 7, 66, 0, 7, 35, 0, 7, 33, 0, 7, 27, 0, 7, 37, 0, 7, 77, 0, 5, 11, 0, 5, 7, 0, 7, 68, 0, 7, 87, 0, 7, 86, 0, 7, 67, 0] \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens b/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens index b496aa68b61f7..d2e7a695282ec 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.tokens @@ -69,45 +69,44 @@ QUOTED_IDENTIFIER=68 EXPR_LINE_COMMENT=69 EXPR_MULTILINE_COMMENT=70 EXPR_WS=71 -OPTIONS=72 -METADATA=73 -FROM_UNQUOTED_IDENTIFIER=74 -FROM_LINE_COMMENT=75 -FROM_MULTILINE_COMMENT=76 -FROM_WS=77 -ID_PATTERN=78 -PROJECT_LINE_COMMENT=79 -PROJECT_MULTILINE_COMMENT=80 -PROJECT_WS=81 -AS=82 -RENAME_LINE_COMMENT=83 -RENAME_MULTILINE_COMMENT=84 -RENAME_WS=85 -ON=86 -WITH=87 -ENRICH_POLICY_NAME=88 -ENRICH_LINE_COMMENT=89 -ENRICH_MULTILINE_COMMENT=90 -ENRICH_WS=91 -ENRICH_FIELD_LINE_COMMENT=92 -ENRICH_FIELD_MULTILINE_COMMENT=93 -ENRICH_FIELD_WS=94 -MVEXPAND_LINE_COMMENT=95 -MVEXPAND_MULTILINE_COMMENT=96 -MVEXPAND_WS=97 -INFO=98 -SHOW_LINE_COMMENT=99 -SHOW_MULTILINE_COMMENT=100 -SHOW_WS=101 -FUNCTIONS=102 -META_LINE_COMMENT=103 -META_MULTILINE_COMMENT=104 -META_WS=105 -COLON=106 -SETTING=107 -SETTING_LINE_COMMENT=108 -SETTTING_MULTILINE_COMMENT=109 -SETTING_WS=110 +METADATA=72 +FROM_UNQUOTED_IDENTIFIER=73 +FROM_LINE_COMMENT=74 +FROM_MULTILINE_COMMENT=75 +FROM_WS=76 +ID_PATTERN=77 +PROJECT_LINE_COMMENT=78 +PROJECT_MULTILINE_COMMENT=79 +PROJECT_WS=80 +AS=81 +RENAME_LINE_COMMENT=82 +RENAME_MULTILINE_COMMENT=83 +RENAME_WS=84 +ON=85 +WITH=86 +ENRICH_POLICY_NAME=87 +ENRICH_LINE_COMMENT=88 +ENRICH_MULTILINE_COMMENT=89 +ENRICH_WS=90 +ENRICH_FIELD_LINE_COMMENT=91 +ENRICH_FIELD_MULTILINE_COMMENT=92 +ENRICH_FIELD_WS=93 +MVEXPAND_LINE_COMMENT=94 +MVEXPAND_MULTILINE_COMMENT=95 +MVEXPAND_WS=96 +INFO=97 +SHOW_LINE_COMMENT=98 +SHOW_MULTILINE_COMMENT=99 +SHOW_WS=100 +FUNCTIONS=101 +META_LINE_COMMENT=102 +META_MULTILINE_COMMENT=103 +META_WS=104 +COLON=105 +SETTING=106 +SETTING_LINE_COMMENT=107 +SETTTING_MULTILINE_COMMENT=108 +SETTING_WS=109 'dissect'=1 'drop'=2 'enrich'=3 @@ -163,11 +162,10 @@ SETTING_WS=110 '/'=63 '%'=64 ']'=66 -'options'=72 -'metadata'=73 -'as'=82 -'on'=86 -'with'=87 -'info'=98 -'functions'=102 -':'=106 +'metadata'=72 +'as'=81 +'on'=85 +'with'=86 +'info'=97 +'functions'=101 +':'=105 diff --git a/packages/kbn-esql-ast/src/antlr/esql_lexer.ts b/packages/kbn-esql-ast/src/antlr/esql_lexer.ts index d10efdb06e4bb..33ff8b4a75db3 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_lexer.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_lexer.ts @@ -84,45 +84,44 @@ export default class esql_lexer extends Lexer { public static readonly EXPR_LINE_COMMENT = 69; public static readonly EXPR_MULTILINE_COMMENT = 70; public static readonly EXPR_WS = 71; - public static readonly OPTIONS = 72; - public static readonly METADATA = 73; - public static readonly FROM_UNQUOTED_IDENTIFIER = 74; - public static readonly FROM_LINE_COMMENT = 75; - public static readonly FROM_MULTILINE_COMMENT = 76; - public static readonly FROM_WS = 77; - public static readonly ID_PATTERN = 78; - public static readonly PROJECT_LINE_COMMENT = 79; - public static readonly PROJECT_MULTILINE_COMMENT = 80; - public static readonly PROJECT_WS = 81; - public static readonly AS = 82; - public static readonly RENAME_LINE_COMMENT = 83; - public static readonly RENAME_MULTILINE_COMMENT = 84; - public static readonly RENAME_WS = 85; - public static readonly ON = 86; - public static readonly WITH = 87; - public static readonly ENRICH_POLICY_NAME = 88; - public static readonly ENRICH_LINE_COMMENT = 89; - public static readonly ENRICH_MULTILINE_COMMENT = 90; - public static readonly ENRICH_WS = 91; - public static readonly ENRICH_FIELD_LINE_COMMENT = 92; - public static readonly ENRICH_FIELD_MULTILINE_COMMENT = 93; - public static readonly ENRICH_FIELD_WS = 94; - public static readonly MVEXPAND_LINE_COMMENT = 95; - public static readonly MVEXPAND_MULTILINE_COMMENT = 96; - public static readonly MVEXPAND_WS = 97; - public static readonly INFO = 98; - public static readonly SHOW_LINE_COMMENT = 99; - public static readonly SHOW_MULTILINE_COMMENT = 100; - public static readonly SHOW_WS = 101; - public static readonly FUNCTIONS = 102; - public static readonly META_LINE_COMMENT = 103; - public static readonly META_MULTILINE_COMMENT = 104; - public static readonly META_WS = 105; - public static readonly COLON = 106; - public static readonly SETTING = 107; - public static readonly SETTING_LINE_COMMENT = 108; - public static readonly SETTTING_MULTILINE_COMMENT = 109; - public static readonly SETTING_WS = 110; + public static readonly METADATA = 72; + public static readonly FROM_UNQUOTED_IDENTIFIER = 73; + public static readonly FROM_LINE_COMMENT = 74; + public static readonly FROM_MULTILINE_COMMENT = 75; + public static readonly FROM_WS = 76; + public static readonly ID_PATTERN = 77; + public static readonly PROJECT_LINE_COMMENT = 78; + public static readonly PROJECT_MULTILINE_COMMENT = 79; + public static readonly PROJECT_WS = 80; + public static readonly AS = 81; + public static readonly RENAME_LINE_COMMENT = 82; + public static readonly RENAME_MULTILINE_COMMENT = 83; + public static readonly RENAME_WS = 84; + public static readonly ON = 85; + public static readonly WITH = 86; + public static readonly ENRICH_POLICY_NAME = 87; + public static readonly ENRICH_LINE_COMMENT = 88; + public static readonly ENRICH_MULTILINE_COMMENT = 89; + public static readonly ENRICH_WS = 90; + public static readonly ENRICH_FIELD_LINE_COMMENT = 91; + public static readonly ENRICH_FIELD_MULTILINE_COMMENT = 92; + public static readonly ENRICH_FIELD_WS = 93; + public static readonly MVEXPAND_LINE_COMMENT = 94; + public static readonly MVEXPAND_MULTILINE_COMMENT = 95; + public static readonly MVEXPAND_WS = 96; + public static readonly INFO = 97; + public static readonly SHOW_LINE_COMMENT = 98; + public static readonly SHOW_MULTILINE_COMMENT = 99; + public static readonly SHOW_WS = 100; + public static readonly FUNCTIONS = 101; + public static readonly META_LINE_COMMENT = 102; + public static readonly META_MULTILINE_COMMENT = 103; + public static readonly META_WS = 104; + public static readonly COLON = 105; + public static readonly SETTING = 106; + public static readonly SETTING_LINE_COMMENT = 107; + public static readonly SETTTING_MULTILINE_COMMENT = 108; + public static readonly SETTING_WS = 109; public static readonly EOF = Token.EOF; public static readonly EXPLAIN_MODE = 1; public static readonly EXPRESSION_MODE = 2; @@ -174,7 +173,6 @@ export default class esql_lexer extends Lexer { "']'", null, null, null, null, null, - "'options'", "'metadata'", null, null, null, null, @@ -237,7 +235,6 @@ export default class esql_lexer extends Lexer { "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", - "OPTIONS", "METADATA", "FROM_UNQUOTED_IDENTIFIER", "FROM_LINE_COMMENT", @@ -294,22 +291,21 @@ export default class esql_lexer extends Lexer { "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", "QUOTED_ID", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", "FROM_PIPE", "FROM_OPENING_BRACKET", "FROM_CLOSING_BRACKET", "FROM_COMMA", "FROM_ASSIGN", - "FROM_QUOTED_STRING", "OPTIONS", "METADATA", "FROM_UNQUOTED_IDENTIFIER_PART", - "FROM_UNQUOTED_IDENTIFIER", "FROM_LINE_COMMENT", "FROM_MULTILINE_COMMENT", - "FROM_WS", "PROJECT_PIPE", "PROJECT_DOT", "PROJECT_COMMA", "UNQUOTED_ID_BODY_WITH_PATTERN", - "UNQUOTED_ID_PATTERN", "ID_PATTERN", "PROJECT_LINE_COMMENT", "PROJECT_MULTILINE_COMMENT", - "PROJECT_WS", "RENAME_PIPE", "RENAME_ASSIGN", "RENAME_COMMA", "RENAME_DOT", - "AS", "RENAME_ID_PATTERN", "RENAME_LINE_COMMENT", "RENAME_MULTILINE_COMMENT", - "RENAME_WS", "ENRICH_PIPE", "ENRICH_OPENING_BRACKET", "ON", "WITH", "ENRICH_POLICY_NAME_BODY", - "ENRICH_POLICY_NAME", "ENRICH_QUOTED_IDENTIFIER", "ENRICH_MODE_UNQUOTED_VALUE", - "ENRICH_LINE_COMMENT", "ENRICH_MULTILINE_COMMENT", "ENRICH_WS", "ENRICH_FIELD_PIPE", - "ENRICH_FIELD_ASSIGN", "ENRICH_FIELD_COMMA", "ENRICH_FIELD_DOT", "ENRICH_FIELD_WITH", - "ENRICH_FIELD_ID_PATTERN", "ENRICH_FIELD_QUOTED_IDENTIFIER", "ENRICH_FIELD_LINE_COMMENT", - "ENRICH_FIELD_MULTILINE_COMMENT", "ENRICH_FIELD_WS", "MVEXPAND_PIPE", - "MVEXPAND_DOT", "MVEXPAND_QUOTED_IDENTIFIER", "MVEXPAND_UNQUOTED_IDENTIFIER", - "MVEXPAND_LINE_COMMENT", "MVEXPAND_MULTILINE_COMMENT", "MVEXPAND_WS", - "SHOW_PIPE", "INFO", "SHOW_LINE_COMMENT", "SHOW_MULTILINE_COMMENT", "SHOW_WS", - "META_PIPE", "FUNCTIONS", "META_LINE_COMMENT", "META_MULTILINE_COMMENT", + "FROM_QUOTED_STRING", "METADATA", "FROM_UNQUOTED_IDENTIFIER_PART", "FROM_UNQUOTED_IDENTIFIER", + "FROM_LINE_COMMENT", "FROM_MULTILINE_COMMENT", "FROM_WS", "PROJECT_PIPE", + "PROJECT_DOT", "PROJECT_COMMA", "UNQUOTED_ID_BODY_WITH_PATTERN", "UNQUOTED_ID_PATTERN", + "ID_PATTERN", "PROJECT_LINE_COMMENT", "PROJECT_MULTILINE_COMMENT", "PROJECT_WS", + "RENAME_PIPE", "RENAME_ASSIGN", "RENAME_COMMA", "RENAME_DOT", "AS", "RENAME_ID_PATTERN", + "RENAME_LINE_COMMENT", "RENAME_MULTILINE_COMMENT", "RENAME_WS", "ENRICH_PIPE", + "ENRICH_OPENING_BRACKET", "ON", "WITH", "ENRICH_POLICY_NAME_BODY", "ENRICH_POLICY_NAME", + "ENRICH_QUOTED_IDENTIFIER", "ENRICH_MODE_UNQUOTED_VALUE", "ENRICH_LINE_COMMENT", + "ENRICH_MULTILINE_COMMENT", "ENRICH_WS", "ENRICH_FIELD_PIPE", "ENRICH_FIELD_ASSIGN", + "ENRICH_FIELD_COMMA", "ENRICH_FIELD_DOT", "ENRICH_FIELD_WITH", "ENRICH_FIELD_ID_PATTERN", + "ENRICH_FIELD_QUOTED_IDENTIFIER", "ENRICH_FIELD_LINE_COMMENT", "ENRICH_FIELD_MULTILINE_COMMENT", + "ENRICH_FIELD_WS", "MVEXPAND_PIPE", "MVEXPAND_DOT", "MVEXPAND_QUOTED_IDENTIFIER", + "MVEXPAND_UNQUOTED_IDENTIFIER", "MVEXPAND_LINE_COMMENT", "MVEXPAND_MULTILINE_COMMENT", + "MVEXPAND_WS", "SHOW_PIPE", "INFO", "SHOW_LINE_COMMENT", "SHOW_MULTILINE_COMMENT", + "SHOW_WS", "META_PIPE", "FUNCTIONS", "META_LINE_COMMENT", "META_MULTILINE_COMMENT", "META_WS", "SETTING_CLOSING_BRACKET", "COLON", "SETTING", "SETTING_LINE_COMMENT", "SETTTING_MULTILINE_COMMENT", "SETTING_WS", ]; @@ -332,7 +328,7 @@ export default class esql_lexer extends Lexer { public get modeNames(): string[] { return esql_lexer.modeNames; } - public static readonly _serializedATN: number[] = [4,0,110,1197,6,-1,6, + public static readonly _serializedATN: number[] = [4,0,109,1187,6,-1,6, -1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,2,0,7,0,2,1,7,1,2, 2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10, 2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2, @@ -356,386 +352,383 @@ export default class esql_lexer extends Lexer { 7,134,2,135,7,135,2,136,7,136,2,137,7,137,2,138,7,138,2,139,7,139,2,140, 7,140,2,141,7,141,2,142,7,142,2,143,7,143,2,144,7,144,2,145,7,145,2,146, 7,146,2,147,7,147,2,148,7,148,2,149,7,149,2,150,7,150,2,151,7,151,2,152, - 7,152,2,153,7,153,2,154,7,154,2,155,7,155,2,156,7,156,2,157,7,157,2,158, - 7,158,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1, - 4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6,1,6,1, - 6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1, - 7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10, - 1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1, - 11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,13,1,13,1,13, - 1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1, - 15,1,15,1,15,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17, - 1,17,1,17,1,17,1,17,1,18,4,18,482,8,18,11,18,12,18,483,1,18,1,18,1,19,1, - 19,1,19,1,19,5,19,492,8,19,10,19,12,19,495,9,19,1,19,3,19,498,8,19,1,19, - 3,19,501,8,19,1,19,1,19,1,20,1,20,1,20,1,20,1,20,5,20,510,8,20,10,20,12, - 20,513,9,20,1,20,1,20,1,20,1,20,1,20,1,21,4,21,521,8,21,11,21,12,21,522, - 1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,23,1,24,1,24,1, - 24,1,24,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,28, - 1,28,1,29,1,29,1,30,1,30,1,30,1,31,1,31,1,32,1,32,3,32,564,8,32,1,32,4, - 32,567,8,32,11,32,12,32,568,1,33,1,33,1,34,1,34,1,35,1,35,1,35,3,35,578, - 8,35,1,36,1,36,1,37,1,37,1,37,3,37,585,8,37,1,38,1,38,1,38,5,38,590,8,38, - 10,38,12,38,593,9,38,1,38,1,38,1,38,1,38,1,38,1,38,5,38,601,8,38,10,38, - 12,38,604,9,38,1,38,1,38,1,38,1,38,1,38,3,38,611,8,38,1,38,3,38,614,8,38, - 3,38,616,8,38,1,39,4,39,619,8,39,11,39,12,39,620,1,40,4,40,624,8,40,11, - 40,12,40,625,1,40,1,40,5,40,630,8,40,10,40,12,40,633,9,40,1,40,1,40,4,40, - 637,8,40,11,40,12,40,638,1,40,4,40,642,8,40,11,40,12,40,643,1,40,1,40,5, - 40,648,8,40,10,40,12,40,651,9,40,3,40,653,8,40,1,40,1,40,1,40,1,40,4,40, - 659,8,40,11,40,12,40,660,1,40,1,40,3,40,665,8,40,1,41,1,41,1,41,1,42,1, - 42,1,42,1,42,1,43,1,43,1,43,1,43,1,44,1,44,1,45,1,45,1,45,1,46,1,46,1,47, - 1,47,1,47,1,47,1,47,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,49,1,50,1,50,1, - 50,1,50,1,50,1,50,1,51,1,51,1,51,1,51,1,51,1,52,1,52,1,53,1,53,1,53,1,54, - 1,54,1,54,1,55,1,55,1,55,1,55,1,55,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1, - 57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,1,59,1,59,1,59,1,60,1,60,1,61,1,61, - 1,61,1,61,1,61,1,61,1,62,1,62,1,63,1,63,1,63,1,63,1,63,1,64,1,64,1,64,1, - 65,1,65,1,65,1,66,1,66,1,66,1,67,1,67,1,68,1,68,1,68,1,69,1,69,1,70,1,70, - 1,70,1,71,1,71,1,72,1,72,1,73,1,73,1,74,1,74,1,75,1,75,1,76,1,76,1,76,1, - 76,1,76,1,77,1,77,1,77,1,77,1,77,1,78,1,78,5,78,796,8,78,10,78,12,78,799, - 9,78,1,78,1,78,3,78,803,8,78,1,78,4,78,806,8,78,11,78,12,78,807,3,78,810, - 8,78,1,79,1,79,4,79,814,8,79,11,79,12,79,815,1,79,1,79,1,80,1,80,1,81,1, - 81,1,81,1,81,1,82,1,82,1,82,1,82,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84, - 1,84,1,85,1,85,1,85,1,85,1,86,1,86,1,86,1,86,1,87,1,87,1,87,1,87,1,88,1, - 88,1,88,1,88,1,89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,90,1,90,1,90,1,90, - 1,91,1,91,1,91,1,91,1,91,1,91,1,91,1,91,1,91,1,92,1,92,1,92,3,92,879,8, - 92,1,93,4,93,882,8,93,11,93,12,93,883,1,94,1,94,1,94,1,94,1,95,1,95,1,95, - 1,95,1,96,1,96,1,96,1,96,1,97,1,97,1,97,1,97,1,97,1,98,1,98,1,98,1,98,1, - 99,1,99,1,99,1,99,1,100,1,100,1,100,1,100,3,100,915,8,100,1,101,1,101,3, - 101,919,8,101,1,101,5,101,922,8,101,10,101,12,101,925,9,101,1,101,1,101, - 3,101,929,8,101,1,101,4,101,932,8,101,11,101,12,101,933,3,101,936,8,101, - 1,102,1,102,4,102,940,8,102,11,102,12,102,941,1,103,1,103,1,103,1,103,1, - 104,1,104,1,104,1,104,1,105,1,105,1,105,1,105,1,106,1,106,1,106,1,106,1, - 106,1,107,1,107,1,107,1,107,1,108,1,108,1,108,1,108,1,109,1,109,1,109,1, - 109,1,110,1,110,1,110,1,111,1,111,1,111,1,111,1,112,1,112,1,112,1,112,1, - 113,1,113,1,113,1,113,1,114,1,114,1,114,1,114,1,115,1,115,1,115,1,115,1, - 115,1,116,1,116,1,116,1,116,1,116,1,117,1,117,1,117,1,117,1,117,1,118,1, - 118,1,118,1,118,1,118,1,118,1,118,1,119,1,119,1,120,4,120,1017,8,120,11, - 120,12,120,1018,1,120,1,120,3,120,1023,8,120,1,120,4,120,1026,8,120,11, - 120,12,120,1027,1,121,1,121,1,121,1,121,1,122,1,122,1,122,1,122,1,123,1, - 123,1,123,1,123,1,124,1,124,1,124,1,124,1,125,1,125,1,125,1,125,1,126,1, - 126,1,126,1,126,1,126,1,126,1,127,1,127,1,127,1,127,1,128,1,128,1,128,1, - 128,1,129,1,129,1,129,1,129,1,130,1,130,1,130,1,130,1,131,1,131,1,131,1, - 131,1,132,1,132,1,132,1,132,1,133,1,133,1,133,1,133,1,134,1,134,1,134,1, - 134,1,135,1,135,1,135,1,135,1,136,1,136,1,136,1,136,1,136,1,137,1,137,1, - 137,1,137,1,138,1,138,1,138,1,138,1,139,1,139,1,139,1,139,1,140,1,140,1, - 140,1,140,1,141,1,141,1,141,1,141,1,142,1,142,1,142,1,142,1,143,1,143,1, - 143,1,143,1,143,1,144,1,144,1,144,1,144,1,144,1,145,1,145,1,145,1,145,1, - 146,1,146,1,146,1,146,1,147,1,147,1,147,1,147,1,148,1,148,1,148,1,148,1, - 148,1,149,1,149,1,149,1,149,1,149,1,149,1,149,1,149,1,149,1,149,1,150,1, - 150,1,150,1,150,1,151,1,151,1,151,1,151,1,152,1,152,1,152,1,152,1,153,1, - 153,1,153,1,153,1,153,1,154,1,154,1,155,1,155,1,155,1,155,1,155,4,155,1182, - 8,155,11,155,12,155,1183,1,156,1,156,1,156,1,156,1,157,1,157,1,157,1,157, - 1,158,1,158,1,158,1,158,2,511,602,0,159,12,1,14,2,16,3,18,4,20,5,22,6,24, - 7,26,8,28,9,30,10,32,11,34,12,36,13,38,14,40,15,42,16,44,17,46,18,48,19, - 50,20,52,21,54,22,56,0,58,0,60,23,62,24,64,25,66,26,68,0,70,0,72,0,74,0, - 76,0,78,0,80,0,82,0,84,0,86,0,88,27,90,28,92,29,94,30,96,31,98,32,100,33, - 102,34,104,35,106,36,108,37,110,38,112,39,114,40,116,41,118,42,120,43,122, - 44,124,45,126,46,128,47,130,48,132,49,134,50,136,51,138,52,140,53,142,54, - 144,55,146,56,148,57,150,58,152,59,154,60,156,61,158,62,160,63,162,64,164, - 65,166,66,168,67,170,0,172,68,174,69,176,70,178,71,180,0,182,0,184,0,186, - 0,188,0,190,0,192,72,194,73,196,0,198,74,200,75,202,76,204,77,206,0,208, - 0,210,0,212,0,214,0,216,78,218,79,220,80,222,81,224,0,226,0,228,0,230,0, - 232,82,234,0,236,83,238,84,240,85,242,0,244,0,246,86,248,87,250,0,252,88, - 254,0,256,0,258,89,260,90,262,91,264,0,266,0,268,0,270,0,272,0,274,0,276, - 0,278,92,280,93,282,94,284,0,286,0,288,0,290,0,292,95,294,96,296,97,298, - 0,300,98,302,99,304,100,306,101,308,0,310,102,312,103,314,104,316,105,318, - 0,320,106,322,107,324,108,326,109,328,110,12,0,1,2,3,4,5,6,7,8,9,10,11, - 35,2,0,68,68,100,100,2,0,73,73,105,105,2,0,83,83,115,115,2,0,69,69,101, - 101,2,0,67,67,99,99,2,0,84,84,116,116,2,0,82,82,114,114,2,0,79,79,111,111, - 2,0,80,80,112,112,2,0,78,78,110,110,2,0,72,72,104,104,2,0,86,86,118,118, - 2,0,65,65,97,97,2,0,76,76,108,108,2,0,88,88,120,120,2,0,70,70,102,102,2, - 0,77,77,109,109,2,0,71,71,103,103,2,0,75,75,107,107,2,0,87,87,119,119,6, - 0,9,10,13,13,32,32,47,47,91,91,93,93,2,0,10,10,13,13,3,0,9,10,13,13,32, - 32,1,0,48,57,2,0,65,90,97,122,8,0,34,34,78,78,82,82,84,84,92,92,110,110, - 114,114,116,116,4,0,10,10,13,13,34,34,92,92,2,0,43,43,45,45,1,0,96,96,2, - 0,66,66,98,98,2,0,89,89,121,121,2,0,85,85,117,117,10,0,9,10,13,13,32,32, - 44,44,47,47,61,61,91,91,93,93,96,96,124,124,2,0,42,42,47,47,11,0,9,10,13, - 13,32,32,34,35,44,44,47,47,58,58,60,60,62,63,92,92,124,124,1224,0,12,1, - 0,0,0,0,14,1,0,0,0,0,16,1,0,0,0,0,18,1,0,0,0,0,20,1,0,0,0,0,22,1,0,0,0, - 0,24,1,0,0,0,0,26,1,0,0,0,0,28,1,0,0,0,0,30,1,0,0,0,0,32,1,0,0,0,0,34,1, - 0,0,0,0,36,1,0,0,0,0,38,1,0,0,0,0,40,1,0,0,0,0,42,1,0,0,0,0,44,1,0,0,0, - 0,46,1,0,0,0,0,48,1,0,0,0,0,50,1,0,0,0,0,52,1,0,0,0,0,54,1,0,0,0,1,56,1, - 0,0,0,1,58,1,0,0,0,1,60,1,0,0,0,1,62,1,0,0,0,1,64,1,0,0,0,2,66,1,0,0,0, - 2,88,1,0,0,0,2,90,1,0,0,0,2,92,1,0,0,0,2,94,1,0,0,0,2,96,1,0,0,0,2,98,1, - 0,0,0,2,100,1,0,0,0,2,102,1,0,0,0,2,104,1,0,0,0,2,106,1,0,0,0,2,108,1,0, - 0,0,2,110,1,0,0,0,2,112,1,0,0,0,2,114,1,0,0,0,2,116,1,0,0,0,2,118,1,0,0, - 0,2,120,1,0,0,0,2,122,1,0,0,0,2,124,1,0,0,0,2,126,1,0,0,0,2,128,1,0,0,0, - 2,130,1,0,0,0,2,132,1,0,0,0,2,134,1,0,0,0,2,136,1,0,0,0,2,138,1,0,0,0,2, - 140,1,0,0,0,2,142,1,0,0,0,2,144,1,0,0,0,2,146,1,0,0,0,2,148,1,0,0,0,2,150, - 1,0,0,0,2,152,1,0,0,0,2,154,1,0,0,0,2,156,1,0,0,0,2,158,1,0,0,0,2,160,1, - 0,0,0,2,162,1,0,0,0,2,164,1,0,0,0,2,166,1,0,0,0,2,168,1,0,0,0,2,172,1,0, - 0,0,2,174,1,0,0,0,2,176,1,0,0,0,2,178,1,0,0,0,3,180,1,0,0,0,3,182,1,0,0, - 0,3,184,1,0,0,0,3,186,1,0,0,0,3,188,1,0,0,0,3,190,1,0,0,0,3,192,1,0,0,0, - 3,194,1,0,0,0,3,198,1,0,0,0,3,200,1,0,0,0,3,202,1,0,0,0,3,204,1,0,0,0,4, - 206,1,0,0,0,4,208,1,0,0,0,4,210,1,0,0,0,4,216,1,0,0,0,4,218,1,0,0,0,4,220, - 1,0,0,0,4,222,1,0,0,0,5,224,1,0,0,0,5,226,1,0,0,0,5,228,1,0,0,0,5,230,1, - 0,0,0,5,232,1,0,0,0,5,234,1,0,0,0,5,236,1,0,0,0,5,238,1,0,0,0,5,240,1,0, - 0,0,6,242,1,0,0,0,6,244,1,0,0,0,6,246,1,0,0,0,6,248,1,0,0,0,6,252,1,0,0, - 0,6,254,1,0,0,0,6,256,1,0,0,0,6,258,1,0,0,0,6,260,1,0,0,0,6,262,1,0,0,0, - 7,264,1,0,0,0,7,266,1,0,0,0,7,268,1,0,0,0,7,270,1,0,0,0,7,272,1,0,0,0,7, - 274,1,0,0,0,7,276,1,0,0,0,7,278,1,0,0,0,7,280,1,0,0,0,7,282,1,0,0,0,8,284, - 1,0,0,0,8,286,1,0,0,0,8,288,1,0,0,0,8,290,1,0,0,0,8,292,1,0,0,0,8,294,1, - 0,0,0,8,296,1,0,0,0,9,298,1,0,0,0,9,300,1,0,0,0,9,302,1,0,0,0,9,304,1,0, - 0,0,9,306,1,0,0,0,10,308,1,0,0,0,10,310,1,0,0,0,10,312,1,0,0,0,10,314,1, - 0,0,0,10,316,1,0,0,0,11,318,1,0,0,0,11,320,1,0,0,0,11,322,1,0,0,0,11,324, - 1,0,0,0,11,326,1,0,0,0,11,328,1,0,0,0,12,330,1,0,0,0,14,340,1,0,0,0,16, - 347,1,0,0,0,18,356,1,0,0,0,20,363,1,0,0,0,22,373,1,0,0,0,24,380,1,0,0,0, - 26,387,1,0,0,0,28,401,1,0,0,0,30,408,1,0,0,0,32,416,1,0,0,0,34,423,1,0, - 0,0,36,435,1,0,0,0,38,444,1,0,0,0,40,450,1,0,0,0,42,457,1,0,0,0,44,464, - 1,0,0,0,46,472,1,0,0,0,48,481,1,0,0,0,50,487,1,0,0,0,52,504,1,0,0,0,54, - 520,1,0,0,0,56,526,1,0,0,0,58,531,1,0,0,0,60,536,1,0,0,0,62,540,1,0,0,0, - 64,544,1,0,0,0,66,548,1,0,0,0,68,552,1,0,0,0,70,554,1,0,0,0,72,556,1,0, - 0,0,74,559,1,0,0,0,76,561,1,0,0,0,78,570,1,0,0,0,80,572,1,0,0,0,82,577, - 1,0,0,0,84,579,1,0,0,0,86,584,1,0,0,0,88,615,1,0,0,0,90,618,1,0,0,0,92, - 664,1,0,0,0,94,666,1,0,0,0,96,669,1,0,0,0,98,673,1,0,0,0,100,677,1,0,0, - 0,102,679,1,0,0,0,104,682,1,0,0,0,106,684,1,0,0,0,108,689,1,0,0,0,110,691, - 1,0,0,0,112,697,1,0,0,0,114,703,1,0,0,0,116,708,1,0,0,0,118,710,1,0,0,0, - 120,713,1,0,0,0,122,716,1,0,0,0,124,721,1,0,0,0,126,725,1,0,0,0,128,730, - 1,0,0,0,130,736,1,0,0,0,132,739,1,0,0,0,134,741,1,0,0,0,136,747,1,0,0,0, - 138,749,1,0,0,0,140,754,1,0,0,0,142,757,1,0,0,0,144,760,1,0,0,0,146,763, - 1,0,0,0,148,765,1,0,0,0,150,768,1,0,0,0,152,770,1,0,0,0,154,773,1,0,0,0, - 156,775,1,0,0,0,158,777,1,0,0,0,160,779,1,0,0,0,162,781,1,0,0,0,164,783, - 1,0,0,0,166,788,1,0,0,0,168,809,1,0,0,0,170,811,1,0,0,0,172,819,1,0,0,0, - 174,821,1,0,0,0,176,825,1,0,0,0,178,829,1,0,0,0,180,833,1,0,0,0,182,838, - 1,0,0,0,184,842,1,0,0,0,186,846,1,0,0,0,188,850,1,0,0,0,190,854,1,0,0,0, - 192,858,1,0,0,0,194,866,1,0,0,0,196,878,1,0,0,0,198,881,1,0,0,0,200,885, - 1,0,0,0,202,889,1,0,0,0,204,893,1,0,0,0,206,897,1,0,0,0,208,902,1,0,0,0, - 210,906,1,0,0,0,212,914,1,0,0,0,214,935,1,0,0,0,216,939,1,0,0,0,218,943, - 1,0,0,0,220,947,1,0,0,0,222,951,1,0,0,0,224,955,1,0,0,0,226,960,1,0,0,0, - 228,964,1,0,0,0,230,968,1,0,0,0,232,972,1,0,0,0,234,975,1,0,0,0,236,979, - 1,0,0,0,238,983,1,0,0,0,240,987,1,0,0,0,242,991,1,0,0,0,244,996,1,0,0,0, - 246,1001,1,0,0,0,248,1006,1,0,0,0,250,1013,1,0,0,0,252,1022,1,0,0,0,254, - 1029,1,0,0,0,256,1033,1,0,0,0,258,1037,1,0,0,0,260,1041,1,0,0,0,262,1045, - 1,0,0,0,264,1049,1,0,0,0,266,1055,1,0,0,0,268,1059,1,0,0,0,270,1063,1,0, - 0,0,272,1067,1,0,0,0,274,1071,1,0,0,0,276,1075,1,0,0,0,278,1079,1,0,0,0, - 280,1083,1,0,0,0,282,1087,1,0,0,0,284,1091,1,0,0,0,286,1096,1,0,0,0,288, - 1100,1,0,0,0,290,1104,1,0,0,0,292,1108,1,0,0,0,294,1112,1,0,0,0,296,1116, - 1,0,0,0,298,1120,1,0,0,0,300,1125,1,0,0,0,302,1130,1,0,0,0,304,1134,1,0, - 0,0,306,1138,1,0,0,0,308,1142,1,0,0,0,310,1147,1,0,0,0,312,1157,1,0,0,0, - 314,1161,1,0,0,0,316,1165,1,0,0,0,318,1169,1,0,0,0,320,1174,1,0,0,0,322, - 1181,1,0,0,0,324,1185,1,0,0,0,326,1189,1,0,0,0,328,1193,1,0,0,0,330,331, - 7,0,0,0,331,332,7,1,0,0,332,333,7,2,0,0,333,334,7,2,0,0,334,335,7,3,0,0, - 335,336,7,4,0,0,336,337,7,5,0,0,337,338,1,0,0,0,338,339,6,0,0,0,339,13, - 1,0,0,0,340,341,7,0,0,0,341,342,7,6,0,0,342,343,7,7,0,0,343,344,7,8,0,0, - 344,345,1,0,0,0,345,346,6,1,1,0,346,15,1,0,0,0,347,348,7,3,0,0,348,349, - 7,9,0,0,349,350,7,6,0,0,350,351,7,1,0,0,351,352,7,4,0,0,352,353,7,10,0, - 0,353,354,1,0,0,0,354,355,6,2,2,0,355,17,1,0,0,0,356,357,7,3,0,0,357,358, - 7,11,0,0,358,359,7,12,0,0,359,360,7,13,0,0,360,361,1,0,0,0,361,362,6,3, - 0,0,362,19,1,0,0,0,363,364,7,3,0,0,364,365,7,14,0,0,365,366,7,8,0,0,366, - 367,7,13,0,0,367,368,7,12,0,0,368,369,7,1,0,0,369,370,7,9,0,0,370,371,1, - 0,0,0,371,372,6,4,3,0,372,21,1,0,0,0,373,374,7,15,0,0,374,375,7,6,0,0,375, - 376,7,7,0,0,376,377,7,16,0,0,377,378,1,0,0,0,378,379,6,5,4,0,379,23,1,0, - 0,0,380,381,7,17,0,0,381,382,7,6,0,0,382,383,7,7,0,0,383,384,7,18,0,0,384, - 385,1,0,0,0,385,386,6,6,0,0,386,25,1,0,0,0,387,388,7,1,0,0,388,389,7,9, - 0,0,389,390,7,13,0,0,390,391,7,1,0,0,391,392,7,9,0,0,392,393,7,3,0,0,393, - 394,7,2,0,0,394,395,7,5,0,0,395,396,7,12,0,0,396,397,7,5,0,0,397,398,7, - 2,0,0,398,399,1,0,0,0,399,400,6,7,0,0,400,27,1,0,0,0,401,402,7,18,0,0,402, - 403,7,3,0,0,403,404,7,3,0,0,404,405,7,8,0,0,405,406,1,0,0,0,406,407,6,8, - 1,0,407,29,1,0,0,0,408,409,7,13,0,0,409,410,7,1,0,0,410,411,7,16,0,0,411, - 412,7,1,0,0,412,413,7,5,0,0,413,414,1,0,0,0,414,415,6,9,0,0,415,31,1,0, - 0,0,416,417,7,16,0,0,417,418,7,3,0,0,418,419,7,5,0,0,419,420,7,12,0,0,420, - 421,1,0,0,0,421,422,6,10,5,0,422,33,1,0,0,0,423,424,7,16,0,0,424,425,7, - 11,0,0,425,426,5,95,0,0,426,427,7,3,0,0,427,428,7,14,0,0,428,429,7,8,0, - 0,429,430,7,12,0,0,430,431,7,9,0,0,431,432,7,0,0,0,432,433,1,0,0,0,433, - 434,6,11,6,0,434,35,1,0,0,0,435,436,7,6,0,0,436,437,7,3,0,0,437,438,7,9, - 0,0,438,439,7,12,0,0,439,440,7,16,0,0,440,441,7,3,0,0,441,442,1,0,0,0,442, - 443,6,12,7,0,443,37,1,0,0,0,444,445,7,6,0,0,445,446,7,7,0,0,446,447,7,19, - 0,0,447,448,1,0,0,0,448,449,6,13,0,0,449,39,1,0,0,0,450,451,7,2,0,0,451, - 452,7,10,0,0,452,453,7,7,0,0,453,454,7,19,0,0,454,455,1,0,0,0,455,456,6, - 14,8,0,456,41,1,0,0,0,457,458,7,2,0,0,458,459,7,7,0,0,459,460,7,6,0,0,460, - 461,7,5,0,0,461,462,1,0,0,0,462,463,6,15,0,0,463,43,1,0,0,0,464,465,7,2, - 0,0,465,466,7,5,0,0,466,467,7,12,0,0,467,468,7,5,0,0,468,469,7,2,0,0,469, - 470,1,0,0,0,470,471,6,16,0,0,471,45,1,0,0,0,472,473,7,19,0,0,473,474,7, - 10,0,0,474,475,7,3,0,0,475,476,7,6,0,0,476,477,7,3,0,0,477,478,1,0,0,0, - 478,479,6,17,0,0,479,47,1,0,0,0,480,482,8,20,0,0,481,480,1,0,0,0,482,483, - 1,0,0,0,483,481,1,0,0,0,483,484,1,0,0,0,484,485,1,0,0,0,485,486,6,18,0, - 0,486,49,1,0,0,0,487,488,5,47,0,0,488,489,5,47,0,0,489,493,1,0,0,0,490, - 492,8,21,0,0,491,490,1,0,0,0,492,495,1,0,0,0,493,491,1,0,0,0,493,494,1, - 0,0,0,494,497,1,0,0,0,495,493,1,0,0,0,496,498,5,13,0,0,497,496,1,0,0,0, - 497,498,1,0,0,0,498,500,1,0,0,0,499,501,5,10,0,0,500,499,1,0,0,0,500,501, - 1,0,0,0,501,502,1,0,0,0,502,503,6,19,9,0,503,51,1,0,0,0,504,505,5,47,0, - 0,505,506,5,42,0,0,506,511,1,0,0,0,507,510,3,52,20,0,508,510,9,0,0,0,509, - 507,1,0,0,0,509,508,1,0,0,0,510,513,1,0,0,0,511,512,1,0,0,0,511,509,1,0, - 0,0,512,514,1,0,0,0,513,511,1,0,0,0,514,515,5,42,0,0,515,516,5,47,0,0,516, - 517,1,0,0,0,517,518,6,20,9,0,518,53,1,0,0,0,519,521,7,22,0,0,520,519,1, - 0,0,0,521,522,1,0,0,0,522,520,1,0,0,0,522,523,1,0,0,0,523,524,1,0,0,0,524, - 525,6,21,9,0,525,55,1,0,0,0,526,527,3,164,76,0,527,528,1,0,0,0,528,529, - 6,22,10,0,529,530,6,22,11,0,530,57,1,0,0,0,531,532,3,66,27,0,532,533,1, - 0,0,0,533,534,6,23,12,0,534,535,6,23,13,0,535,59,1,0,0,0,536,537,3,54,21, - 0,537,538,1,0,0,0,538,539,6,24,9,0,539,61,1,0,0,0,540,541,3,50,19,0,541, - 542,1,0,0,0,542,543,6,25,9,0,543,63,1,0,0,0,544,545,3,52,20,0,545,546,1, - 0,0,0,546,547,6,26,9,0,547,65,1,0,0,0,548,549,5,124,0,0,549,550,1,0,0,0, - 550,551,6,27,13,0,551,67,1,0,0,0,552,553,7,23,0,0,553,69,1,0,0,0,554,555, - 7,24,0,0,555,71,1,0,0,0,556,557,5,92,0,0,557,558,7,25,0,0,558,73,1,0,0, - 0,559,560,8,26,0,0,560,75,1,0,0,0,561,563,7,3,0,0,562,564,7,27,0,0,563, - 562,1,0,0,0,563,564,1,0,0,0,564,566,1,0,0,0,565,567,3,68,28,0,566,565,1, - 0,0,0,567,568,1,0,0,0,568,566,1,0,0,0,568,569,1,0,0,0,569,77,1,0,0,0,570, - 571,5,64,0,0,571,79,1,0,0,0,572,573,5,96,0,0,573,81,1,0,0,0,574,578,8,28, - 0,0,575,576,5,96,0,0,576,578,5,96,0,0,577,574,1,0,0,0,577,575,1,0,0,0,578, - 83,1,0,0,0,579,580,5,95,0,0,580,85,1,0,0,0,581,585,3,70,29,0,582,585,3, - 68,28,0,583,585,3,84,36,0,584,581,1,0,0,0,584,582,1,0,0,0,584,583,1,0,0, - 0,585,87,1,0,0,0,586,591,5,34,0,0,587,590,3,72,30,0,588,590,3,74,31,0,589, - 587,1,0,0,0,589,588,1,0,0,0,590,593,1,0,0,0,591,589,1,0,0,0,591,592,1,0, - 0,0,592,594,1,0,0,0,593,591,1,0,0,0,594,616,5,34,0,0,595,596,5,34,0,0,596, - 597,5,34,0,0,597,598,5,34,0,0,598,602,1,0,0,0,599,601,8,21,0,0,600,599, - 1,0,0,0,601,604,1,0,0,0,602,603,1,0,0,0,602,600,1,0,0,0,603,605,1,0,0,0, - 604,602,1,0,0,0,605,606,5,34,0,0,606,607,5,34,0,0,607,608,5,34,0,0,608, - 610,1,0,0,0,609,611,5,34,0,0,610,609,1,0,0,0,610,611,1,0,0,0,611,613,1, - 0,0,0,612,614,5,34,0,0,613,612,1,0,0,0,613,614,1,0,0,0,614,616,1,0,0,0, - 615,586,1,0,0,0,615,595,1,0,0,0,616,89,1,0,0,0,617,619,3,68,28,0,618,617, - 1,0,0,0,619,620,1,0,0,0,620,618,1,0,0,0,620,621,1,0,0,0,621,91,1,0,0,0, - 622,624,3,68,28,0,623,622,1,0,0,0,624,625,1,0,0,0,625,623,1,0,0,0,625,626, - 1,0,0,0,626,627,1,0,0,0,627,631,3,108,48,0,628,630,3,68,28,0,629,628,1, - 0,0,0,630,633,1,0,0,0,631,629,1,0,0,0,631,632,1,0,0,0,632,665,1,0,0,0,633, - 631,1,0,0,0,634,636,3,108,48,0,635,637,3,68,28,0,636,635,1,0,0,0,637,638, - 1,0,0,0,638,636,1,0,0,0,638,639,1,0,0,0,639,665,1,0,0,0,640,642,3,68,28, - 0,641,640,1,0,0,0,642,643,1,0,0,0,643,641,1,0,0,0,643,644,1,0,0,0,644,652, - 1,0,0,0,645,649,3,108,48,0,646,648,3,68,28,0,647,646,1,0,0,0,648,651,1, - 0,0,0,649,647,1,0,0,0,649,650,1,0,0,0,650,653,1,0,0,0,651,649,1,0,0,0,652, - 645,1,0,0,0,652,653,1,0,0,0,653,654,1,0,0,0,654,655,3,76,32,0,655,665,1, - 0,0,0,656,658,3,108,48,0,657,659,3,68,28,0,658,657,1,0,0,0,659,660,1,0, - 0,0,660,658,1,0,0,0,660,661,1,0,0,0,661,662,1,0,0,0,662,663,3,76,32,0,663, - 665,1,0,0,0,664,623,1,0,0,0,664,634,1,0,0,0,664,641,1,0,0,0,664,656,1,0, - 0,0,665,93,1,0,0,0,666,667,7,29,0,0,667,668,7,30,0,0,668,95,1,0,0,0,669, - 670,7,12,0,0,670,671,7,9,0,0,671,672,7,0,0,0,672,97,1,0,0,0,673,674,7,12, - 0,0,674,675,7,2,0,0,675,676,7,4,0,0,676,99,1,0,0,0,677,678,5,61,0,0,678, - 101,1,0,0,0,679,680,5,58,0,0,680,681,5,58,0,0,681,103,1,0,0,0,682,683,5, - 44,0,0,683,105,1,0,0,0,684,685,7,0,0,0,685,686,7,3,0,0,686,687,7,2,0,0, - 687,688,7,4,0,0,688,107,1,0,0,0,689,690,5,46,0,0,690,109,1,0,0,0,691,692, - 7,15,0,0,692,693,7,12,0,0,693,694,7,13,0,0,694,695,7,2,0,0,695,696,7,3, - 0,0,696,111,1,0,0,0,697,698,7,15,0,0,698,699,7,1,0,0,699,700,7,6,0,0,700, - 701,7,2,0,0,701,702,7,5,0,0,702,113,1,0,0,0,703,704,7,13,0,0,704,705,7, - 12,0,0,705,706,7,2,0,0,706,707,7,5,0,0,707,115,1,0,0,0,708,709,5,40,0,0, - 709,117,1,0,0,0,710,711,7,1,0,0,711,712,7,9,0,0,712,119,1,0,0,0,713,714, - 7,1,0,0,714,715,7,2,0,0,715,121,1,0,0,0,716,717,7,13,0,0,717,718,7,1,0, - 0,718,719,7,18,0,0,719,720,7,3,0,0,720,123,1,0,0,0,721,722,7,9,0,0,722, - 723,7,7,0,0,723,724,7,5,0,0,724,125,1,0,0,0,725,726,7,9,0,0,726,727,7,31, - 0,0,727,728,7,13,0,0,728,729,7,13,0,0,729,127,1,0,0,0,730,731,7,9,0,0,731, - 732,7,31,0,0,732,733,7,13,0,0,733,734,7,13,0,0,734,735,7,2,0,0,735,129, - 1,0,0,0,736,737,7,7,0,0,737,738,7,6,0,0,738,131,1,0,0,0,739,740,5,63,0, - 0,740,133,1,0,0,0,741,742,7,6,0,0,742,743,7,13,0,0,743,744,7,1,0,0,744, - 745,7,18,0,0,745,746,7,3,0,0,746,135,1,0,0,0,747,748,5,41,0,0,748,137,1, - 0,0,0,749,750,7,5,0,0,750,751,7,6,0,0,751,752,7,31,0,0,752,753,7,3,0,0, - 753,139,1,0,0,0,754,755,5,61,0,0,755,756,5,61,0,0,756,141,1,0,0,0,757,758, - 5,61,0,0,758,759,5,126,0,0,759,143,1,0,0,0,760,761,5,33,0,0,761,762,5,61, - 0,0,762,145,1,0,0,0,763,764,5,60,0,0,764,147,1,0,0,0,765,766,5,60,0,0,766, - 767,5,61,0,0,767,149,1,0,0,0,768,769,5,62,0,0,769,151,1,0,0,0,770,771,5, - 62,0,0,771,772,5,61,0,0,772,153,1,0,0,0,773,774,5,43,0,0,774,155,1,0,0, - 0,775,776,5,45,0,0,776,157,1,0,0,0,777,778,5,42,0,0,778,159,1,0,0,0,779, - 780,5,47,0,0,780,161,1,0,0,0,781,782,5,37,0,0,782,163,1,0,0,0,783,784,5, - 91,0,0,784,785,1,0,0,0,785,786,6,76,0,0,786,787,6,76,0,0,787,165,1,0,0, - 0,788,789,5,93,0,0,789,790,1,0,0,0,790,791,6,77,13,0,791,792,6,77,13,0, - 792,167,1,0,0,0,793,797,3,70,29,0,794,796,3,86,37,0,795,794,1,0,0,0,796, - 799,1,0,0,0,797,795,1,0,0,0,797,798,1,0,0,0,798,810,1,0,0,0,799,797,1,0, - 0,0,800,803,3,84,36,0,801,803,3,78,33,0,802,800,1,0,0,0,802,801,1,0,0,0, - 803,805,1,0,0,0,804,806,3,86,37,0,805,804,1,0,0,0,806,807,1,0,0,0,807,805, - 1,0,0,0,807,808,1,0,0,0,808,810,1,0,0,0,809,793,1,0,0,0,809,802,1,0,0,0, - 810,169,1,0,0,0,811,813,3,80,34,0,812,814,3,82,35,0,813,812,1,0,0,0,814, - 815,1,0,0,0,815,813,1,0,0,0,815,816,1,0,0,0,816,817,1,0,0,0,817,818,3,80, - 34,0,818,171,1,0,0,0,819,820,3,170,79,0,820,173,1,0,0,0,821,822,3,50,19, - 0,822,823,1,0,0,0,823,824,6,81,9,0,824,175,1,0,0,0,825,826,3,52,20,0,826, - 827,1,0,0,0,827,828,6,82,9,0,828,177,1,0,0,0,829,830,3,54,21,0,830,831, - 1,0,0,0,831,832,6,83,9,0,832,179,1,0,0,0,833,834,3,66,27,0,834,835,1,0, - 0,0,835,836,6,84,12,0,836,837,6,84,13,0,837,181,1,0,0,0,838,839,3,164,76, - 0,839,840,1,0,0,0,840,841,6,85,10,0,841,183,1,0,0,0,842,843,3,166,77,0, - 843,844,1,0,0,0,844,845,6,86,14,0,845,185,1,0,0,0,846,847,3,104,46,0,847, - 848,1,0,0,0,848,849,6,87,15,0,849,187,1,0,0,0,850,851,3,100,44,0,851,852, - 1,0,0,0,852,853,6,88,16,0,853,189,1,0,0,0,854,855,3,88,38,0,855,856,1,0, - 0,0,856,857,6,89,17,0,857,191,1,0,0,0,858,859,7,7,0,0,859,860,7,8,0,0,860, - 861,7,5,0,0,861,862,7,1,0,0,862,863,7,7,0,0,863,864,7,9,0,0,864,865,7,2, - 0,0,865,193,1,0,0,0,866,867,7,16,0,0,867,868,7,3,0,0,868,869,7,5,0,0,869, - 870,7,12,0,0,870,871,7,0,0,0,871,872,7,12,0,0,872,873,7,5,0,0,873,874,7, - 12,0,0,874,195,1,0,0,0,875,879,8,32,0,0,876,877,5,47,0,0,877,879,8,33,0, - 0,878,875,1,0,0,0,878,876,1,0,0,0,879,197,1,0,0,0,880,882,3,196,92,0,881, - 880,1,0,0,0,882,883,1,0,0,0,883,881,1,0,0,0,883,884,1,0,0,0,884,199,1,0, - 0,0,885,886,3,50,19,0,886,887,1,0,0,0,887,888,6,94,9,0,888,201,1,0,0,0, - 889,890,3,52,20,0,890,891,1,0,0,0,891,892,6,95,9,0,892,203,1,0,0,0,893, - 894,3,54,21,0,894,895,1,0,0,0,895,896,6,96,9,0,896,205,1,0,0,0,897,898, - 3,66,27,0,898,899,1,0,0,0,899,900,6,97,12,0,900,901,6,97,13,0,901,207,1, - 0,0,0,902,903,3,108,48,0,903,904,1,0,0,0,904,905,6,98,18,0,905,209,1,0, - 0,0,906,907,3,104,46,0,907,908,1,0,0,0,908,909,6,99,15,0,909,211,1,0,0, - 0,910,915,3,70,29,0,911,915,3,68,28,0,912,915,3,84,36,0,913,915,3,158,73, - 0,914,910,1,0,0,0,914,911,1,0,0,0,914,912,1,0,0,0,914,913,1,0,0,0,915,213, - 1,0,0,0,916,919,3,70,29,0,917,919,3,158,73,0,918,916,1,0,0,0,918,917,1, - 0,0,0,919,923,1,0,0,0,920,922,3,212,100,0,921,920,1,0,0,0,922,925,1,0,0, - 0,923,921,1,0,0,0,923,924,1,0,0,0,924,936,1,0,0,0,925,923,1,0,0,0,926,929, - 3,84,36,0,927,929,3,78,33,0,928,926,1,0,0,0,928,927,1,0,0,0,929,931,1,0, - 0,0,930,932,3,212,100,0,931,930,1,0,0,0,932,933,1,0,0,0,933,931,1,0,0,0, - 933,934,1,0,0,0,934,936,1,0,0,0,935,918,1,0,0,0,935,928,1,0,0,0,936,215, - 1,0,0,0,937,940,3,214,101,0,938,940,3,170,79,0,939,937,1,0,0,0,939,938, - 1,0,0,0,940,941,1,0,0,0,941,939,1,0,0,0,941,942,1,0,0,0,942,217,1,0,0,0, - 943,944,3,50,19,0,944,945,1,0,0,0,945,946,6,103,9,0,946,219,1,0,0,0,947, - 948,3,52,20,0,948,949,1,0,0,0,949,950,6,104,9,0,950,221,1,0,0,0,951,952, - 3,54,21,0,952,953,1,0,0,0,953,954,6,105,9,0,954,223,1,0,0,0,955,956,3,66, - 27,0,956,957,1,0,0,0,957,958,6,106,12,0,958,959,6,106,13,0,959,225,1,0, - 0,0,960,961,3,100,44,0,961,962,1,0,0,0,962,963,6,107,16,0,963,227,1,0,0, - 0,964,965,3,104,46,0,965,966,1,0,0,0,966,967,6,108,15,0,967,229,1,0,0,0, - 968,969,3,108,48,0,969,970,1,0,0,0,970,971,6,109,18,0,971,231,1,0,0,0,972, - 973,7,12,0,0,973,974,7,2,0,0,974,233,1,0,0,0,975,976,3,216,102,0,976,977, - 1,0,0,0,977,978,6,111,19,0,978,235,1,0,0,0,979,980,3,50,19,0,980,981,1, - 0,0,0,981,982,6,112,9,0,982,237,1,0,0,0,983,984,3,52,20,0,984,985,1,0,0, - 0,985,986,6,113,9,0,986,239,1,0,0,0,987,988,3,54,21,0,988,989,1,0,0,0,989, - 990,6,114,9,0,990,241,1,0,0,0,991,992,3,66,27,0,992,993,1,0,0,0,993,994, - 6,115,12,0,994,995,6,115,13,0,995,243,1,0,0,0,996,997,3,164,76,0,997,998, - 1,0,0,0,998,999,6,116,10,0,999,1000,6,116,20,0,1000,245,1,0,0,0,1001,1002, - 7,7,0,0,1002,1003,7,9,0,0,1003,1004,1,0,0,0,1004,1005,6,117,21,0,1005,247, - 1,0,0,0,1006,1007,7,19,0,0,1007,1008,7,1,0,0,1008,1009,7,5,0,0,1009,1010, - 7,10,0,0,1010,1011,1,0,0,0,1011,1012,6,118,21,0,1012,249,1,0,0,0,1013,1014, - 8,34,0,0,1014,251,1,0,0,0,1015,1017,3,250,119,0,1016,1015,1,0,0,0,1017, - 1018,1,0,0,0,1018,1016,1,0,0,0,1018,1019,1,0,0,0,1019,1020,1,0,0,0,1020, - 1021,3,320,154,0,1021,1023,1,0,0,0,1022,1016,1,0,0,0,1022,1023,1,0,0,0, - 1023,1025,1,0,0,0,1024,1026,3,250,119,0,1025,1024,1,0,0,0,1026,1027,1,0, - 0,0,1027,1025,1,0,0,0,1027,1028,1,0,0,0,1028,253,1,0,0,0,1029,1030,3,172, - 80,0,1030,1031,1,0,0,0,1031,1032,6,121,22,0,1032,255,1,0,0,0,1033,1034, - 3,252,120,0,1034,1035,1,0,0,0,1035,1036,6,122,23,0,1036,257,1,0,0,0,1037, - 1038,3,50,19,0,1038,1039,1,0,0,0,1039,1040,6,123,9,0,1040,259,1,0,0,0,1041, - 1042,3,52,20,0,1042,1043,1,0,0,0,1043,1044,6,124,9,0,1044,261,1,0,0,0,1045, - 1046,3,54,21,0,1046,1047,1,0,0,0,1047,1048,6,125,9,0,1048,263,1,0,0,0,1049, - 1050,3,66,27,0,1050,1051,1,0,0,0,1051,1052,6,126,12,0,1052,1053,6,126,13, - 0,1053,1054,6,126,13,0,1054,265,1,0,0,0,1055,1056,3,100,44,0,1056,1057, - 1,0,0,0,1057,1058,6,127,16,0,1058,267,1,0,0,0,1059,1060,3,104,46,0,1060, - 1061,1,0,0,0,1061,1062,6,128,15,0,1062,269,1,0,0,0,1063,1064,3,108,48,0, - 1064,1065,1,0,0,0,1065,1066,6,129,18,0,1066,271,1,0,0,0,1067,1068,3,248, - 118,0,1068,1069,1,0,0,0,1069,1070,6,130,24,0,1070,273,1,0,0,0,1071,1072, - 3,216,102,0,1072,1073,1,0,0,0,1073,1074,6,131,19,0,1074,275,1,0,0,0,1075, - 1076,3,172,80,0,1076,1077,1,0,0,0,1077,1078,6,132,22,0,1078,277,1,0,0,0, - 1079,1080,3,50,19,0,1080,1081,1,0,0,0,1081,1082,6,133,9,0,1082,279,1,0, - 0,0,1083,1084,3,52,20,0,1084,1085,1,0,0,0,1085,1086,6,134,9,0,1086,281, - 1,0,0,0,1087,1088,3,54,21,0,1088,1089,1,0,0,0,1089,1090,6,135,9,0,1090, - 283,1,0,0,0,1091,1092,3,66,27,0,1092,1093,1,0,0,0,1093,1094,6,136,12,0, - 1094,1095,6,136,13,0,1095,285,1,0,0,0,1096,1097,3,108,48,0,1097,1098,1, - 0,0,0,1098,1099,6,137,18,0,1099,287,1,0,0,0,1100,1101,3,172,80,0,1101,1102, - 1,0,0,0,1102,1103,6,138,22,0,1103,289,1,0,0,0,1104,1105,3,168,78,0,1105, - 1106,1,0,0,0,1106,1107,6,139,25,0,1107,291,1,0,0,0,1108,1109,3,50,19,0, - 1109,1110,1,0,0,0,1110,1111,6,140,9,0,1111,293,1,0,0,0,1112,1113,3,52,20, - 0,1113,1114,1,0,0,0,1114,1115,6,141,9,0,1115,295,1,0,0,0,1116,1117,3,54, - 21,0,1117,1118,1,0,0,0,1118,1119,6,142,9,0,1119,297,1,0,0,0,1120,1121,3, - 66,27,0,1121,1122,1,0,0,0,1122,1123,6,143,12,0,1123,1124,6,143,13,0,1124, - 299,1,0,0,0,1125,1126,7,1,0,0,1126,1127,7,9,0,0,1127,1128,7,15,0,0,1128, - 1129,7,7,0,0,1129,301,1,0,0,0,1130,1131,3,50,19,0,1131,1132,1,0,0,0,1132, - 1133,6,145,9,0,1133,303,1,0,0,0,1134,1135,3,52,20,0,1135,1136,1,0,0,0,1136, - 1137,6,146,9,0,1137,305,1,0,0,0,1138,1139,3,54,21,0,1139,1140,1,0,0,0,1140, - 1141,6,147,9,0,1141,307,1,0,0,0,1142,1143,3,66,27,0,1143,1144,1,0,0,0,1144, - 1145,6,148,12,0,1145,1146,6,148,13,0,1146,309,1,0,0,0,1147,1148,7,15,0, - 0,1148,1149,7,31,0,0,1149,1150,7,9,0,0,1150,1151,7,4,0,0,1151,1152,7,5, - 0,0,1152,1153,7,1,0,0,1153,1154,7,7,0,0,1154,1155,7,9,0,0,1155,1156,7,2, - 0,0,1156,311,1,0,0,0,1157,1158,3,50,19,0,1158,1159,1,0,0,0,1159,1160,6, - 150,9,0,1160,313,1,0,0,0,1161,1162,3,52,20,0,1162,1163,1,0,0,0,1163,1164, - 6,151,9,0,1164,315,1,0,0,0,1165,1166,3,54,21,0,1166,1167,1,0,0,0,1167,1168, - 6,152,9,0,1168,317,1,0,0,0,1169,1170,3,166,77,0,1170,1171,1,0,0,0,1171, - 1172,6,153,14,0,1172,1173,6,153,13,0,1173,319,1,0,0,0,1174,1175,5,58,0, - 0,1175,321,1,0,0,0,1176,1182,3,78,33,0,1177,1182,3,68,28,0,1178,1182,3, - 108,48,0,1179,1182,3,70,29,0,1180,1182,3,84,36,0,1181,1176,1,0,0,0,1181, - 1177,1,0,0,0,1181,1178,1,0,0,0,1181,1179,1,0,0,0,1181,1180,1,0,0,0,1182, - 1183,1,0,0,0,1183,1181,1,0,0,0,1183,1184,1,0,0,0,1184,323,1,0,0,0,1185, - 1186,3,50,19,0,1186,1187,1,0,0,0,1187,1188,6,156,9,0,1188,325,1,0,0,0,1189, - 1190,3,52,20,0,1190,1191,1,0,0,0,1191,1192,6,157,9,0,1192,327,1,0,0,0,1193, - 1194,3,54,21,0,1194,1195,1,0,0,0,1195,1196,6,158,9,0,1196,329,1,0,0,0,58, - 0,1,2,3,4,5,6,7,8,9,10,11,483,493,497,500,509,511,522,563,568,577,584,589, - 591,602,610,613,615,620,625,631,638,643,649,652,660,664,797,802,807,809, - 815,878,883,914,918,923,928,933,935,939,941,1018,1022,1027,1181,1183,26, - 5,2,0,5,4,0,5,6,0,5,1,0,5,3,0,5,10,0,5,8,0,5,5,0,5,9,0,0,1,0,7,65,0,5,0, - 0,7,26,0,4,0,0,7,66,0,7,35,0,7,33,0,7,27,0,7,37,0,7,78,0,5,11,0,5,7,0,7, - 68,0,7,88,0,7,87,0,7,67,0]; + 7,152,2,153,7,153,2,154,7,154,2,155,7,155,2,156,7,156,2,157,7,157,1,0,1, + 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1, + 2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1,4,1, + 4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6,1,6,1, + 6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1, + 8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1, + 10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11, + 1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1, + 13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15, + 1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1, + 17,1,17,1,18,4,18,480,8,18,11,18,12,18,481,1,18,1,18,1,19,1,19,1,19,1,19, + 5,19,490,8,19,10,19,12,19,493,9,19,1,19,3,19,496,8,19,1,19,3,19,499,8,19, + 1,19,1,19,1,20,1,20,1,20,1,20,1,20,5,20,508,8,20,10,20,12,20,511,9,20,1, + 20,1,20,1,20,1,20,1,20,1,21,4,21,519,8,21,11,21,12,21,520,1,21,1,21,1,22, + 1,22,1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,23,1,24,1,24,1,24,1,24,1,25,1, + 25,1,25,1,25,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,28,1,28,1,29,1,29, + 1,30,1,30,1,30,1,31,1,31,1,32,1,32,3,32,562,8,32,1,32,4,32,565,8,32,11, + 32,12,32,566,1,33,1,33,1,34,1,34,1,35,1,35,1,35,3,35,576,8,35,1,36,1,36, + 1,37,1,37,1,37,3,37,583,8,37,1,38,1,38,1,38,5,38,588,8,38,10,38,12,38,591, + 9,38,1,38,1,38,1,38,1,38,1,38,1,38,5,38,599,8,38,10,38,12,38,602,9,38,1, + 38,1,38,1,38,1,38,1,38,3,38,609,8,38,1,38,3,38,612,8,38,3,38,614,8,38,1, + 39,4,39,617,8,39,11,39,12,39,618,1,40,4,40,622,8,40,11,40,12,40,623,1,40, + 1,40,5,40,628,8,40,10,40,12,40,631,9,40,1,40,1,40,4,40,635,8,40,11,40,12, + 40,636,1,40,4,40,640,8,40,11,40,12,40,641,1,40,1,40,5,40,646,8,40,10,40, + 12,40,649,9,40,3,40,651,8,40,1,40,1,40,1,40,1,40,4,40,657,8,40,11,40,12, + 40,658,1,40,1,40,3,40,663,8,40,1,41,1,41,1,41,1,42,1,42,1,42,1,42,1,43, + 1,43,1,43,1,43,1,44,1,44,1,45,1,45,1,45,1,46,1,46,1,47,1,47,1,47,1,47,1, + 47,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,49,1,50,1,50,1,50,1,50,1,50,1,50, + 1,51,1,51,1,51,1,51,1,51,1,52,1,52,1,53,1,53,1,53,1,54,1,54,1,54,1,55,1, + 55,1,55,1,55,1,55,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1,57,1,57,1,58,1,58, + 1,58,1,58,1,58,1,58,1,59,1,59,1,59,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1, + 61,1,62,1,62,1,63,1,63,1,63,1,63,1,63,1,64,1,64,1,64,1,65,1,65,1,65,1,66, + 1,66,1,66,1,67,1,67,1,68,1,68,1,68,1,69,1,69,1,70,1,70,1,70,1,71,1,71,1, + 72,1,72,1,73,1,73,1,74,1,74,1,75,1,75,1,76,1,76,1,76,1,76,1,76,1,77,1,77, + 1,77,1,77,1,77,1,78,1,78,5,78,794,8,78,10,78,12,78,797,9,78,1,78,1,78,3, + 78,801,8,78,1,78,4,78,804,8,78,11,78,12,78,805,3,78,808,8,78,1,79,1,79, + 4,79,812,8,79,11,79,12,79,813,1,79,1,79,1,80,1,80,1,81,1,81,1,81,1,81,1, + 82,1,82,1,82,1,82,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,1,85,1,85, + 1,85,1,85,1,86,1,86,1,86,1,86,1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1, + 89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,90,1,90,1,90,1,90,1,90,1,91,1,91, + 1,91,3,91,869,8,91,1,92,4,92,872,8,92,11,92,12,92,873,1,93,1,93,1,93,1, + 93,1,94,1,94,1,94,1,94,1,95,1,95,1,95,1,95,1,96,1,96,1,96,1,96,1,96,1,97, + 1,97,1,97,1,97,1,98,1,98,1,98,1,98,1,99,1,99,1,99,1,99,3,99,905,8,99,1, + 100,1,100,3,100,909,8,100,1,100,5,100,912,8,100,10,100,12,100,915,9,100, + 1,100,1,100,3,100,919,8,100,1,100,4,100,922,8,100,11,100,12,100,923,3,100, + 926,8,100,1,101,1,101,4,101,930,8,101,11,101,12,101,931,1,102,1,102,1,102, + 1,102,1,103,1,103,1,103,1,103,1,104,1,104,1,104,1,104,1,105,1,105,1,105, + 1,105,1,105,1,106,1,106,1,106,1,106,1,107,1,107,1,107,1,107,1,108,1,108, + 1,108,1,108,1,109,1,109,1,109,1,110,1,110,1,110,1,110,1,111,1,111,1,111, + 1,111,1,112,1,112,1,112,1,112,1,113,1,113,1,113,1,113,1,114,1,114,1,114, + 1,114,1,114,1,115,1,115,1,115,1,115,1,115,1,116,1,116,1,116,1,116,1,116, + 1,117,1,117,1,117,1,117,1,117,1,117,1,117,1,118,1,118,1,119,4,119,1007, + 8,119,11,119,12,119,1008,1,119,1,119,3,119,1013,8,119,1,119,4,119,1016, + 8,119,11,119,12,119,1017,1,120,1,120,1,120,1,120,1,121,1,121,1,121,1,121, + 1,122,1,122,1,122,1,122,1,123,1,123,1,123,1,123,1,124,1,124,1,124,1,124, + 1,125,1,125,1,125,1,125,1,125,1,125,1,126,1,126,1,126,1,126,1,127,1,127, + 1,127,1,127,1,128,1,128,1,128,1,128,1,129,1,129,1,129,1,129,1,130,1,130, + 1,130,1,130,1,131,1,131,1,131,1,131,1,132,1,132,1,132,1,132,1,133,1,133, + 1,133,1,133,1,134,1,134,1,134,1,134,1,135,1,135,1,135,1,135,1,135,1,136, + 1,136,1,136,1,136,1,137,1,137,1,137,1,137,1,138,1,138,1,138,1,138,1,139, + 1,139,1,139,1,139,1,140,1,140,1,140,1,140,1,141,1,141,1,141,1,141,1,142, + 1,142,1,142,1,142,1,142,1,143,1,143,1,143,1,143,1,143,1,144,1,144,1,144, + 1,144,1,145,1,145,1,145,1,145,1,146,1,146,1,146,1,146,1,147,1,147,1,147, + 1,147,1,147,1,148,1,148,1,148,1,148,1,148,1,148,1,148,1,148,1,148,1,148, + 1,149,1,149,1,149,1,149,1,150,1,150,1,150,1,150,1,151,1,151,1,151,1,151, + 1,152,1,152,1,152,1,152,1,152,1,153,1,153,1,154,1,154,1,154,1,154,1,154, + 4,154,1172,8,154,11,154,12,154,1173,1,155,1,155,1,155,1,155,1,156,1,156, + 1,156,1,156,1,157,1,157,1,157,1,157,2,509,600,0,158,12,1,14,2,16,3,18,4, + 20,5,22,6,24,7,26,8,28,9,30,10,32,11,34,12,36,13,38,14,40,15,42,16,44,17, + 46,18,48,19,50,20,52,21,54,22,56,0,58,0,60,23,62,24,64,25,66,26,68,0,70, + 0,72,0,74,0,76,0,78,0,80,0,82,0,84,0,86,0,88,27,90,28,92,29,94,30,96,31, + 98,32,100,33,102,34,104,35,106,36,108,37,110,38,112,39,114,40,116,41,118, + 42,120,43,122,44,124,45,126,46,128,47,130,48,132,49,134,50,136,51,138,52, + 140,53,142,54,144,55,146,56,148,57,150,58,152,59,154,60,156,61,158,62,160, + 63,162,64,164,65,166,66,168,67,170,0,172,68,174,69,176,70,178,71,180,0, + 182,0,184,0,186,0,188,0,190,0,192,72,194,0,196,73,198,74,200,75,202,76, + 204,0,206,0,208,0,210,0,212,0,214,77,216,78,218,79,220,80,222,0,224,0,226, + 0,228,0,230,81,232,0,234,82,236,83,238,84,240,0,242,0,244,85,246,86,248, + 0,250,87,252,0,254,0,256,88,258,89,260,90,262,0,264,0,266,0,268,0,270,0, + 272,0,274,0,276,91,278,92,280,93,282,0,284,0,286,0,288,0,290,94,292,95, + 294,96,296,0,298,97,300,98,302,99,304,100,306,0,308,101,310,102,312,103, + 314,104,316,0,318,105,320,106,322,107,324,108,326,109,12,0,1,2,3,4,5,6, + 7,8,9,10,11,35,2,0,68,68,100,100,2,0,73,73,105,105,2,0,83,83,115,115,2, + 0,69,69,101,101,2,0,67,67,99,99,2,0,84,84,116,116,2,0,82,82,114,114,2,0, + 79,79,111,111,2,0,80,80,112,112,2,0,78,78,110,110,2,0,72,72,104,104,2,0, + 86,86,118,118,2,0,65,65,97,97,2,0,76,76,108,108,2,0,88,88,120,120,2,0,70, + 70,102,102,2,0,77,77,109,109,2,0,71,71,103,103,2,0,75,75,107,107,2,0,87, + 87,119,119,6,0,9,10,13,13,32,32,47,47,91,91,93,93,2,0,10,10,13,13,3,0,9, + 10,13,13,32,32,1,0,48,57,2,0,65,90,97,122,8,0,34,34,78,78,82,82,84,84,92, + 92,110,110,114,114,116,116,4,0,10,10,13,13,34,34,92,92,2,0,43,43,45,45, + 1,0,96,96,2,0,66,66,98,98,2,0,89,89,121,121,2,0,85,85,117,117,10,0,9,10, + 13,13,32,32,44,44,47,47,61,61,91,91,93,93,96,96,124,124,2,0,42,42,47,47, + 11,0,9,10,13,13,32,32,34,35,44,44,47,47,58,58,60,60,62,63,92,92,124,124, + 1214,0,12,1,0,0,0,0,14,1,0,0,0,0,16,1,0,0,0,0,18,1,0,0,0,0,20,1,0,0,0,0, + 22,1,0,0,0,0,24,1,0,0,0,0,26,1,0,0,0,0,28,1,0,0,0,0,30,1,0,0,0,0,32,1,0, + 0,0,0,34,1,0,0,0,0,36,1,0,0,0,0,38,1,0,0,0,0,40,1,0,0,0,0,42,1,0,0,0,0, + 44,1,0,0,0,0,46,1,0,0,0,0,48,1,0,0,0,0,50,1,0,0,0,0,52,1,0,0,0,0,54,1,0, + 0,0,1,56,1,0,0,0,1,58,1,0,0,0,1,60,1,0,0,0,1,62,1,0,0,0,1,64,1,0,0,0,2, + 66,1,0,0,0,2,88,1,0,0,0,2,90,1,0,0,0,2,92,1,0,0,0,2,94,1,0,0,0,2,96,1,0, + 0,0,2,98,1,0,0,0,2,100,1,0,0,0,2,102,1,0,0,0,2,104,1,0,0,0,2,106,1,0,0, + 0,2,108,1,0,0,0,2,110,1,0,0,0,2,112,1,0,0,0,2,114,1,0,0,0,2,116,1,0,0,0, + 2,118,1,0,0,0,2,120,1,0,0,0,2,122,1,0,0,0,2,124,1,0,0,0,2,126,1,0,0,0,2, + 128,1,0,0,0,2,130,1,0,0,0,2,132,1,0,0,0,2,134,1,0,0,0,2,136,1,0,0,0,2,138, + 1,0,0,0,2,140,1,0,0,0,2,142,1,0,0,0,2,144,1,0,0,0,2,146,1,0,0,0,2,148,1, + 0,0,0,2,150,1,0,0,0,2,152,1,0,0,0,2,154,1,0,0,0,2,156,1,0,0,0,2,158,1,0, + 0,0,2,160,1,0,0,0,2,162,1,0,0,0,2,164,1,0,0,0,2,166,1,0,0,0,2,168,1,0,0, + 0,2,172,1,0,0,0,2,174,1,0,0,0,2,176,1,0,0,0,2,178,1,0,0,0,3,180,1,0,0,0, + 3,182,1,0,0,0,3,184,1,0,0,0,3,186,1,0,0,0,3,188,1,0,0,0,3,190,1,0,0,0,3, + 192,1,0,0,0,3,196,1,0,0,0,3,198,1,0,0,0,3,200,1,0,0,0,3,202,1,0,0,0,4,204, + 1,0,0,0,4,206,1,0,0,0,4,208,1,0,0,0,4,214,1,0,0,0,4,216,1,0,0,0,4,218,1, + 0,0,0,4,220,1,0,0,0,5,222,1,0,0,0,5,224,1,0,0,0,5,226,1,0,0,0,5,228,1,0, + 0,0,5,230,1,0,0,0,5,232,1,0,0,0,5,234,1,0,0,0,5,236,1,0,0,0,5,238,1,0,0, + 0,6,240,1,0,0,0,6,242,1,0,0,0,6,244,1,0,0,0,6,246,1,0,0,0,6,250,1,0,0,0, + 6,252,1,0,0,0,6,254,1,0,0,0,6,256,1,0,0,0,6,258,1,0,0,0,6,260,1,0,0,0,7, + 262,1,0,0,0,7,264,1,0,0,0,7,266,1,0,0,0,7,268,1,0,0,0,7,270,1,0,0,0,7,272, + 1,0,0,0,7,274,1,0,0,0,7,276,1,0,0,0,7,278,1,0,0,0,7,280,1,0,0,0,8,282,1, + 0,0,0,8,284,1,0,0,0,8,286,1,0,0,0,8,288,1,0,0,0,8,290,1,0,0,0,8,292,1,0, + 0,0,8,294,1,0,0,0,9,296,1,0,0,0,9,298,1,0,0,0,9,300,1,0,0,0,9,302,1,0,0, + 0,9,304,1,0,0,0,10,306,1,0,0,0,10,308,1,0,0,0,10,310,1,0,0,0,10,312,1,0, + 0,0,10,314,1,0,0,0,11,316,1,0,0,0,11,318,1,0,0,0,11,320,1,0,0,0,11,322, + 1,0,0,0,11,324,1,0,0,0,11,326,1,0,0,0,12,328,1,0,0,0,14,338,1,0,0,0,16, + 345,1,0,0,0,18,354,1,0,0,0,20,361,1,0,0,0,22,371,1,0,0,0,24,378,1,0,0,0, + 26,385,1,0,0,0,28,399,1,0,0,0,30,406,1,0,0,0,32,414,1,0,0,0,34,421,1,0, + 0,0,36,433,1,0,0,0,38,442,1,0,0,0,40,448,1,0,0,0,42,455,1,0,0,0,44,462, + 1,0,0,0,46,470,1,0,0,0,48,479,1,0,0,0,50,485,1,0,0,0,52,502,1,0,0,0,54, + 518,1,0,0,0,56,524,1,0,0,0,58,529,1,0,0,0,60,534,1,0,0,0,62,538,1,0,0,0, + 64,542,1,0,0,0,66,546,1,0,0,0,68,550,1,0,0,0,70,552,1,0,0,0,72,554,1,0, + 0,0,74,557,1,0,0,0,76,559,1,0,0,0,78,568,1,0,0,0,80,570,1,0,0,0,82,575, + 1,0,0,0,84,577,1,0,0,0,86,582,1,0,0,0,88,613,1,0,0,0,90,616,1,0,0,0,92, + 662,1,0,0,0,94,664,1,0,0,0,96,667,1,0,0,0,98,671,1,0,0,0,100,675,1,0,0, + 0,102,677,1,0,0,0,104,680,1,0,0,0,106,682,1,0,0,0,108,687,1,0,0,0,110,689, + 1,0,0,0,112,695,1,0,0,0,114,701,1,0,0,0,116,706,1,0,0,0,118,708,1,0,0,0, + 120,711,1,0,0,0,122,714,1,0,0,0,124,719,1,0,0,0,126,723,1,0,0,0,128,728, + 1,0,0,0,130,734,1,0,0,0,132,737,1,0,0,0,134,739,1,0,0,0,136,745,1,0,0,0, + 138,747,1,0,0,0,140,752,1,0,0,0,142,755,1,0,0,0,144,758,1,0,0,0,146,761, + 1,0,0,0,148,763,1,0,0,0,150,766,1,0,0,0,152,768,1,0,0,0,154,771,1,0,0,0, + 156,773,1,0,0,0,158,775,1,0,0,0,160,777,1,0,0,0,162,779,1,0,0,0,164,781, + 1,0,0,0,166,786,1,0,0,0,168,807,1,0,0,0,170,809,1,0,0,0,172,817,1,0,0,0, + 174,819,1,0,0,0,176,823,1,0,0,0,178,827,1,0,0,0,180,831,1,0,0,0,182,836, + 1,0,0,0,184,840,1,0,0,0,186,844,1,0,0,0,188,848,1,0,0,0,190,852,1,0,0,0, + 192,856,1,0,0,0,194,868,1,0,0,0,196,871,1,0,0,0,198,875,1,0,0,0,200,879, + 1,0,0,0,202,883,1,0,0,0,204,887,1,0,0,0,206,892,1,0,0,0,208,896,1,0,0,0, + 210,904,1,0,0,0,212,925,1,0,0,0,214,929,1,0,0,0,216,933,1,0,0,0,218,937, + 1,0,0,0,220,941,1,0,0,0,222,945,1,0,0,0,224,950,1,0,0,0,226,954,1,0,0,0, + 228,958,1,0,0,0,230,962,1,0,0,0,232,965,1,0,0,0,234,969,1,0,0,0,236,973, + 1,0,0,0,238,977,1,0,0,0,240,981,1,0,0,0,242,986,1,0,0,0,244,991,1,0,0,0, + 246,996,1,0,0,0,248,1003,1,0,0,0,250,1012,1,0,0,0,252,1019,1,0,0,0,254, + 1023,1,0,0,0,256,1027,1,0,0,0,258,1031,1,0,0,0,260,1035,1,0,0,0,262,1039, + 1,0,0,0,264,1045,1,0,0,0,266,1049,1,0,0,0,268,1053,1,0,0,0,270,1057,1,0, + 0,0,272,1061,1,0,0,0,274,1065,1,0,0,0,276,1069,1,0,0,0,278,1073,1,0,0,0, + 280,1077,1,0,0,0,282,1081,1,0,0,0,284,1086,1,0,0,0,286,1090,1,0,0,0,288, + 1094,1,0,0,0,290,1098,1,0,0,0,292,1102,1,0,0,0,294,1106,1,0,0,0,296,1110, + 1,0,0,0,298,1115,1,0,0,0,300,1120,1,0,0,0,302,1124,1,0,0,0,304,1128,1,0, + 0,0,306,1132,1,0,0,0,308,1137,1,0,0,0,310,1147,1,0,0,0,312,1151,1,0,0,0, + 314,1155,1,0,0,0,316,1159,1,0,0,0,318,1164,1,0,0,0,320,1171,1,0,0,0,322, + 1175,1,0,0,0,324,1179,1,0,0,0,326,1183,1,0,0,0,328,329,7,0,0,0,329,330, + 7,1,0,0,330,331,7,2,0,0,331,332,7,2,0,0,332,333,7,3,0,0,333,334,7,4,0,0, + 334,335,7,5,0,0,335,336,1,0,0,0,336,337,6,0,0,0,337,13,1,0,0,0,338,339, + 7,0,0,0,339,340,7,6,0,0,340,341,7,7,0,0,341,342,7,8,0,0,342,343,1,0,0,0, + 343,344,6,1,1,0,344,15,1,0,0,0,345,346,7,3,0,0,346,347,7,9,0,0,347,348, + 7,6,0,0,348,349,7,1,0,0,349,350,7,4,0,0,350,351,7,10,0,0,351,352,1,0,0, + 0,352,353,6,2,2,0,353,17,1,0,0,0,354,355,7,3,0,0,355,356,7,11,0,0,356,357, + 7,12,0,0,357,358,7,13,0,0,358,359,1,0,0,0,359,360,6,3,0,0,360,19,1,0,0, + 0,361,362,7,3,0,0,362,363,7,14,0,0,363,364,7,8,0,0,364,365,7,13,0,0,365, + 366,7,12,0,0,366,367,7,1,0,0,367,368,7,9,0,0,368,369,1,0,0,0,369,370,6, + 4,3,0,370,21,1,0,0,0,371,372,7,15,0,0,372,373,7,6,0,0,373,374,7,7,0,0,374, + 375,7,16,0,0,375,376,1,0,0,0,376,377,6,5,4,0,377,23,1,0,0,0,378,379,7,17, + 0,0,379,380,7,6,0,0,380,381,7,7,0,0,381,382,7,18,0,0,382,383,1,0,0,0,383, + 384,6,6,0,0,384,25,1,0,0,0,385,386,7,1,0,0,386,387,7,9,0,0,387,388,7,13, + 0,0,388,389,7,1,0,0,389,390,7,9,0,0,390,391,7,3,0,0,391,392,7,2,0,0,392, + 393,7,5,0,0,393,394,7,12,0,0,394,395,7,5,0,0,395,396,7,2,0,0,396,397,1, + 0,0,0,397,398,6,7,0,0,398,27,1,0,0,0,399,400,7,18,0,0,400,401,7,3,0,0,401, + 402,7,3,0,0,402,403,7,8,0,0,403,404,1,0,0,0,404,405,6,8,1,0,405,29,1,0, + 0,0,406,407,7,13,0,0,407,408,7,1,0,0,408,409,7,16,0,0,409,410,7,1,0,0,410, + 411,7,5,0,0,411,412,1,0,0,0,412,413,6,9,0,0,413,31,1,0,0,0,414,415,7,16, + 0,0,415,416,7,3,0,0,416,417,7,5,0,0,417,418,7,12,0,0,418,419,1,0,0,0,419, + 420,6,10,5,0,420,33,1,0,0,0,421,422,7,16,0,0,422,423,7,11,0,0,423,424,5, + 95,0,0,424,425,7,3,0,0,425,426,7,14,0,0,426,427,7,8,0,0,427,428,7,12,0, + 0,428,429,7,9,0,0,429,430,7,0,0,0,430,431,1,0,0,0,431,432,6,11,6,0,432, + 35,1,0,0,0,433,434,7,6,0,0,434,435,7,3,0,0,435,436,7,9,0,0,436,437,7,12, + 0,0,437,438,7,16,0,0,438,439,7,3,0,0,439,440,1,0,0,0,440,441,6,12,7,0,441, + 37,1,0,0,0,442,443,7,6,0,0,443,444,7,7,0,0,444,445,7,19,0,0,445,446,1,0, + 0,0,446,447,6,13,0,0,447,39,1,0,0,0,448,449,7,2,0,0,449,450,7,10,0,0,450, + 451,7,7,0,0,451,452,7,19,0,0,452,453,1,0,0,0,453,454,6,14,8,0,454,41,1, + 0,0,0,455,456,7,2,0,0,456,457,7,7,0,0,457,458,7,6,0,0,458,459,7,5,0,0,459, + 460,1,0,0,0,460,461,6,15,0,0,461,43,1,0,0,0,462,463,7,2,0,0,463,464,7,5, + 0,0,464,465,7,12,0,0,465,466,7,5,0,0,466,467,7,2,0,0,467,468,1,0,0,0,468, + 469,6,16,0,0,469,45,1,0,0,0,470,471,7,19,0,0,471,472,7,10,0,0,472,473,7, + 3,0,0,473,474,7,6,0,0,474,475,7,3,0,0,475,476,1,0,0,0,476,477,6,17,0,0, + 477,47,1,0,0,0,478,480,8,20,0,0,479,478,1,0,0,0,480,481,1,0,0,0,481,479, + 1,0,0,0,481,482,1,0,0,0,482,483,1,0,0,0,483,484,6,18,0,0,484,49,1,0,0,0, + 485,486,5,47,0,0,486,487,5,47,0,0,487,491,1,0,0,0,488,490,8,21,0,0,489, + 488,1,0,0,0,490,493,1,0,0,0,491,489,1,0,0,0,491,492,1,0,0,0,492,495,1,0, + 0,0,493,491,1,0,0,0,494,496,5,13,0,0,495,494,1,0,0,0,495,496,1,0,0,0,496, + 498,1,0,0,0,497,499,5,10,0,0,498,497,1,0,0,0,498,499,1,0,0,0,499,500,1, + 0,0,0,500,501,6,19,9,0,501,51,1,0,0,0,502,503,5,47,0,0,503,504,5,42,0,0, + 504,509,1,0,0,0,505,508,3,52,20,0,506,508,9,0,0,0,507,505,1,0,0,0,507,506, + 1,0,0,0,508,511,1,0,0,0,509,510,1,0,0,0,509,507,1,0,0,0,510,512,1,0,0,0, + 511,509,1,0,0,0,512,513,5,42,0,0,513,514,5,47,0,0,514,515,1,0,0,0,515,516, + 6,20,9,0,516,53,1,0,0,0,517,519,7,22,0,0,518,517,1,0,0,0,519,520,1,0,0, + 0,520,518,1,0,0,0,520,521,1,0,0,0,521,522,1,0,0,0,522,523,6,21,9,0,523, + 55,1,0,0,0,524,525,3,164,76,0,525,526,1,0,0,0,526,527,6,22,10,0,527,528, + 6,22,11,0,528,57,1,0,0,0,529,530,3,66,27,0,530,531,1,0,0,0,531,532,6,23, + 12,0,532,533,6,23,13,0,533,59,1,0,0,0,534,535,3,54,21,0,535,536,1,0,0,0, + 536,537,6,24,9,0,537,61,1,0,0,0,538,539,3,50,19,0,539,540,1,0,0,0,540,541, + 6,25,9,0,541,63,1,0,0,0,542,543,3,52,20,0,543,544,1,0,0,0,544,545,6,26, + 9,0,545,65,1,0,0,0,546,547,5,124,0,0,547,548,1,0,0,0,548,549,6,27,13,0, + 549,67,1,0,0,0,550,551,7,23,0,0,551,69,1,0,0,0,552,553,7,24,0,0,553,71, + 1,0,0,0,554,555,5,92,0,0,555,556,7,25,0,0,556,73,1,0,0,0,557,558,8,26,0, + 0,558,75,1,0,0,0,559,561,7,3,0,0,560,562,7,27,0,0,561,560,1,0,0,0,561,562, + 1,0,0,0,562,564,1,0,0,0,563,565,3,68,28,0,564,563,1,0,0,0,565,566,1,0,0, + 0,566,564,1,0,0,0,566,567,1,0,0,0,567,77,1,0,0,0,568,569,5,64,0,0,569,79, + 1,0,0,0,570,571,5,96,0,0,571,81,1,0,0,0,572,576,8,28,0,0,573,574,5,96,0, + 0,574,576,5,96,0,0,575,572,1,0,0,0,575,573,1,0,0,0,576,83,1,0,0,0,577,578, + 5,95,0,0,578,85,1,0,0,0,579,583,3,70,29,0,580,583,3,68,28,0,581,583,3,84, + 36,0,582,579,1,0,0,0,582,580,1,0,0,0,582,581,1,0,0,0,583,87,1,0,0,0,584, + 589,5,34,0,0,585,588,3,72,30,0,586,588,3,74,31,0,587,585,1,0,0,0,587,586, + 1,0,0,0,588,591,1,0,0,0,589,587,1,0,0,0,589,590,1,0,0,0,590,592,1,0,0,0, + 591,589,1,0,0,0,592,614,5,34,0,0,593,594,5,34,0,0,594,595,5,34,0,0,595, + 596,5,34,0,0,596,600,1,0,0,0,597,599,8,21,0,0,598,597,1,0,0,0,599,602,1, + 0,0,0,600,601,1,0,0,0,600,598,1,0,0,0,601,603,1,0,0,0,602,600,1,0,0,0,603, + 604,5,34,0,0,604,605,5,34,0,0,605,606,5,34,0,0,606,608,1,0,0,0,607,609, + 5,34,0,0,608,607,1,0,0,0,608,609,1,0,0,0,609,611,1,0,0,0,610,612,5,34,0, + 0,611,610,1,0,0,0,611,612,1,0,0,0,612,614,1,0,0,0,613,584,1,0,0,0,613,593, + 1,0,0,0,614,89,1,0,0,0,615,617,3,68,28,0,616,615,1,0,0,0,617,618,1,0,0, + 0,618,616,1,0,0,0,618,619,1,0,0,0,619,91,1,0,0,0,620,622,3,68,28,0,621, + 620,1,0,0,0,622,623,1,0,0,0,623,621,1,0,0,0,623,624,1,0,0,0,624,625,1,0, + 0,0,625,629,3,108,48,0,626,628,3,68,28,0,627,626,1,0,0,0,628,631,1,0,0, + 0,629,627,1,0,0,0,629,630,1,0,0,0,630,663,1,0,0,0,631,629,1,0,0,0,632,634, + 3,108,48,0,633,635,3,68,28,0,634,633,1,0,0,0,635,636,1,0,0,0,636,634,1, + 0,0,0,636,637,1,0,0,0,637,663,1,0,0,0,638,640,3,68,28,0,639,638,1,0,0,0, + 640,641,1,0,0,0,641,639,1,0,0,0,641,642,1,0,0,0,642,650,1,0,0,0,643,647, + 3,108,48,0,644,646,3,68,28,0,645,644,1,0,0,0,646,649,1,0,0,0,647,645,1, + 0,0,0,647,648,1,0,0,0,648,651,1,0,0,0,649,647,1,0,0,0,650,643,1,0,0,0,650, + 651,1,0,0,0,651,652,1,0,0,0,652,653,3,76,32,0,653,663,1,0,0,0,654,656,3, + 108,48,0,655,657,3,68,28,0,656,655,1,0,0,0,657,658,1,0,0,0,658,656,1,0, + 0,0,658,659,1,0,0,0,659,660,1,0,0,0,660,661,3,76,32,0,661,663,1,0,0,0,662, + 621,1,0,0,0,662,632,1,0,0,0,662,639,1,0,0,0,662,654,1,0,0,0,663,93,1,0, + 0,0,664,665,7,29,0,0,665,666,7,30,0,0,666,95,1,0,0,0,667,668,7,12,0,0,668, + 669,7,9,0,0,669,670,7,0,0,0,670,97,1,0,0,0,671,672,7,12,0,0,672,673,7,2, + 0,0,673,674,7,4,0,0,674,99,1,0,0,0,675,676,5,61,0,0,676,101,1,0,0,0,677, + 678,5,58,0,0,678,679,5,58,0,0,679,103,1,0,0,0,680,681,5,44,0,0,681,105, + 1,0,0,0,682,683,7,0,0,0,683,684,7,3,0,0,684,685,7,2,0,0,685,686,7,4,0,0, + 686,107,1,0,0,0,687,688,5,46,0,0,688,109,1,0,0,0,689,690,7,15,0,0,690,691, + 7,12,0,0,691,692,7,13,0,0,692,693,7,2,0,0,693,694,7,3,0,0,694,111,1,0,0, + 0,695,696,7,15,0,0,696,697,7,1,0,0,697,698,7,6,0,0,698,699,7,2,0,0,699, + 700,7,5,0,0,700,113,1,0,0,0,701,702,7,13,0,0,702,703,7,12,0,0,703,704,7, + 2,0,0,704,705,7,5,0,0,705,115,1,0,0,0,706,707,5,40,0,0,707,117,1,0,0,0, + 708,709,7,1,0,0,709,710,7,9,0,0,710,119,1,0,0,0,711,712,7,1,0,0,712,713, + 7,2,0,0,713,121,1,0,0,0,714,715,7,13,0,0,715,716,7,1,0,0,716,717,7,18,0, + 0,717,718,7,3,0,0,718,123,1,0,0,0,719,720,7,9,0,0,720,721,7,7,0,0,721,722, + 7,5,0,0,722,125,1,0,0,0,723,724,7,9,0,0,724,725,7,31,0,0,725,726,7,13,0, + 0,726,727,7,13,0,0,727,127,1,0,0,0,728,729,7,9,0,0,729,730,7,31,0,0,730, + 731,7,13,0,0,731,732,7,13,0,0,732,733,7,2,0,0,733,129,1,0,0,0,734,735,7, + 7,0,0,735,736,7,6,0,0,736,131,1,0,0,0,737,738,5,63,0,0,738,133,1,0,0,0, + 739,740,7,6,0,0,740,741,7,13,0,0,741,742,7,1,0,0,742,743,7,18,0,0,743,744, + 7,3,0,0,744,135,1,0,0,0,745,746,5,41,0,0,746,137,1,0,0,0,747,748,7,5,0, + 0,748,749,7,6,0,0,749,750,7,31,0,0,750,751,7,3,0,0,751,139,1,0,0,0,752, + 753,5,61,0,0,753,754,5,61,0,0,754,141,1,0,0,0,755,756,5,61,0,0,756,757, + 5,126,0,0,757,143,1,0,0,0,758,759,5,33,0,0,759,760,5,61,0,0,760,145,1,0, + 0,0,761,762,5,60,0,0,762,147,1,0,0,0,763,764,5,60,0,0,764,765,5,61,0,0, + 765,149,1,0,0,0,766,767,5,62,0,0,767,151,1,0,0,0,768,769,5,62,0,0,769,770, + 5,61,0,0,770,153,1,0,0,0,771,772,5,43,0,0,772,155,1,0,0,0,773,774,5,45, + 0,0,774,157,1,0,0,0,775,776,5,42,0,0,776,159,1,0,0,0,777,778,5,47,0,0,778, + 161,1,0,0,0,779,780,5,37,0,0,780,163,1,0,0,0,781,782,5,91,0,0,782,783,1, + 0,0,0,783,784,6,76,0,0,784,785,6,76,0,0,785,165,1,0,0,0,786,787,5,93,0, + 0,787,788,1,0,0,0,788,789,6,77,13,0,789,790,6,77,13,0,790,167,1,0,0,0,791, + 795,3,70,29,0,792,794,3,86,37,0,793,792,1,0,0,0,794,797,1,0,0,0,795,793, + 1,0,0,0,795,796,1,0,0,0,796,808,1,0,0,0,797,795,1,0,0,0,798,801,3,84,36, + 0,799,801,3,78,33,0,800,798,1,0,0,0,800,799,1,0,0,0,801,803,1,0,0,0,802, + 804,3,86,37,0,803,802,1,0,0,0,804,805,1,0,0,0,805,803,1,0,0,0,805,806,1, + 0,0,0,806,808,1,0,0,0,807,791,1,0,0,0,807,800,1,0,0,0,808,169,1,0,0,0,809, + 811,3,80,34,0,810,812,3,82,35,0,811,810,1,0,0,0,812,813,1,0,0,0,813,811, + 1,0,0,0,813,814,1,0,0,0,814,815,1,0,0,0,815,816,3,80,34,0,816,171,1,0,0, + 0,817,818,3,170,79,0,818,173,1,0,0,0,819,820,3,50,19,0,820,821,1,0,0,0, + 821,822,6,81,9,0,822,175,1,0,0,0,823,824,3,52,20,0,824,825,1,0,0,0,825, + 826,6,82,9,0,826,177,1,0,0,0,827,828,3,54,21,0,828,829,1,0,0,0,829,830, + 6,83,9,0,830,179,1,0,0,0,831,832,3,66,27,0,832,833,1,0,0,0,833,834,6,84, + 12,0,834,835,6,84,13,0,835,181,1,0,0,0,836,837,3,164,76,0,837,838,1,0,0, + 0,838,839,6,85,10,0,839,183,1,0,0,0,840,841,3,166,77,0,841,842,1,0,0,0, + 842,843,6,86,14,0,843,185,1,0,0,0,844,845,3,104,46,0,845,846,1,0,0,0,846, + 847,6,87,15,0,847,187,1,0,0,0,848,849,3,100,44,0,849,850,1,0,0,0,850,851, + 6,88,16,0,851,189,1,0,0,0,852,853,3,88,38,0,853,854,1,0,0,0,854,855,6,89, + 17,0,855,191,1,0,0,0,856,857,7,16,0,0,857,858,7,3,0,0,858,859,7,5,0,0,859, + 860,7,12,0,0,860,861,7,0,0,0,861,862,7,12,0,0,862,863,7,5,0,0,863,864,7, + 12,0,0,864,193,1,0,0,0,865,869,8,32,0,0,866,867,5,47,0,0,867,869,8,33,0, + 0,868,865,1,0,0,0,868,866,1,0,0,0,869,195,1,0,0,0,870,872,3,194,91,0,871, + 870,1,0,0,0,872,873,1,0,0,0,873,871,1,0,0,0,873,874,1,0,0,0,874,197,1,0, + 0,0,875,876,3,50,19,0,876,877,1,0,0,0,877,878,6,93,9,0,878,199,1,0,0,0, + 879,880,3,52,20,0,880,881,1,0,0,0,881,882,6,94,9,0,882,201,1,0,0,0,883, + 884,3,54,21,0,884,885,1,0,0,0,885,886,6,95,9,0,886,203,1,0,0,0,887,888, + 3,66,27,0,888,889,1,0,0,0,889,890,6,96,12,0,890,891,6,96,13,0,891,205,1, + 0,0,0,892,893,3,108,48,0,893,894,1,0,0,0,894,895,6,97,18,0,895,207,1,0, + 0,0,896,897,3,104,46,0,897,898,1,0,0,0,898,899,6,98,15,0,899,209,1,0,0, + 0,900,905,3,70,29,0,901,905,3,68,28,0,902,905,3,84,36,0,903,905,3,158,73, + 0,904,900,1,0,0,0,904,901,1,0,0,0,904,902,1,0,0,0,904,903,1,0,0,0,905,211, + 1,0,0,0,906,909,3,70,29,0,907,909,3,158,73,0,908,906,1,0,0,0,908,907,1, + 0,0,0,909,913,1,0,0,0,910,912,3,210,99,0,911,910,1,0,0,0,912,915,1,0,0, + 0,913,911,1,0,0,0,913,914,1,0,0,0,914,926,1,0,0,0,915,913,1,0,0,0,916,919, + 3,84,36,0,917,919,3,78,33,0,918,916,1,0,0,0,918,917,1,0,0,0,919,921,1,0, + 0,0,920,922,3,210,99,0,921,920,1,0,0,0,922,923,1,0,0,0,923,921,1,0,0,0, + 923,924,1,0,0,0,924,926,1,0,0,0,925,908,1,0,0,0,925,918,1,0,0,0,926,213, + 1,0,0,0,927,930,3,212,100,0,928,930,3,170,79,0,929,927,1,0,0,0,929,928, + 1,0,0,0,930,931,1,0,0,0,931,929,1,0,0,0,931,932,1,0,0,0,932,215,1,0,0,0, + 933,934,3,50,19,0,934,935,1,0,0,0,935,936,6,102,9,0,936,217,1,0,0,0,937, + 938,3,52,20,0,938,939,1,0,0,0,939,940,6,103,9,0,940,219,1,0,0,0,941,942, + 3,54,21,0,942,943,1,0,0,0,943,944,6,104,9,0,944,221,1,0,0,0,945,946,3,66, + 27,0,946,947,1,0,0,0,947,948,6,105,12,0,948,949,6,105,13,0,949,223,1,0, + 0,0,950,951,3,100,44,0,951,952,1,0,0,0,952,953,6,106,16,0,953,225,1,0,0, + 0,954,955,3,104,46,0,955,956,1,0,0,0,956,957,6,107,15,0,957,227,1,0,0,0, + 958,959,3,108,48,0,959,960,1,0,0,0,960,961,6,108,18,0,961,229,1,0,0,0,962, + 963,7,12,0,0,963,964,7,2,0,0,964,231,1,0,0,0,965,966,3,214,101,0,966,967, + 1,0,0,0,967,968,6,110,19,0,968,233,1,0,0,0,969,970,3,50,19,0,970,971,1, + 0,0,0,971,972,6,111,9,0,972,235,1,0,0,0,973,974,3,52,20,0,974,975,1,0,0, + 0,975,976,6,112,9,0,976,237,1,0,0,0,977,978,3,54,21,0,978,979,1,0,0,0,979, + 980,6,113,9,0,980,239,1,0,0,0,981,982,3,66,27,0,982,983,1,0,0,0,983,984, + 6,114,12,0,984,985,6,114,13,0,985,241,1,0,0,0,986,987,3,164,76,0,987,988, + 1,0,0,0,988,989,6,115,10,0,989,990,6,115,20,0,990,243,1,0,0,0,991,992,7, + 7,0,0,992,993,7,9,0,0,993,994,1,0,0,0,994,995,6,116,21,0,995,245,1,0,0, + 0,996,997,7,19,0,0,997,998,7,1,0,0,998,999,7,5,0,0,999,1000,7,10,0,0,1000, + 1001,1,0,0,0,1001,1002,6,117,21,0,1002,247,1,0,0,0,1003,1004,8,34,0,0,1004, + 249,1,0,0,0,1005,1007,3,248,118,0,1006,1005,1,0,0,0,1007,1008,1,0,0,0,1008, + 1006,1,0,0,0,1008,1009,1,0,0,0,1009,1010,1,0,0,0,1010,1011,3,318,153,0, + 1011,1013,1,0,0,0,1012,1006,1,0,0,0,1012,1013,1,0,0,0,1013,1015,1,0,0,0, + 1014,1016,3,248,118,0,1015,1014,1,0,0,0,1016,1017,1,0,0,0,1017,1015,1,0, + 0,0,1017,1018,1,0,0,0,1018,251,1,0,0,0,1019,1020,3,172,80,0,1020,1021,1, + 0,0,0,1021,1022,6,120,22,0,1022,253,1,0,0,0,1023,1024,3,250,119,0,1024, + 1025,1,0,0,0,1025,1026,6,121,23,0,1026,255,1,0,0,0,1027,1028,3,50,19,0, + 1028,1029,1,0,0,0,1029,1030,6,122,9,0,1030,257,1,0,0,0,1031,1032,3,52,20, + 0,1032,1033,1,0,0,0,1033,1034,6,123,9,0,1034,259,1,0,0,0,1035,1036,3,54, + 21,0,1036,1037,1,0,0,0,1037,1038,6,124,9,0,1038,261,1,0,0,0,1039,1040,3, + 66,27,0,1040,1041,1,0,0,0,1041,1042,6,125,12,0,1042,1043,6,125,13,0,1043, + 1044,6,125,13,0,1044,263,1,0,0,0,1045,1046,3,100,44,0,1046,1047,1,0,0,0, + 1047,1048,6,126,16,0,1048,265,1,0,0,0,1049,1050,3,104,46,0,1050,1051,1, + 0,0,0,1051,1052,6,127,15,0,1052,267,1,0,0,0,1053,1054,3,108,48,0,1054,1055, + 1,0,0,0,1055,1056,6,128,18,0,1056,269,1,0,0,0,1057,1058,3,246,117,0,1058, + 1059,1,0,0,0,1059,1060,6,129,24,0,1060,271,1,0,0,0,1061,1062,3,214,101, + 0,1062,1063,1,0,0,0,1063,1064,6,130,19,0,1064,273,1,0,0,0,1065,1066,3,172, + 80,0,1066,1067,1,0,0,0,1067,1068,6,131,22,0,1068,275,1,0,0,0,1069,1070, + 3,50,19,0,1070,1071,1,0,0,0,1071,1072,6,132,9,0,1072,277,1,0,0,0,1073,1074, + 3,52,20,0,1074,1075,1,0,0,0,1075,1076,6,133,9,0,1076,279,1,0,0,0,1077,1078, + 3,54,21,0,1078,1079,1,0,0,0,1079,1080,6,134,9,0,1080,281,1,0,0,0,1081,1082, + 3,66,27,0,1082,1083,1,0,0,0,1083,1084,6,135,12,0,1084,1085,6,135,13,0,1085, + 283,1,0,0,0,1086,1087,3,108,48,0,1087,1088,1,0,0,0,1088,1089,6,136,18,0, + 1089,285,1,0,0,0,1090,1091,3,172,80,0,1091,1092,1,0,0,0,1092,1093,6,137, + 22,0,1093,287,1,0,0,0,1094,1095,3,168,78,0,1095,1096,1,0,0,0,1096,1097, + 6,138,25,0,1097,289,1,0,0,0,1098,1099,3,50,19,0,1099,1100,1,0,0,0,1100, + 1101,6,139,9,0,1101,291,1,0,0,0,1102,1103,3,52,20,0,1103,1104,1,0,0,0,1104, + 1105,6,140,9,0,1105,293,1,0,0,0,1106,1107,3,54,21,0,1107,1108,1,0,0,0,1108, + 1109,6,141,9,0,1109,295,1,0,0,0,1110,1111,3,66,27,0,1111,1112,1,0,0,0,1112, + 1113,6,142,12,0,1113,1114,6,142,13,0,1114,297,1,0,0,0,1115,1116,7,1,0,0, + 1116,1117,7,9,0,0,1117,1118,7,15,0,0,1118,1119,7,7,0,0,1119,299,1,0,0,0, + 1120,1121,3,50,19,0,1121,1122,1,0,0,0,1122,1123,6,144,9,0,1123,301,1,0, + 0,0,1124,1125,3,52,20,0,1125,1126,1,0,0,0,1126,1127,6,145,9,0,1127,303, + 1,0,0,0,1128,1129,3,54,21,0,1129,1130,1,0,0,0,1130,1131,6,146,9,0,1131, + 305,1,0,0,0,1132,1133,3,66,27,0,1133,1134,1,0,0,0,1134,1135,6,147,12,0, + 1135,1136,6,147,13,0,1136,307,1,0,0,0,1137,1138,7,15,0,0,1138,1139,7,31, + 0,0,1139,1140,7,9,0,0,1140,1141,7,4,0,0,1141,1142,7,5,0,0,1142,1143,7,1, + 0,0,1143,1144,7,7,0,0,1144,1145,7,9,0,0,1145,1146,7,2,0,0,1146,309,1,0, + 0,0,1147,1148,3,50,19,0,1148,1149,1,0,0,0,1149,1150,6,149,9,0,1150,311, + 1,0,0,0,1151,1152,3,52,20,0,1152,1153,1,0,0,0,1153,1154,6,150,9,0,1154, + 313,1,0,0,0,1155,1156,3,54,21,0,1156,1157,1,0,0,0,1157,1158,6,151,9,0,1158, + 315,1,0,0,0,1159,1160,3,166,77,0,1160,1161,1,0,0,0,1161,1162,6,152,14,0, + 1162,1163,6,152,13,0,1163,317,1,0,0,0,1164,1165,5,58,0,0,1165,319,1,0,0, + 0,1166,1172,3,78,33,0,1167,1172,3,68,28,0,1168,1172,3,108,48,0,1169,1172, + 3,70,29,0,1170,1172,3,84,36,0,1171,1166,1,0,0,0,1171,1167,1,0,0,0,1171, + 1168,1,0,0,0,1171,1169,1,0,0,0,1171,1170,1,0,0,0,1172,1173,1,0,0,0,1173, + 1171,1,0,0,0,1173,1174,1,0,0,0,1174,321,1,0,0,0,1175,1176,3,50,19,0,1176, + 1177,1,0,0,0,1177,1178,6,155,9,0,1178,323,1,0,0,0,1179,1180,3,52,20,0,1180, + 1181,1,0,0,0,1181,1182,6,156,9,0,1182,325,1,0,0,0,1183,1184,3,54,21,0,1184, + 1185,1,0,0,0,1185,1186,6,157,9,0,1186,327,1,0,0,0,58,0,1,2,3,4,5,6,7,8, + 9,10,11,481,491,495,498,507,509,520,561,566,575,582,587,589,600,608,611, + 613,618,623,629,636,641,647,650,658,662,795,800,805,807,813,868,873,904, + 908,913,918,923,925,929,931,1008,1012,1017,1171,1173,26,5,2,0,5,4,0,5,6, + 0,5,1,0,5,3,0,5,10,0,5,8,0,5,5,0,5,9,0,0,1,0,7,65,0,5,0,0,7,26,0,4,0,0, + 7,66,0,7,35,0,7,33,0,7,27,0,7,37,0,7,77,0,5,11,0,5,7,0,7,68,0,7,87,0,7, + 86,0,7,67,0]; private static __ATN: ATN; public static get _ATN(): ATN { diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.g4 b/packages/kbn-esql-ast/src/antlr/esql_parser.g4 index 6f445b0df1730..b3496a20e3b8f 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.g4 +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.g4 @@ -7,14 +7,6 @@ // DO NOT MODIFY THIS FILE BY HAND. IT IS MANAGED BY A CI JOB. - -/* - * 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. - */ - parser grammar esql_parser; options {tokenVocab=esql_lexer;} @@ -113,21 +105,13 @@ field ; fromCommand - : FROM fromIdentifier (COMMA fromIdentifier)* metadata? fromOptions? + : FROM fromIdentifier (COMMA fromIdentifier)* metadata? ; fromIdentifier : FROM_UNQUOTED_IDENTIFIER ; -fromOptions - : OPTIONS configOption (COMMA configOption)* - ; - -configOption - : string ASSIGN string - ; - metadata : metadataOption | deprecated_metadata diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.interp b/packages/kbn-esql-ast/src/antlr/esql_parser.interp index 2b887065985d3..04dbf1b4a43dd 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.interp +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.interp @@ -71,7 +71,6 @@ null null null null -'options' 'metadata' null null @@ -184,7 +183,6 @@ QUOTED_IDENTIFIER EXPR_LINE_COMMENT EXPR_MULTILINE_COMMENT EXPR_WS -OPTIONS METADATA FROM_UNQUOTED_IDENTIFIER FROM_LINE_COMMENT @@ -242,8 +240,6 @@ fields field fromCommand fromIdentifier -fromOptions -configOption metadata metadataOption deprecated_metadata @@ -282,4 +278,4 @@ enrichWithClause atn: -[4, 1, 110, 543, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 118, 8, 1, 10, 1, 12, 1, 121, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 128, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 143, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 155, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 162, 8, 5, 10, 5, 12, 5, 165, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 172, 8, 5, 1, 5, 1, 5, 3, 5, 176, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 184, 8, 5, 10, 5, 12, 5, 187, 9, 5, 1, 6, 1, 6, 3, 6, 191, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 198, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 203, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 210, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 216, 8, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 5, 8, 224, 8, 8, 10, 8, 12, 8, 227, 9, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 237, 8, 9, 1, 9, 1, 9, 1, 9, 5, 9, 242, 8, 9, 10, 9, 12, 9, 245, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 253, 8, 10, 10, 10, 12, 10, 256, 9, 10, 3, 10, 258, 8, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 270, 8, 13, 10, 13, 12, 13, 273, 9, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 280, 8, 14, 1, 15, 1, 15, 1, 15, 1, 15, 5, 15, 286, 8, 15, 10, 15, 12, 15, 289, 9, 15, 1, 15, 3, 15, 292, 8, 15, 1, 15, 3, 15, 295, 8, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 5, 17, 303, 8, 17, 10, 17, 12, 17, 306, 9, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 3, 19, 314, 8, 19, 1, 20, 1, 20, 1, 20, 1, 20, 5, 20, 320, 8, 20, 10, 20, 12, 20, 323, 9, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 3, 23, 334, 8, 23, 1, 23, 1, 23, 3, 23, 338, 8, 23, 1, 24, 1, 24, 1, 24, 1, 24, 3, 24, 344, 8, 24, 1, 25, 1, 25, 1, 25, 5, 25, 349, 8, 25, 10, 25, 12, 25, 352, 9, 25, 1, 26, 1, 26, 1, 26, 5, 26, 357, 8, 26, 10, 26, 12, 26, 360, 9, 26, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 379, 8, 29, 10, 29, 12, 29, 382, 9, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 390, 8, 29, 10, 29, 12, 29, 393, 9, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 401, 8, 29, 10, 29, 12, 29, 404, 9, 29, 1, 29, 1, 29, 3, 29, 408, 8, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 417, 8, 31, 10, 31, 12, 31, 420, 9, 31, 1, 32, 1, 32, 3, 32, 424, 8, 32, 1, 32, 1, 32, 3, 32, 428, 8, 32, 1, 33, 1, 33, 1, 33, 1, 33, 5, 33, 434, 8, 33, 10, 33, 12, 33, 437, 9, 33, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 443, 8, 34, 10, 34, 12, 34, 446, 9, 34, 1, 35, 1, 35, 1, 35, 1, 35, 5, 35, 452, 8, 35, 10, 35, 12, 35, 455, 9, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 3, 37, 465, 8, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 5, 40, 477, 8, 40, 10, 40, 12, 40, 480, 9, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 43, 1, 43, 3, 43, 490, 8, 43, 1, 44, 3, 44, 493, 8, 44, 1, 44, 1, 44, 1, 45, 3, 45, 498, 8, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 3, 52, 523, 8, 52, 1, 52, 1, 52, 1, 52, 1, 52, 5, 52, 529, 8, 52, 10, 52, 12, 52, 532, 9, 52, 3, 52, 534, 8, 52, 1, 53, 1, 53, 1, 53, 3, 53, 539, 8, 53, 1, 53, 1, 53, 1, 53, 0, 4, 2, 10, 16, 18, 54, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 0, 7, 1, 0, 60, 61, 1, 0, 62, 64, 1, 0, 67, 68, 2, 0, 32, 32, 36, 36, 1, 0, 39, 40, 2, 0, 38, 38, 52, 52, 2, 0, 53, 53, 55, 59, 568, 0, 108, 1, 0, 0, 0, 2, 111, 1, 0, 0, 0, 4, 127, 1, 0, 0, 0, 6, 142, 1, 0, 0, 0, 8, 144, 1, 0, 0, 0, 10, 175, 1, 0, 0, 0, 12, 202, 1, 0, 0, 0, 14, 209, 1, 0, 0, 0, 16, 215, 1, 0, 0, 0, 18, 236, 1, 0, 0, 0, 20, 246, 1, 0, 0, 0, 22, 261, 1, 0, 0, 0, 24, 263, 1, 0, 0, 0, 26, 266, 1, 0, 0, 0, 28, 279, 1, 0, 0, 0, 30, 281, 1, 0, 0, 0, 32, 296, 1, 0, 0, 0, 34, 298, 1, 0, 0, 0, 36, 307, 1, 0, 0, 0, 38, 313, 1, 0, 0, 0, 40, 315, 1, 0, 0, 0, 42, 324, 1, 0, 0, 0, 44, 328, 1, 0, 0, 0, 46, 331, 1, 0, 0, 0, 48, 339, 1, 0, 0, 0, 50, 345, 1, 0, 0, 0, 52, 353, 1, 0, 0, 0, 54, 361, 1, 0, 0, 0, 56, 363, 1, 0, 0, 0, 58, 407, 1, 0, 0, 0, 60, 409, 1, 0, 0, 0, 62, 412, 1, 0, 0, 0, 64, 421, 1, 0, 0, 0, 66, 429, 1, 0, 0, 0, 68, 438, 1, 0, 0, 0, 70, 447, 1, 0, 0, 0, 72, 456, 1, 0, 0, 0, 74, 460, 1, 0, 0, 0, 76, 466, 1, 0, 0, 0, 78, 470, 1, 0, 0, 0, 80, 473, 1, 0, 0, 0, 82, 481, 1, 0, 0, 0, 84, 485, 1, 0, 0, 0, 86, 489, 1, 0, 0, 0, 88, 492, 1, 0, 0, 0, 90, 497, 1, 0, 0, 0, 92, 501, 1, 0, 0, 0, 94, 503, 1, 0, 0, 0, 96, 505, 1, 0, 0, 0, 98, 508, 1, 0, 0, 0, 100, 512, 1, 0, 0, 0, 102, 515, 1, 0, 0, 0, 104, 518, 1, 0, 0, 0, 106, 538, 1, 0, 0, 0, 108, 109, 3, 2, 1, 0, 109, 110, 5, 0, 0, 1, 110, 1, 1, 0, 0, 0, 111, 112, 6, 1, -1, 0, 112, 113, 3, 4, 2, 0, 113, 119, 1, 0, 0, 0, 114, 115, 10, 1, 0, 0, 115, 116, 5, 26, 0, 0, 116, 118, 3, 6, 3, 0, 117, 114, 1, 0, 0, 0, 118, 121, 1, 0, 0, 0, 119, 117, 1, 0, 0, 0, 119, 120, 1, 0, 0, 0, 120, 3, 1, 0, 0, 0, 121, 119, 1, 0, 0, 0, 122, 128, 3, 96, 48, 0, 123, 128, 3, 30, 15, 0, 124, 128, 3, 24, 12, 0, 125, 128, 3, 100, 50, 0, 126, 128, 3, 102, 51, 0, 127, 122, 1, 0, 0, 0, 127, 123, 1, 0, 0, 0, 127, 124, 1, 0, 0, 0, 127, 125, 1, 0, 0, 0, 127, 126, 1, 0, 0, 0, 128, 5, 1, 0, 0, 0, 129, 143, 3, 44, 22, 0, 130, 143, 3, 48, 24, 0, 131, 143, 3, 60, 30, 0, 132, 143, 3, 66, 33, 0, 133, 143, 3, 62, 31, 0, 134, 143, 3, 46, 23, 0, 135, 143, 3, 8, 4, 0, 136, 143, 3, 68, 34, 0, 137, 143, 3, 70, 35, 0, 138, 143, 3, 74, 37, 0, 139, 143, 3, 76, 38, 0, 140, 143, 3, 104, 52, 0, 141, 143, 3, 78, 39, 0, 142, 129, 1, 0, 0, 0, 142, 130, 1, 0, 0, 0, 142, 131, 1, 0, 0, 0, 142, 132, 1, 0, 0, 0, 142, 133, 1, 0, 0, 0, 142, 134, 1, 0, 0, 0, 142, 135, 1, 0, 0, 0, 142, 136, 1, 0, 0, 0, 142, 137, 1, 0, 0, 0, 142, 138, 1, 0, 0, 0, 142, 139, 1, 0, 0, 0, 142, 140, 1, 0, 0, 0, 142, 141, 1, 0, 0, 0, 143, 7, 1, 0, 0, 0, 144, 145, 5, 18, 0, 0, 145, 146, 3, 10, 5, 0, 146, 9, 1, 0, 0, 0, 147, 148, 6, 5, -1, 0, 148, 149, 5, 45, 0, 0, 149, 176, 3, 10, 5, 7, 150, 176, 3, 14, 7, 0, 151, 176, 3, 12, 6, 0, 152, 154, 3, 14, 7, 0, 153, 155, 5, 45, 0, 0, 154, 153, 1, 0, 0, 0, 154, 155, 1, 0, 0, 0, 155, 156, 1, 0, 0, 0, 156, 157, 5, 42, 0, 0, 157, 158, 5, 41, 0, 0, 158, 163, 3, 14, 7, 0, 159, 160, 5, 35, 0, 0, 160, 162, 3, 14, 7, 0, 161, 159, 1, 0, 0, 0, 162, 165, 1, 0, 0, 0, 163, 161, 1, 0, 0, 0, 163, 164, 1, 0, 0, 0, 164, 166, 1, 0, 0, 0, 165, 163, 1, 0, 0, 0, 166, 167, 5, 51, 0, 0, 167, 176, 1, 0, 0, 0, 168, 169, 3, 14, 7, 0, 169, 171, 5, 43, 0, 0, 170, 172, 5, 45, 0, 0, 171, 170, 1, 0, 0, 0, 171, 172, 1, 0, 0, 0, 172, 173, 1, 0, 0, 0, 173, 174, 5, 46, 0, 0, 174, 176, 1, 0, 0, 0, 175, 147, 1, 0, 0, 0, 175, 150, 1, 0, 0, 0, 175, 151, 1, 0, 0, 0, 175, 152, 1, 0, 0, 0, 175, 168, 1, 0, 0, 0, 176, 185, 1, 0, 0, 0, 177, 178, 10, 4, 0, 0, 178, 179, 5, 31, 0, 0, 179, 184, 3, 10, 5, 5, 180, 181, 10, 3, 0, 0, 181, 182, 5, 48, 0, 0, 182, 184, 3, 10, 5, 4, 183, 177, 1, 0, 0, 0, 183, 180, 1, 0, 0, 0, 184, 187, 1, 0, 0, 0, 185, 183, 1, 0, 0, 0, 185, 186, 1, 0, 0, 0, 186, 11, 1, 0, 0, 0, 187, 185, 1, 0, 0, 0, 188, 190, 3, 14, 7, 0, 189, 191, 5, 45, 0, 0, 190, 189, 1, 0, 0, 0, 190, 191, 1, 0, 0, 0, 191, 192, 1, 0, 0, 0, 192, 193, 5, 44, 0, 0, 193, 194, 3, 92, 46, 0, 194, 203, 1, 0, 0, 0, 195, 197, 3, 14, 7, 0, 196, 198, 5, 45, 0, 0, 197, 196, 1, 0, 0, 0, 197, 198, 1, 0, 0, 0, 198, 199, 1, 0, 0, 0, 199, 200, 5, 50, 0, 0, 200, 201, 3, 92, 46, 0, 201, 203, 1, 0, 0, 0, 202, 188, 1, 0, 0, 0, 202, 195, 1, 0, 0, 0, 203, 13, 1, 0, 0, 0, 204, 210, 3, 16, 8, 0, 205, 206, 3, 16, 8, 0, 206, 207, 3, 94, 47, 0, 207, 208, 3, 16, 8, 0, 208, 210, 1, 0, 0, 0, 209, 204, 1, 0, 0, 0, 209, 205, 1, 0, 0, 0, 210, 15, 1, 0, 0, 0, 211, 212, 6, 8, -1, 0, 212, 216, 3, 18, 9, 0, 213, 214, 7, 0, 0, 0, 214, 216, 3, 16, 8, 3, 215, 211, 1, 0, 0, 0, 215, 213, 1, 0, 0, 0, 216, 225, 1, 0, 0, 0, 217, 218, 10, 2, 0, 0, 218, 219, 7, 1, 0, 0, 219, 224, 3, 16, 8, 3, 220, 221, 10, 1, 0, 0, 221, 222, 7, 0, 0, 0, 222, 224, 3, 16, 8, 2, 223, 217, 1, 0, 0, 0, 223, 220, 1, 0, 0, 0, 224, 227, 1, 0, 0, 0, 225, 223, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 17, 1, 0, 0, 0, 227, 225, 1, 0, 0, 0, 228, 229, 6, 9, -1, 0, 229, 237, 3, 58, 29, 0, 230, 237, 3, 50, 25, 0, 231, 237, 3, 20, 10, 0, 232, 233, 5, 41, 0, 0, 233, 234, 3, 10, 5, 0, 234, 235, 5, 51, 0, 0, 235, 237, 1, 0, 0, 0, 236, 228, 1, 0, 0, 0, 236, 230, 1, 0, 0, 0, 236, 231, 1, 0, 0, 0, 236, 232, 1, 0, 0, 0, 237, 243, 1, 0, 0, 0, 238, 239, 10, 1, 0, 0, 239, 240, 5, 34, 0, 0, 240, 242, 3, 22, 11, 0, 241, 238, 1, 0, 0, 0, 242, 245, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 19, 1, 0, 0, 0, 245, 243, 1, 0, 0, 0, 246, 247, 3, 54, 27, 0, 247, 257, 5, 41, 0, 0, 248, 258, 5, 62, 0, 0, 249, 254, 3, 10, 5, 0, 250, 251, 5, 35, 0, 0, 251, 253, 3, 10, 5, 0, 252, 250, 1, 0, 0, 0, 253, 256, 1, 0, 0, 0, 254, 252, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 258, 1, 0, 0, 0, 256, 254, 1, 0, 0, 0, 257, 248, 1, 0, 0, 0, 257, 249, 1, 0, 0, 0, 257, 258, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 260, 5, 51, 0, 0, 260, 21, 1, 0, 0, 0, 261, 262, 3, 54, 27, 0, 262, 23, 1, 0, 0, 0, 263, 264, 5, 14, 0, 0, 264, 265, 3, 26, 13, 0, 265, 25, 1, 0, 0, 0, 266, 271, 3, 28, 14, 0, 267, 268, 5, 35, 0, 0, 268, 270, 3, 28, 14, 0, 269, 267, 1, 0, 0, 0, 270, 273, 1, 0, 0, 0, 271, 269, 1, 0, 0, 0, 271, 272, 1, 0, 0, 0, 272, 27, 1, 0, 0, 0, 273, 271, 1, 0, 0, 0, 274, 280, 3, 10, 5, 0, 275, 276, 3, 50, 25, 0, 276, 277, 5, 33, 0, 0, 277, 278, 3, 10, 5, 0, 278, 280, 1, 0, 0, 0, 279, 274, 1, 0, 0, 0, 279, 275, 1, 0, 0, 0, 280, 29, 1, 0, 0, 0, 281, 282, 5, 6, 0, 0, 282, 287, 3, 32, 16, 0, 283, 284, 5, 35, 0, 0, 284, 286, 3, 32, 16, 0, 285, 283, 1, 0, 0, 0, 286, 289, 1, 0, 0, 0, 287, 285, 1, 0, 0, 0, 287, 288, 1, 0, 0, 0, 288, 291, 1, 0, 0, 0, 289, 287, 1, 0, 0, 0, 290, 292, 3, 38, 19, 0, 291, 290, 1, 0, 0, 0, 291, 292, 1, 0, 0, 0, 292, 294, 1, 0, 0, 0, 293, 295, 3, 34, 17, 0, 294, 293, 1, 0, 0, 0, 294, 295, 1, 0, 0, 0, 295, 31, 1, 0, 0, 0, 296, 297, 5, 74, 0, 0, 297, 33, 1, 0, 0, 0, 298, 299, 5, 72, 0, 0, 299, 304, 3, 36, 18, 0, 300, 301, 5, 35, 0, 0, 301, 303, 3, 36, 18, 0, 302, 300, 1, 0, 0, 0, 303, 306, 1, 0, 0, 0, 304, 302, 1, 0, 0, 0, 304, 305, 1, 0, 0, 0, 305, 35, 1, 0, 0, 0, 306, 304, 1, 0, 0, 0, 307, 308, 3, 92, 46, 0, 308, 309, 5, 33, 0, 0, 309, 310, 3, 92, 46, 0, 310, 37, 1, 0, 0, 0, 311, 314, 3, 40, 20, 0, 312, 314, 3, 42, 21, 0, 313, 311, 1, 0, 0, 0, 313, 312, 1, 0, 0, 0, 314, 39, 1, 0, 0, 0, 315, 316, 5, 73, 0, 0, 316, 321, 3, 32, 16, 0, 317, 318, 5, 35, 0, 0, 318, 320, 3, 32, 16, 0, 319, 317, 1, 0, 0, 0, 320, 323, 1, 0, 0, 0, 321, 319, 1, 0, 0, 0, 321, 322, 1, 0, 0, 0, 322, 41, 1, 0, 0, 0, 323, 321, 1, 0, 0, 0, 324, 325, 5, 65, 0, 0, 325, 326, 3, 40, 20, 0, 326, 327, 5, 66, 0, 0, 327, 43, 1, 0, 0, 0, 328, 329, 5, 4, 0, 0, 329, 330, 3, 26, 13, 0, 330, 45, 1, 0, 0, 0, 331, 333, 5, 17, 0, 0, 332, 334, 3, 26, 13, 0, 333, 332, 1, 0, 0, 0, 333, 334, 1, 0, 0, 0, 334, 337, 1, 0, 0, 0, 335, 336, 5, 30, 0, 0, 336, 338, 3, 26, 13, 0, 337, 335, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 47, 1, 0, 0, 0, 339, 340, 5, 8, 0, 0, 340, 343, 3, 26, 13, 0, 341, 342, 5, 30, 0, 0, 342, 344, 3, 26, 13, 0, 343, 341, 1, 0, 0, 0, 343, 344, 1, 0, 0, 0, 344, 49, 1, 0, 0, 0, 345, 350, 3, 54, 27, 0, 346, 347, 5, 37, 0, 0, 347, 349, 3, 54, 27, 0, 348, 346, 1, 0, 0, 0, 349, 352, 1, 0, 0, 0, 350, 348, 1, 0, 0, 0, 350, 351, 1, 0, 0, 0, 351, 51, 1, 0, 0, 0, 352, 350, 1, 0, 0, 0, 353, 358, 3, 56, 28, 0, 354, 355, 5, 37, 0, 0, 355, 357, 3, 56, 28, 0, 356, 354, 1, 0, 0, 0, 357, 360, 1, 0, 0, 0, 358, 356, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 53, 1, 0, 0, 0, 360, 358, 1, 0, 0, 0, 361, 362, 7, 2, 0, 0, 362, 55, 1, 0, 0, 0, 363, 364, 5, 78, 0, 0, 364, 57, 1, 0, 0, 0, 365, 408, 5, 46, 0, 0, 366, 367, 3, 90, 45, 0, 367, 368, 5, 67, 0, 0, 368, 408, 1, 0, 0, 0, 369, 408, 3, 88, 44, 0, 370, 408, 3, 90, 45, 0, 371, 408, 3, 84, 42, 0, 372, 408, 5, 49, 0, 0, 373, 408, 3, 92, 46, 0, 374, 375, 5, 65, 0, 0, 375, 380, 3, 86, 43, 0, 376, 377, 5, 35, 0, 0, 377, 379, 3, 86, 43, 0, 378, 376, 1, 0, 0, 0, 379, 382, 1, 0, 0, 0, 380, 378, 1, 0, 0, 0, 380, 381, 1, 0, 0, 0, 381, 383, 1, 0, 0, 0, 382, 380, 1, 0, 0, 0, 383, 384, 5, 66, 0, 0, 384, 408, 1, 0, 0, 0, 385, 386, 5, 65, 0, 0, 386, 391, 3, 84, 42, 0, 387, 388, 5, 35, 0, 0, 388, 390, 3, 84, 42, 0, 389, 387, 1, 0, 0, 0, 390, 393, 1, 0, 0, 0, 391, 389, 1, 0, 0, 0, 391, 392, 1, 0, 0, 0, 392, 394, 1, 0, 0, 0, 393, 391, 1, 0, 0, 0, 394, 395, 5, 66, 0, 0, 395, 408, 1, 0, 0, 0, 396, 397, 5, 65, 0, 0, 397, 402, 3, 92, 46, 0, 398, 399, 5, 35, 0, 0, 399, 401, 3, 92, 46, 0, 400, 398, 1, 0, 0, 0, 401, 404, 1, 0, 0, 0, 402, 400, 1, 0, 0, 0, 402, 403, 1, 0, 0, 0, 403, 405, 1, 0, 0, 0, 404, 402, 1, 0, 0, 0, 405, 406, 5, 66, 0, 0, 406, 408, 1, 0, 0, 0, 407, 365, 1, 0, 0, 0, 407, 366, 1, 0, 0, 0, 407, 369, 1, 0, 0, 0, 407, 370, 1, 0, 0, 0, 407, 371, 1, 0, 0, 0, 407, 372, 1, 0, 0, 0, 407, 373, 1, 0, 0, 0, 407, 374, 1, 0, 0, 0, 407, 385, 1, 0, 0, 0, 407, 396, 1, 0, 0, 0, 408, 59, 1, 0, 0, 0, 409, 410, 5, 10, 0, 0, 410, 411, 5, 28, 0, 0, 411, 61, 1, 0, 0, 0, 412, 413, 5, 16, 0, 0, 413, 418, 3, 64, 32, 0, 414, 415, 5, 35, 0, 0, 415, 417, 3, 64, 32, 0, 416, 414, 1, 0, 0, 0, 417, 420, 1, 0, 0, 0, 418, 416, 1, 0, 0, 0, 418, 419, 1, 0, 0, 0, 419, 63, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 421, 423, 3, 10, 5, 0, 422, 424, 7, 3, 0, 0, 423, 422, 1, 0, 0, 0, 423, 424, 1, 0, 0, 0, 424, 427, 1, 0, 0, 0, 425, 426, 5, 47, 0, 0, 426, 428, 7, 4, 0, 0, 427, 425, 1, 0, 0, 0, 427, 428, 1, 0, 0, 0, 428, 65, 1, 0, 0, 0, 429, 430, 5, 9, 0, 0, 430, 435, 3, 52, 26, 0, 431, 432, 5, 35, 0, 0, 432, 434, 3, 52, 26, 0, 433, 431, 1, 0, 0, 0, 434, 437, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 435, 436, 1, 0, 0, 0, 436, 67, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 438, 439, 5, 2, 0, 0, 439, 444, 3, 52, 26, 0, 440, 441, 5, 35, 0, 0, 441, 443, 3, 52, 26, 0, 442, 440, 1, 0, 0, 0, 443, 446, 1, 0, 0, 0, 444, 442, 1, 0, 0, 0, 444, 445, 1, 0, 0, 0, 445, 69, 1, 0, 0, 0, 446, 444, 1, 0, 0, 0, 447, 448, 5, 13, 0, 0, 448, 453, 3, 72, 36, 0, 449, 450, 5, 35, 0, 0, 450, 452, 3, 72, 36, 0, 451, 449, 1, 0, 0, 0, 452, 455, 1, 0, 0, 0, 453, 451, 1, 0, 0, 0, 453, 454, 1, 0, 0, 0, 454, 71, 1, 0, 0, 0, 455, 453, 1, 0, 0, 0, 456, 457, 3, 52, 26, 0, 457, 458, 5, 82, 0, 0, 458, 459, 3, 52, 26, 0, 459, 73, 1, 0, 0, 0, 460, 461, 5, 1, 0, 0, 461, 462, 3, 18, 9, 0, 462, 464, 3, 92, 46, 0, 463, 465, 3, 80, 40, 0, 464, 463, 1, 0, 0, 0, 464, 465, 1, 0, 0, 0, 465, 75, 1, 0, 0, 0, 466, 467, 5, 7, 0, 0, 467, 468, 3, 18, 9, 0, 468, 469, 3, 92, 46, 0, 469, 77, 1, 0, 0, 0, 470, 471, 5, 12, 0, 0, 471, 472, 3, 50, 25, 0, 472, 79, 1, 0, 0, 0, 473, 478, 3, 82, 41, 0, 474, 475, 5, 35, 0, 0, 475, 477, 3, 82, 41, 0, 476, 474, 1, 0, 0, 0, 477, 480, 1, 0, 0, 0, 478, 476, 1, 0, 0, 0, 478, 479, 1, 0, 0, 0, 479, 81, 1, 0, 0, 0, 480, 478, 1, 0, 0, 0, 481, 482, 3, 54, 27, 0, 482, 483, 5, 33, 0, 0, 483, 484, 3, 58, 29, 0, 484, 83, 1, 0, 0, 0, 485, 486, 7, 5, 0, 0, 486, 85, 1, 0, 0, 0, 487, 490, 3, 88, 44, 0, 488, 490, 3, 90, 45, 0, 489, 487, 1, 0, 0, 0, 489, 488, 1, 0, 0, 0, 490, 87, 1, 0, 0, 0, 491, 493, 7, 0, 0, 0, 492, 491, 1, 0, 0, 0, 492, 493, 1, 0, 0, 0, 493, 494, 1, 0, 0, 0, 494, 495, 5, 29, 0, 0, 495, 89, 1, 0, 0, 0, 496, 498, 7, 0, 0, 0, 497, 496, 1, 0, 0, 0, 497, 498, 1, 0, 0, 0, 498, 499, 1, 0, 0, 0, 499, 500, 5, 28, 0, 0, 500, 91, 1, 0, 0, 0, 501, 502, 5, 27, 0, 0, 502, 93, 1, 0, 0, 0, 503, 504, 7, 6, 0, 0, 504, 95, 1, 0, 0, 0, 505, 506, 5, 5, 0, 0, 506, 507, 3, 98, 49, 0, 507, 97, 1, 0, 0, 0, 508, 509, 5, 65, 0, 0, 509, 510, 3, 2, 1, 0, 510, 511, 5, 66, 0, 0, 511, 99, 1, 0, 0, 0, 512, 513, 5, 15, 0, 0, 513, 514, 5, 98, 0, 0, 514, 101, 1, 0, 0, 0, 515, 516, 5, 11, 0, 0, 516, 517, 5, 102, 0, 0, 517, 103, 1, 0, 0, 0, 518, 519, 5, 3, 0, 0, 519, 522, 5, 88, 0, 0, 520, 521, 5, 86, 0, 0, 521, 523, 3, 52, 26, 0, 522, 520, 1, 0, 0, 0, 522, 523, 1, 0, 0, 0, 523, 533, 1, 0, 0, 0, 524, 525, 5, 87, 0, 0, 525, 530, 3, 106, 53, 0, 526, 527, 5, 35, 0, 0, 527, 529, 3, 106, 53, 0, 528, 526, 1, 0, 0, 0, 529, 532, 1, 0, 0, 0, 530, 528, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 534, 1, 0, 0, 0, 532, 530, 1, 0, 0, 0, 533, 524, 1, 0, 0, 0, 533, 534, 1, 0, 0, 0, 534, 105, 1, 0, 0, 0, 535, 536, 3, 52, 26, 0, 536, 537, 5, 33, 0, 0, 537, 539, 1, 0, 0, 0, 538, 535, 1, 0, 0, 0, 538, 539, 1, 0, 0, 0, 539, 540, 1, 0, 0, 0, 540, 541, 3, 52, 26, 0, 541, 107, 1, 0, 0, 0, 52, 119, 127, 142, 154, 163, 171, 175, 183, 185, 190, 197, 202, 209, 215, 223, 225, 236, 243, 254, 257, 271, 279, 287, 291, 294, 304, 313, 321, 333, 337, 343, 350, 358, 380, 391, 402, 407, 418, 423, 427, 435, 444, 453, 464, 478, 489, 492, 497, 522, 530, 533, 538] \ No newline at end of file +[4, 1, 109, 523, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 114, 8, 1, 10, 1, 12, 1, 117, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 124, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 139, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 151, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 158, 8, 5, 10, 5, 12, 5, 161, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 168, 8, 5, 1, 5, 1, 5, 3, 5, 172, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 180, 8, 5, 10, 5, 12, 5, 183, 9, 5, 1, 6, 1, 6, 3, 6, 187, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 194, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 199, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 206, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 212, 8, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 5, 8, 220, 8, 8, 10, 8, 12, 8, 223, 9, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 233, 8, 9, 1, 9, 1, 9, 1, 9, 5, 9, 238, 8, 9, 10, 9, 12, 9, 241, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 5, 10, 249, 8, 10, 10, 10, 12, 10, 252, 9, 10, 3, 10, 254, 8, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 5, 13, 266, 8, 13, 10, 13, 12, 13, 269, 9, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 3, 14, 276, 8, 14, 1, 15, 1, 15, 1, 15, 1, 15, 5, 15, 282, 8, 15, 10, 15, 12, 15, 285, 9, 15, 1, 15, 3, 15, 288, 8, 15, 1, 16, 1, 16, 1, 17, 1, 17, 3, 17, 294, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 5, 18, 300, 8, 18, 10, 18, 12, 18, 303, 9, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 3, 21, 314, 8, 21, 1, 21, 1, 21, 3, 21, 318, 8, 21, 1, 22, 1, 22, 1, 22, 1, 22, 3, 22, 324, 8, 22, 1, 23, 1, 23, 1, 23, 5, 23, 329, 8, 23, 10, 23, 12, 23, 332, 9, 23, 1, 24, 1, 24, 1, 24, 5, 24, 337, 8, 24, 10, 24, 12, 24, 340, 9, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 5, 27, 359, 8, 27, 10, 27, 12, 27, 362, 9, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 5, 27, 370, 8, 27, 10, 27, 12, 27, 373, 9, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 5, 27, 381, 8, 27, 10, 27, 12, 27, 384, 9, 27, 1, 27, 1, 27, 3, 27, 388, 8, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 397, 8, 29, 10, 29, 12, 29, 400, 9, 29, 1, 30, 1, 30, 3, 30, 404, 8, 30, 1, 30, 1, 30, 3, 30, 408, 8, 30, 1, 31, 1, 31, 1, 31, 1, 31, 5, 31, 414, 8, 31, 10, 31, 12, 31, 417, 9, 31, 1, 32, 1, 32, 1, 32, 1, 32, 5, 32, 423, 8, 32, 10, 32, 12, 32, 426, 9, 32, 1, 33, 1, 33, 1, 33, 1, 33, 5, 33, 432, 8, 33, 10, 33, 12, 33, 435, 9, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 3, 35, 445, 8, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 5, 38, 457, 8, 38, 10, 38, 12, 38, 460, 9, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 41, 1, 41, 3, 41, 470, 8, 41, 1, 42, 3, 42, 473, 8, 42, 1, 42, 1, 42, 1, 43, 3, 43, 478, 8, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 3, 50, 503, 8, 50, 1, 50, 1, 50, 1, 50, 1, 50, 5, 50, 509, 8, 50, 10, 50, 12, 50, 512, 9, 50, 3, 50, 514, 8, 50, 1, 51, 1, 51, 1, 51, 3, 51, 519, 8, 51, 1, 51, 1, 51, 1, 51, 0, 4, 2, 10, 16, 18, 52, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 0, 7, 1, 0, 60, 61, 1, 0, 62, 64, 1, 0, 67, 68, 2, 0, 32, 32, 36, 36, 1, 0, 39, 40, 2, 0, 38, 38, 52, 52, 2, 0, 53, 53, 55, 59, 548, 0, 104, 1, 0, 0, 0, 2, 107, 1, 0, 0, 0, 4, 123, 1, 0, 0, 0, 6, 138, 1, 0, 0, 0, 8, 140, 1, 0, 0, 0, 10, 171, 1, 0, 0, 0, 12, 198, 1, 0, 0, 0, 14, 205, 1, 0, 0, 0, 16, 211, 1, 0, 0, 0, 18, 232, 1, 0, 0, 0, 20, 242, 1, 0, 0, 0, 22, 257, 1, 0, 0, 0, 24, 259, 1, 0, 0, 0, 26, 262, 1, 0, 0, 0, 28, 275, 1, 0, 0, 0, 30, 277, 1, 0, 0, 0, 32, 289, 1, 0, 0, 0, 34, 293, 1, 0, 0, 0, 36, 295, 1, 0, 0, 0, 38, 304, 1, 0, 0, 0, 40, 308, 1, 0, 0, 0, 42, 311, 1, 0, 0, 0, 44, 319, 1, 0, 0, 0, 46, 325, 1, 0, 0, 0, 48, 333, 1, 0, 0, 0, 50, 341, 1, 0, 0, 0, 52, 343, 1, 0, 0, 0, 54, 387, 1, 0, 0, 0, 56, 389, 1, 0, 0, 0, 58, 392, 1, 0, 0, 0, 60, 401, 1, 0, 0, 0, 62, 409, 1, 0, 0, 0, 64, 418, 1, 0, 0, 0, 66, 427, 1, 0, 0, 0, 68, 436, 1, 0, 0, 0, 70, 440, 1, 0, 0, 0, 72, 446, 1, 0, 0, 0, 74, 450, 1, 0, 0, 0, 76, 453, 1, 0, 0, 0, 78, 461, 1, 0, 0, 0, 80, 465, 1, 0, 0, 0, 82, 469, 1, 0, 0, 0, 84, 472, 1, 0, 0, 0, 86, 477, 1, 0, 0, 0, 88, 481, 1, 0, 0, 0, 90, 483, 1, 0, 0, 0, 92, 485, 1, 0, 0, 0, 94, 488, 1, 0, 0, 0, 96, 492, 1, 0, 0, 0, 98, 495, 1, 0, 0, 0, 100, 498, 1, 0, 0, 0, 102, 518, 1, 0, 0, 0, 104, 105, 3, 2, 1, 0, 105, 106, 5, 0, 0, 1, 106, 1, 1, 0, 0, 0, 107, 108, 6, 1, -1, 0, 108, 109, 3, 4, 2, 0, 109, 115, 1, 0, 0, 0, 110, 111, 10, 1, 0, 0, 111, 112, 5, 26, 0, 0, 112, 114, 3, 6, 3, 0, 113, 110, 1, 0, 0, 0, 114, 117, 1, 0, 0, 0, 115, 113, 1, 0, 0, 0, 115, 116, 1, 0, 0, 0, 116, 3, 1, 0, 0, 0, 117, 115, 1, 0, 0, 0, 118, 124, 3, 92, 46, 0, 119, 124, 3, 30, 15, 0, 120, 124, 3, 24, 12, 0, 121, 124, 3, 96, 48, 0, 122, 124, 3, 98, 49, 0, 123, 118, 1, 0, 0, 0, 123, 119, 1, 0, 0, 0, 123, 120, 1, 0, 0, 0, 123, 121, 1, 0, 0, 0, 123, 122, 1, 0, 0, 0, 124, 5, 1, 0, 0, 0, 125, 139, 3, 40, 20, 0, 126, 139, 3, 44, 22, 0, 127, 139, 3, 56, 28, 0, 128, 139, 3, 62, 31, 0, 129, 139, 3, 58, 29, 0, 130, 139, 3, 42, 21, 0, 131, 139, 3, 8, 4, 0, 132, 139, 3, 64, 32, 0, 133, 139, 3, 66, 33, 0, 134, 139, 3, 70, 35, 0, 135, 139, 3, 72, 36, 0, 136, 139, 3, 100, 50, 0, 137, 139, 3, 74, 37, 0, 138, 125, 1, 0, 0, 0, 138, 126, 1, 0, 0, 0, 138, 127, 1, 0, 0, 0, 138, 128, 1, 0, 0, 0, 138, 129, 1, 0, 0, 0, 138, 130, 1, 0, 0, 0, 138, 131, 1, 0, 0, 0, 138, 132, 1, 0, 0, 0, 138, 133, 1, 0, 0, 0, 138, 134, 1, 0, 0, 0, 138, 135, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 138, 137, 1, 0, 0, 0, 139, 7, 1, 0, 0, 0, 140, 141, 5, 18, 0, 0, 141, 142, 3, 10, 5, 0, 142, 9, 1, 0, 0, 0, 143, 144, 6, 5, -1, 0, 144, 145, 5, 45, 0, 0, 145, 172, 3, 10, 5, 7, 146, 172, 3, 14, 7, 0, 147, 172, 3, 12, 6, 0, 148, 150, 3, 14, 7, 0, 149, 151, 5, 45, 0, 0, 150, 149, 1, 0, 0, 0, 150, 151, 1, 0, 0, 0, 151, 152, 1, 0, 0, 0, 152, 153, 5, 42, 0, 0, 153, 154, 5, 41, 0, 0, 154, 159, 3, 14, 7, 0, 155, 156, 5, 35, 0, 0, 156, 158, 3, 14, 7, 0, 157, 155, 1, 0, 0, 0, 158, 161, 1, 0, 0, 0, 159, 157, 1, 0, 0, 0, 159, 160, 1, 0, 0, 0, 160, 162, 1, 0, 0, 0, 161, 159, 1, 0, 0, 0, 162, 163, 5, 51, 0, 0, 163, 172, 1, 0, 0, 0, 164, 165, 3, 14, 7, 0, 165, 167, 5, 43, 0, 0, 166, 168, 5, 45, 0, 0, 167, 166, 1, 0, 0, 0, 167, 168, 1, 0, 0, 0, 168, 169, 1, 0, 0, 0, 169, 170, 5, 46, 0, 0, 170, 172, 1, 0, 0, 0, 171, 143, 1, 0, 0, 0, 171, 146, 1, 0, 0, 0, 171, 147, 1, 0, 0, 0, 171, 148, 1, 0, 0, 0, 171, 164, 1, 0, 0, 0, 172, 181, 1, 0, 0, 0, 173, 174, 10, 4, 0, 0, 174, 175, 5, 31, 0, 0, 175, 180, 3, 10, 5, 5, 176, 177, 10, 3, 0, 0, 177, 178, 5, 48, 0, 0, 178, 180, 3, 10, 5, 4, 179, 173, 1, 0, 0, 0, 179, 176, 1, 0, 0, 0, 180, 183, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 11, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 184, 186, 3, 14, 7, 0, 185, 187, 5, 45, 0, 0, 186, 185, 1, 0, 0, 0, 186, 187, 1, 0, 0, 0, 187, 188, 1, 0, 0, 0, 188, 189, 5, 44, 0, 0, 189, 190, 3, 88, 44, 0, 190, 199, 1, 0, 0, 0, 191, 193, 3, 14, 7, 0, 192, 194, 5, 45, 0, 0, 193, 192, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, 194, 195, 1, 0, 0, 0, 195, 196, 5, 50, 0, 0, 196, 197, 3, 88, 44, 0, 197, 199, 1, 0, 0, 0, 198, 184, 1, 0, 0, 0, 198, 191, 1, 0, 0, 0, 199, 13, 1, 0, 0, 0, 200, 206, 3, 16, 8, 0, 201, 202, 3, 16, 8, 0, 202, 203, 3, 90, 45, 0, 203, 204, 3, 16, 8, 0, 204, 206, 1, 0, 0, 0, 205, 200, 1, 0, 0, 0, 205, 201, 1, 0, 0, 0, 206, 15, 1, 0, 0, 0, 207, 208, 6, 8, -1, 0, 208, 212, 3, 18, 9, 0, 209, 210, 7, 0, 0, 0, 210, 212, 3, 16, 8, 3, 211, 207, 1, 0, 0, 0, 211, 209, 1, 0, 0, 0, 212, 221, 1, 0, 0, 0, 213, 214, 10, 2, 0, 0, 214, 215, 7, 1, 0, 0, 215, 220, 3, 16, 8, 3, 216, 217, 10, 1, 0, 0, 217, 218, 7, 0, 0, 0, 218, 220, 3, 16, 8, 2, 219, 213, 1, 0, 0, 0, 219, 216, 1, 0, 0, 0, 220, 223, 1, 0, 0, 0, 221, 219, 1, 0, 0, 0, 221, 222, 1, 0, 0, 0, 222, 17, 1, 0, 0, 0, 223, 221, 1, 0, 0, 0, 224, 225, 6, 9, -1, 0, 225, 233, 3, 54, 27, 0, 226, 233, 3, 46, 23, 0, 227, 233, 3, 20, 10, 0, 228, 229, 5, 41, 0, 0, 229, 230, 3, 10, 5, 0, 230, 231, 5, 51, 0, 0, 231, 233, 1, 0, 0, 0, 232, 224, 1, 0, 0, 0, 232, 226, 1, 0, 0, 0, 232, 227, 1, 0, 0, 0, 232, 228, 1, 0, 0, 0, 233, 239, 1, 0, 0, 0, 234, 235, 10, 1, 0, 0, 235, 236, 5, 34, 0, 0, 236, 238, 3, 22, 11, 0, 237, 234, 1, 0, 0, 0, 238, 241, 1, 0, 0, 0, 239, 237, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 19, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 242, 243, 3, 50, 25, 0, 243, 253, 5, 41, 0, 0, 244, 254, 5, 62, 0, 0, 245, 250, 3, 10, 5, 0, 246, 247, 5, 35, 0, 0, 247, 249, 3, 10, 5, 0, 248, 246, 1, 0, 0, 0, 249, 252, 1, 0, 0, 0, 250, 248, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 254, 1, 0, 0, 0, 252, 250, 1, 0, 0, 0, 253, 244, 1, 0, 0, 0, 253, 245, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 256, 5, 51, 0, 0, 256, 21, 1, 0, 0, 0, 257, 258, 3, 50, 25, 0, 258, 23, 1, 0, 0, 0, 259, 260, 5, 14, 0, 0, 260, 261, 3, 26, 13, 0, 261, 25, 1, 0, 0, 0, 262, 267, 3, 28, 14, 0, 263, 264, 5, 35, 0, 0, 264, 266, 3, 28, 14, 0, 265, 263, 1, 0, 0, 0, 266, 269, 1, 0, 0, 0, 267, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 27, 1, 0, 0, 0, 269, 267, 1, 0, 0, 0, 270, 276, 3, 10, 5, 0, 271, 272, 3, 46, 23, 0, 272, 273, 5, 33, 0, 0, 273, 274, 3, 10, 5, 0, 274, 276, 1, 0, 0, 0, 275, 270, 1, 0, 0, 0, 275, 271, 1, 0, 0, 0, 276, 29, 1, 0, 0, 0, 277, 278, 5, 6, 0, 0, 278, 283, 3, 32, 16, 0, 279, 280, 5, 35, 0, 0, 280, 282, 3, 32, 16, 0, 281, 279, 1, 0, 0, 0, 282, 285, 1, 0, 0, 0, 283, 281, 1, 0, 0, 0, 283, 284, 1, 0, 0, 0, 284, 287, 1, 0, 0, 0, 285, 283, 1, 0, 0, 0, 286, 288, 3, 34, 17, 0, 287, 286, 1, 0, 0, 0, 287, 288, 1, 0, 0, 0, 288, 31, 1, 0, 0, 0, 289, 290, 5, 73, 0, 0, 290, 33, 1, 0, 0, 0, 291, 294, 3, 36, 18, 0, 292, 294, 3, 38, 19, 0, 293, 291, 1, 0, 0, 0, 293, 292, 1, 0, 0, 0, 294, 35, 1, 0, 0, 0, 295, 296, 5, 72, 0, 0, 296, 301, 3, 32, 16, 0, 297, 298, 5, 35, 0, 0, 298, 300, 3, 32, 16, 0, 299, 297, 1, 0, 0, 0, 300, 303, 1, 0, 0, 0, 301, 299, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 37, 1, 0, 0, 0, 303, 301, 1, 0, 0, 0, 304, 305, 5, 65, 0, 0, 305, 306, 3, 36, 18, 0, 306, 307, 5, 66, 0, 0, 307, 39, 1, 0, 0, 0, 308, 309, 5, 4, 0, 0, 309, 310, 3, 26, 13, 0, 310, 41, 1, 0, 0, 0, 311, 313, 5, 17, 0, 0, 312, 314, 3, 26, 13, 0, 313, 312, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 317, 1, 0, 0, 0, 315, 316, 5, 30, 0, 0, 316, 318, 3, 26, 13, 0, 317, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 43, 1, 0, 0, 0, 319, 320, 5, 8, 0, 0, 320, 323, 3, 26, 13, 0, 321, 322, 5, 30, 0, 0, 322, 324, 3, 26, 13, 0, 323, 321, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 324, 45, 1, 0, 0, 0, 325, 330, 3, 50, 25, 0, 326, 327, 5, 37, 0, 0, 327, 329, 3, 50, 25, 0, 328, 326, 1, 0, 0, 0, 329, 332, 1, 0, 0, 0, 330, 328, 1, 0, 0, 0, 330, 331, 1, 0, 0, 0, 331, 47, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 333, 338, 3, 52, 26, 0, 334, 335, 5, 37, 0, 0, 335, 337, 3, 52, 26, 0, 336, 334, 1, 0, 0, 0, 337, 340, 1, 0, 0, 0, 338, 336, 1, 0, 0, 0, 338, 339, 1, 0, 0, 0, 339, 49, 1, 0, 0, 0, 340, 338, 1, 0, 0, 0, 341, 342, 7, 2, 0, 0, 342, 51, 1, 0, 0, 0, 343, 344, 5, 77, 0, 0, 344, 53, 1, 0, 0, 0, 345, 388, 5, 46, 0, 0, 346, 347, 3, 86, 43, 0, 347, 348, 5, 67, 0, 0, 348, 388, 1, 0, 0, 0, 349, 388, 3, 84, 42, 0, 350, 388, 3, 86, 43, 0, 351, 388, 3, 80, 40, 0, 352, 388, 5, 49, 0, 0, 353, 388, 3, 88, 44, 0, 354, 355, 5, 65, 0, 0, 355, 360, 3, 82, 41, 0, 356, 357, 5, 35, 0, 0, 357, 359, 3, 82, 41, 0, 358, 356, 1, 0, 0, 0, 359, 362, 1, 0, 0, 0, 360, 358, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 363, 1, 0, 0, 0, 362, 360, 1, 0, 0, 0, 363, 364, 5, 66, 0, 0, 364, 388, 1, 0, 0, 0, 365, 366, 5, 65, 0, 0, 366, 371, 3, 80, 40, 0, 367, 368, 5, 35, 0, 0, 368, 370, 3, 80, 40, 0, 369, 367, 1, 0, 0, 0, 370, 373, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 372, 1, 0, 0, 0, 372, 374, 1, 0, 0, 0, 373, 371, 1, 0, 0, 0, 374, 375, 5, 66, 0, 0, 375, 388, 1, 0, 0, 0, 376, 377, 5, 65, 0, 0, 377, 382, 3, 88, 44, 0, 378, 379, 5, 35, 0, 0, 379, 381, 3, 88, 44, 0, 380, 378, 1, 0, 0, 0, 381, 384, 1, 0, 0, 0, 382, 380, 1, 0, 0, 0, 382, 383, 1, 0, 0, 0, 383, 385, 1, 0, 0, 0, 384, 382, 1, 0, 0, 0, 385, 386, 5, 66, 0, 0, 386, 388, 1, 0, 0, 0, 387, 345, 1, 0, 0, 0, 387, 346, 1, 0, 0, 0, 387, 349, 1, 0, 0, 0, 387, 350, 1, 0, 0, 0, 387, 351, 1, 0, 0, 0, 387, 352, 1, 0, 0, 0, 387, 353, 1, 0, 0, 0, 387, 354, 1, 0, 0, 0, 387, 365, 1, 0, 0, 0, 387, 376, 1, 0, 0, 0, 388, 55, 1, 0, 0, 0, 389, 390, 5, 10, 0, 0, 390, 391, 5, 28, 0, 0, 391, 57, 1, 0, 0, 0, 392, 393, 5, 16, 0, 0, 393, 398, 3, 60, 30, 0, 394, 395, 5, 35, 0, 0, 395, 397, 3, 60, 30, 0, 396, 394, 1, 0, 0, 0, 397, 400, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 398, 399, 1, 0, 0, 0, 399, 59, 1, 0, 0, 0, 400, 398, 1, 0, 0, 0, 401, 403, 3, 10, 5, 0, 402, 404, 7, 3, 0, 0, 403, 402, 1, 0, 0, 0, 403, 404, 1, 0, 0, 0, 404, 407, 1, 0, 0, 0, 405, 406, 5, 47, 0, 0, 406, 408, 7, 4, 0, 0, 407, 405, 1, 0, 0, 0, 407, 408, 1, 0, 0, 0, 408, 61, 1, 0, 0, 0, 409, 410, 5, 9, 0, 0, 410, 415, 3, 48, 24, 0, 411, 412, 5, 35, 0, 0, 412, 414, 3, 48, 24, 0, 413, 411, 1, 0, 0, 0, 414, 417, 1, 0, 0, 0, 415, 413, 1, 0, 0, 0, 415, 416, 1, 0, 0, 0, 416, 63, 1, 0, 0, 0, 417, 415, 1, 0, 0, 0, 418, 419, 5, 2, 0, 0, 419, 424, 3, 48, 24, 0, 420, 421, 5, 35, 0, 0, 421, 423, 3, 48, 24, 0, 422, 420, 1, 0, 0, 0, 423, 426, 1, 0, 0, 0, 424, 422, 1, 0, 0, 0, 424, 425, 1, 0, 0, 0, 425, 65, 1, 0, 0, 0, 426, 424, 1, 0, 0, 0, 427, 428, 5, 13, 0, 0, 428, 433, 3, 68, 34, 0, 429, 430, 5, 35, 0, 0, 430, 432, 3, 68, 34, 0, 431, 429, 1, 0, 0, 0, 432, 435, 1, 0, 0, 0, 433, 431, 1, 0, 0, 0, 433, 434, 1, 0, 0, 0, 434, 67, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 436, 437, 3, 48, 24, 0, 437, 438, 5, 81, 0, 0, 438, 439, 3, 48, 24, 0, 439, 69, 1, 0, 0, 0, 440, 441, 5, 1, 0, 0, 441, 442, 3, 18, 9, 0, 442, 444, 3, 88, 44, 0, 443, 445, 3, 76, 38, 0, 444, 443, 1, 0, 0, 0, 444, 445, 1, 0, 0, 0, 445, 71, 1, 0, 0, 0, 446, 447, 5, 7, 0, 0, 447, 448, 3, 18, 9, 0, 448, 449, 3, 88, 44, 0, 449, 73, 1, 0, 0, 0, 450, 451, 5, 12, 0, 0, 451, 452, 3, 46, 23, 0, 452, 75, 1, 0, 0, 0, 453, 458, 3, 78, 39, 0, 454, 455, 5, 35, 0, 0, 455, 457, 3, 78, 39, 0, 456, 454, 1, 0, 0, 0, 457, 460, 1, 0, 0, 0, 458, 456, 1, 0, 0, 0, 458, 459, 1, 0, 0, 0, 459, 77, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0, 461, 462, 3, 50, 25, 0, 462, 463, 5, 33, 0, 0, 463, 464, 3, 54, 27, 0, 464, 79, 1, 0, 0, 0, 465, 466, 7, 5, 0, 0, 466, 81, 1, 0, 0, 0, 467, 470, 3, 84, 42, 0, 468, 470, 3, 86, 43, 0, 469, 467, 1, 0, 0, 0, 469, 468, 1, 0, 0, 0, 470, 83, 1, 0, 0, 0, 471, 473, 7, 0, 0, 0, 472, 471, 1, 0, 0, 0, 472, 473, 1, 0, 0, 0, 473, 474, 1, 0, 0, 0, 474, 475, 5, 29, 0, 0, 475, 85, 1, 0, 0, 0, 476, 478, 7, 0, 0, 0, 477, 476, 1, 0, 0, 0, 477, 478, 1, 0, 0, 0, 478, 479, 1, 0, 0, 0, 479, 480, 5, 28, 0, 0, 480, 87, 1, 0, 0, 0, 481, 482, 5, 27, 0, 0, 482, 89, 1, 0, 0, 0, 483, 484, 7, 6, 0, 0, 484, 91, 1, 0, 0, 0, 485, 486, 5, 5, 0, 0, 486, 487, 3, 94, 47, 0, 487, 93, 1, 0, 0, 0, 488, 489, 5, 65, 0, 0, 489, 490, 3, 2, 1, 0, 490, 491, 5, 66, 0, 0, 491, 95, 1, 0, 0, 0, 492, 493, 5, 15, 0, 0, 493, 494, 5, 97, 0, 0, 494, 97, 1, 0, 0, 0, 495, 496, 5, 11, 0, 0, 496, 497, 5, 101, 0, 0, 497, 99, 1, 0, 0, 0, 498, 499, 5, 3, 0, 0, 499, 502, 5, 87, 0, 0, 500, 501, 5, 85, 0, 0, 501, 503, 3, 48, 24, 0, 502, 500, 1, 0, 0, 0, 502, 503, 1, 0, 0, 0, 503, 513, 1, 0, 0, 0, 504, 505, 5, 86, 0, 0, 505, 510, 3, 102, 51, 0, 506, 507, 5, 35, 0, 0, 507, 509, 3, 102, 51, 0, 508, 506, 1, 0, 0, 0, 509, 512, 1, 0, 0, 0, 510, 508, 1, 0, 0, 0, 510, 511, 1, 0, 0, 0, 511, 514, 1, 0, 0, 0, 512, 510, 1, 0, 0, 0, 513, 504, 1, 0, 0, 0, 513, 514, 1, 0, 0, 0, 514, 101, 1, 0, 0, 0, 515, 516, 3, 48, 24, 0, 516, 517, 5, 33, 0, 0, 517, 519, 1, 0, 0, 0, 518, 515, 1, 0, 0, 0, 518, 519, 1, 0, 0, 0, 519, 520, 1, 0, 0, 0, 520, 521, 3, 48, 24, 0, 521, 103, 1, 0, 0, 0, 50, 115, 123, 138, 150, 159, 167, 171, 179, 181, 186, 193, 198, 205, 211, 219, 221, 232, 239, 250, 253, 267, 275, 283, 287, 293, 301, 313, 317, 323, 330, 338, 360, 371, 382, 387, 398, 403, 407, 415, 424, 433, 444, 458, 469, 472, 477, 502, 510, 513, 518] \ No newline at end of file diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.tokens b/packages/kbn-esql-ast/src/antlr/esql_parser.tokens index b496aa68b61f7..d2e7a695282ec 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.tokens +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.tokens @@ -69,45 +69,44 @@ QUOTED_IDENTIFIER=68 EXPR_LINE_COMMENT=69 EXPR_MULTILINE_COMMENT=70 EXPR_WS=71 -OPTIONS=72 -METADATA=73 -FROM_UNQUOTED_IDENTIFIER=74 -FROM_LINE_COMMENT=75 -FROM_MULTILINE_COMMENT=76 -FROM_WS=77 -ID_PATTERN=78 -PROJECT_LINE_COMMENT=79 -PROJECT_MULTILINE_COMMENT=80 -PROJECT_WS=81 -AS=82 -RENAME_LINE_COMMENT=83 -RENAME_MULTILINE_COMMENT=84 -RENAME_WS=85 -ON=86 -WITH=87 -ENRICH_POLICY_NAME=88 -ENRICH_LINE_COMMENT=89 -ENRICH_MULTILINE_COMMENT=90 -ENRICH_WS=91 -ENRICH_FIELD_LINE_COMMENT=92 -ENRICH_FIELD_MULTILINE_COMMENT=93 -ENRICH_FIELD_WS=94 -MVEXPAND_LINE_COMMENT=95 -MVEXPAND_MULTILINE_COMMENT=96 -MVEXPAND_WS=97 -INFO=98 -SHOW_LINE_COMMENT=99 -SHOW_MULTILINE_COMMENT=100 -SHOW_WS=101 -FUNCTIONS=102 -META_LINE_COMMENT=103 -META_MULTILINE_COMMENT=104 -META_WS=105 -COLON=106 -SETTING=107 -SETTING_LINE_COMMENT=108 -SETTTING_MULTILINE_COMMENT=109 -SETTING_WS=110 +METADATA=72 +FROM_UNQUOTED_IDENTIFIER=73 +FROM_LINE_COMMENT=74 +FROM_MULTILINE_COMMENT=75 +FROM_WS=76 +ID_PATTERN=77 +PROJECT_LINE_COMMENT=78 +PROJECT_MULTILINE_COMMENT=79 +PROJECT_WS=80 +AS=81 +RENAME_LINE_COMMENT=82 +RENAME_MULTILINE_COMMENT=83 +RENAME_WS=84 +ON=85 +WITH=86 +ENRICH_POLICY_NAME=87 +ENRICH_LINE_COMMENT=88 +ENRICH_MULTILINE_COMMENT=89 +ENRICH_WS=90 +ENRICH_FIELD_LINE_COMMENT=91 +ENRICH_FIELD_MULTILINE_COMMENT=92 +ENRICH_FIELD_WS=93 +MVEXPAND_LINE_COMMENT=94 +MVEXPAND_MULTILINE_COMMENT=95 +MVEXPAND_WS=96 +INFO=97 +SHOW_LINE_COMMENT=98 +SHOW_MULTILINE_COMMENT=99 +SHOW_WS=100 +FUNCTIONS=101 +META_LINE_COMMENT=102 +META_MULTILINE_COMMENT=103 +META_WS=104 +COLON=105 +SETTING=106 +SETTING_LINE_COMMENT=107 +SETTTING_MULTILINE_COMMENT=108 +SETTING_WS=109 'dissect'=1 'drop'=2 'enrich'=3 @@ -163,11 +162,10 @@ SETTING_WS=110 '/'=63 '%'=64 ']'=66 -'options'=72 -'metadata'=73 -'as'=82 -'on'=86 -'with'=87 -'info'=98 -'functions'=102 -':'=106 +'metadata'=72 +'as'=81 +'on'=85 +'with'=86 +'info'=97 +'functions'=101 +':'=105 diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser.ts b/packages/kbn-esql-ast/src/antlr/esql_parser.ts index f8151aae343e8..d0258b35756e9 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_parser.ts @@ -89,45 +89,44 @@ export default class esql_parser extends Parser { public static readonly EXPR_LINE_COMMENT = 69; public static readonly EXPR_MULTILINE_COMMENT = 70; public static readonly EXPR_WS = 71; - public static readonly OPTIONS = 72; - public static readonly METADATA = 73; - public static readonly FROM_UNQUOTED_IDENTIFIER = 74; - public static readonly FROM_LINE_COMMENT = 75; - public static readonly FROM_MULTILINE_COMMENT = 76; - public static readonly FROM_WS = 77; - public static readonly ID_PATTERN = 78; - public static readonly PROJECT_LINE_COMMENT = 79; - public static readonly PROJECT_MULTILINE_COMMENT = 80; - public static readonly PROJECT_WS = 81; - public static readonly AS = 82; - public static readonly RENAME_LINE_COMMENT = 83; - public static readonly RENAME_MULTILINE_COMMENT = 84; - public static readonly RENAME_WS = 85; - public static readonly ON = 86; - public static readonly WITH = 87; - public static readonly ENRICH_POLICY_NAME = 88; - public static readonly ENRICH_LINE_COMMENT = 89; - public static readonly ENRICH_MULTILINE_COMMENT = 90; - public static readonly ENRICH_WS = 91; - public static readonly ENRICH_FIELD_LINE_COMMENT = 92; - public static readonly ENRICH_FIELD_MULTILINE_COMMENT = 93; - public static readonly ENRICH_FIELD_WS = 94; - public static readonly MVEXPAND_LINE_COMMENT = 95; - public static readonly MVEXPAND_MULTILINE_COMMENT = 96; - public static readonly MVEXPAND_WS = 97; - public static readonly INFO = 98; - public static readonly SHOW_LINE_COMMENT = 99; - public static readonly SHOW_MULTILINE_COMMENT = 100; - public static readonly SHOW_WS = 101; - public static readonly FUNCTIONS = 102; - public static readonly META_LINE_COMMENT = 103; - public static readonly META_MULTILINE_COMMENT = 104; - public static readonly META_WS = 105; - public static readonly COLON = 106; - public static readonly SETTING = 107; - public static readonly SETTING_LINE_COMMENT = 108; - public static readonly SETTTING_MULTILINE_COMMENT = 109; - public static readonly SETTING_WS = 110; + public static readonly METADATA = 72; + public static readonly FROM_UNQUOTED_IDENTIFIER = 73; + public static readonly FROM_LINE_COMMENT = 74; + public static readonly FROM_MULTILINE_COMMENT = 75; + public static readonly FROM_WS = 76; + public static readonly ID_PATTERN = 77; + public static readonly PROJECT_LINE_COMMENT = 78; + public static readonly PROJECT_MULTILINE_COMMENT = 79; + public static readonly PROJECT_WS = 80; + public static readonly AS = 81; + public static readonly RENAME_LINE_COMMENT = 82; + public static readonly RENAME_MULTILINE_COMMENT = 83; + public static readonly RENAME_WS = 84; + public static readonly ON = 85; + public static readonly WITH = 86; + public static readonly ENRICH_POLICY_NAME = 87; + public static readonly ENRICH_LINE_COMMENT = 88; + public static readonly ENRICH_MULTILINE_COMMENT = 89; + public static readonly ENRICH_WS = 90; + public static readonly ENRICH_FIELD_LINE_COMMENT = 91; + public static readonly ENRICH_FIELD_MULTILINE_COMMENT = 92; + public static readonly ENRICH_FIELD_WS = 93; + public static readonly MVEXPAND_LINE_COMMENT = 94; + public static readonly MVEXPAND_MULTILINE_COMMENT = 95; + public static readonly MVEXPAND_WS = 96; + public static readonly INFO = 97; + public static readonly SHOW_LINE_COMMENT = 98; + public static readonly SHOW_MULTILINE_COMMENT = 99; + public static readonly SHOW_WS = 100; + public static readonly FUNCTIONS = 101; + public static readonly META_LINE_COMMENT = 102; + public static readonly META_MULTILINE_COMMENT = 103; + public static readonly META_WS = 104; + public static readonly COLON = 105; + public static readonly SETTING = 106; + public static readonly SETTING_LINE_COMMENT = 107; + public static readonly SETTTING_MULTILINE_COMMENT = 108; + public static readonly SETTING_WS = 109; public static readonly EOF = Token.EOF; public static readonly RULE_singleStatement = 0; public static readonly RULE_query = 1; @@ -146,43 +145,41 @@ export default class esql_parser extends Parser { public static readonly RULE_field = 14; public static readonly RULE_fromCommand = 15; public static readonly RULE_fromIdentifier = 16; - public static readonly RULE_fromOptions = 17; - public static readonly RULE_configOption = 18; - public static readonly RULE_metadata = 19; - public static readonly RULE_metadataOption = 20; - public static readonly RULE_deprecated_metadata = 21; - public static readonly RULE_evalCommand = 22; - public static readonly RULE_statsCommand = 23; - public static readonly RULE_inlinestatsCommand = 24; - public static readonly RULE_qualifiedName = 25; - public static readonly RULE_qualifiedNamePattern = 26; - public static readonly RULE_identifier = 27; - public static readonly RULE_identifierPattern = 28; - public static readonly RULE_constant = 29; - public static readonly RULE_limitCommand = 30; - public static readonly RULE_sortCommand = 31; - public static readonly RULE_orderExpression = 32; - public static readonly RULE_keepCommand = 33; - public static readonly RULE_dropCommand = 34; - public static readonly RULE_renameCommand = 35; - public static readonly RULE_renameClause = 36; - public static readonly RULE_dissectCommand = 37; - public static readonly RULE_grokCommand = 38; - public static readonly RULE_mvExpandCommand = 39; - public static readonly RULE_commandOptions = 40; - public static readonly RULE_commandOption = 41; - public static readonly RULE_booleanValue = 42; - public static readonly RULE_numericValue = 43; - public static readonly RULE_decimalValue = 44; - public static readonly RULE_integerValue = 45; - public static readonly RULE_string = 46; - public static readonly RULE_comparisonOperator = 47; - public static readonly RULE_explainCommand = 48; - public static readonly RULE_subqueryExpression = 49; - public static readonly RULE_showCommand = 50; - public static readonly RULE_metaCommand = 51; - public static readonly RULE_enrichCommand = 52; - public static readonly RULE_enrichWithClause = 53; + public static readonly RULE_metadata = 17; + public static readonly RULE_metadataOption = 18; + public static readonly RULE_deprecated_metadata = 19; + public static readonly RULE_evalCommand = 20; + public static readonly RULE_statsCommand = 21; + public static readonly RULE_inlinestatsCommand = 22; + public static readonly RULE_qualifiedName = 23; + public static readonly RULE_qualifiedNamePattern = 24; + public static readonly RULE_identifier = 25; + public static readonly RULE_identifierPattern = 26; + public static readonly RULE_constant = 27; + public static readonly RULE_limitCommand = 28; + public static readonly RULE_sortCommand = 29; + public static readonly RULE_orderExpression = 30; + public static readonly RULE_keepCommand = 31; + public static readonly RULE_dropCommand = 32; + public static readonly RULE_renameCommand = 33; + public static readonly RULE_renameClause = 34; + public static readonly RULE_dissectCommand = 35; + public static readonly RULE_grokCommand = 36; + public static readonly RULE_mvExpandCommand = 37; + public static readonly RULE_commandOptions = 38; + public static readonly RULE_commandOption = 39; + public static readonly RULE_booleanValue = 40; + public static readonly RULE_numericValue = 41; + public static readonly RULE_decimalValue = 42; + public static readonly RULE_integerValue = 43; + public static readonly RULE_string = 44; + public static readonly RULE_comparisonOperator = 45; + public static readonly RULE_explainCommand = 46; + public static readonly RULE_subqueryExpression = 47; + public static readonly RULE_showCommand = 48; + public static readonly RULE_metaCommand = 49; + public static readonly RULE_enrichCommand = 50; + public static readonly RULE_enrichWithClause = 51; public static readonly literalNames: (string | null)[] = [ null, "'dissect'", "'drop'", "'enrich'", "'eval'", "'explain'", @@ -220,7 +217,6 @@ export default class esql_parser extends Parser { "']'", null, null, null, null, null, - "'options'", "'metadata'", null, null, null, null, @@ -283,7 +279,6 @@ export default class esql_parser extends Parser { "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", - "OPTIONS", "METADATA", "FROM_UNQUOTED_IDENTIFIER", "FROM_LINE_COMMENT", @@ -323,12 +318,12 @@ export default class esql_parser extends Parser { "singleStatement", "query", "sourceCommand", "processingCommand", "whereCommand", "booleanExpression", "regexBooleanExpression", "valueExpression", "operatorExpression", "primaryExpression", "functionExpression", "dataType", "rowCommand", "fields", - "field", "fromCommand", "fromIdentifier", "fromOptions", "configOption", - "metadata", "metadataOption", "deprecated_metadata", "evalCommand", "statsCommand", - "inlinestatsCommand", "qualifiedName", "qualifiedNamePattern", "identifier", - "identifierPattern", "constant", "limitCommand", "sortCommand", "orderExpression", - "keepCommand", "dropCommand", "renameCommand", "renameClause", "dissectCommand", - "grokCommand", "mvExpandCommand", "commandOptions", "commandOption", "booleanValue", + "field", "fromCommand", "fromIdentifier", "metadata", "metadataOption", + "deprecated_metadata", "evalCommand", "statsCommand", "inlinestatsCommand", + "qualifiedName", "qualifiedNamePattern", "identifier", "identifierPattern", + "constant", "limitCommand", "sortCommand", "orderExpression", "keepCommand", + "dropCommand", "renameCommand", "renameClause", "dissectCommand", "grokCommand", + "mvExpandCommand", "commandOptions", "commandOption", "booleanValue", "numericValue", "decimalValue", "integerValue", "string", "comparisonOperator", "explainCommand", "subqueryExpression", "showCommand", "metaCommand", "enrichCommand", "enrichWithClause", @@ -354,9 +349,9 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 108; + this.state = 104; this.query(0); - this.state = 109; + this.state = 105; this.match(esql_parser.EOF); } } @@ -398,11 +393,11 @@ export default class esql_parser extends Parser { this._ctx = localctx; _prevctx = localctx; - this.state = 112; + this.state = 108; this.sourceCommand(); } this._ctx.stop = this._input.LT(-1); - this.state = 119; + this.state = 115; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 0, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -415,18 +410,18 @@ export default class esql_parser extends Parser { { localctx = new CompositeQueryContext(this, new QueryContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_query); - this.state = 114; + this.state = 110; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 115; + this.state = 111; this.match(esql_parser.PIPE); - this.state = 116; + this.state = 112; this.processingCommand(); } } } - this.state = 121; + this.state = 117; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 0, this._ctx); } @@ -451,41 +446,41 @@ export default class esql_parser extends Parser { let localctx: SourceCommandContext = new SourceCommandContext(this, this._ctx, this.state); this.enterRule(localctx, 4, esql_parser.RULE_sourceCommand); try { - this.state = 127; + this.state = 123; this._errHandler.sync(this); switch (this._input.LA(1)) { case 5: this.enterOuterAlt(localctx, 1); { - this.state = 122; + this.state = 118; this.explainCommand(); } break; case 6: this.enterOuterAlt(localctx, 2); { - this.state = 123; + this.state = 119; this.fromCommand(); } break; case 14: this.enterOuterAlt(localctx, 3); { - this.state = 124; + this.state = 120; this.rowCommand(); } break; case 15: this.enterOuterAlt(localctx, 4); { - this.state = 125; + this.state = 121; this.showCommand(); } break; case 11: this.enterOuterAlt(localctx, 5); { - this.state = 126; + this.state = 122; this.metaCommand(); } break; @@ -512,97 +507,97 @@ export default class esql_parser extends Parser { let localctx: ProcessingCommandContext = new ProcessingCommandContext(this, this._ctx, this.state); this.enterRule(localctx, 6, esql_parser.RULE_processingCommand); try { - this.state = 142; + this.state = 138; this._errHandler.sync(this); switch (this._input.LA(1)) { case 4: this.enterOuterAlt(localctx, 1); { - this.state = 129; + this.state = 125; this.evalCommand(); } break; case 8: this.enterOuterAlt(localctx, 2); { - this.state = 130; + this.state = 126; this.inlinestatsCommand(); } break; case 10: this.enterOuterAlt(localctx, 3); { - this.state = 131; + this.state = 127; this.limitCommand(); } break; case 9: this.enterOuterAlt(localctx, 4); { - this.state = 132; + this.state = 128; this.keepCommand(); } break; case 16: this.enterOuterAlt(localctx, 5); { - this.state = 133; + this.state = 129; this.sortCommand(); } break; case 17: this.enterOuterAlt(localctx, 6); { - this.state = 134; + this.state = 130; this.statsCommand(); } break; case 18: this.enterOuterAlt(localctx, 7); { - this.state = 135; + this.state = 131; this.whereCommand(); } break; case 2: this.enterOuterAlt(localctx, 8); { - this.state = 136; + this.state = 132; this.dropCommand(); } break; case 13: this.enterOuterAlt(localctx, 9); { - this.state = 137; + this.state = 133; this.renameCommand(); } break; case 1: this.enterOuterAlt(localctx, 10); { - this.state = 138; + this.state = 134; this.dissectCommand(); } break; case 7: this.enterOuterAlt(localctx, 11); { - this.state = 139; + this.state = 135; this.grokCommand(); } break; case 3: this.enterOuterAlt(localctx, 12); { - this.state = 140; + this.state = 136; this.enrichCommand(); } break; case 12: this.enterOuterAlt(localctx, 13); { - this.state = 141; + this.state = 137; this.mvExpandCommand(); } break; @@ -631,9 +626,9 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 144; + this.state = 140; this.match(esql_parser.WHERE); - this.state = 145; + this.state = 141; this.booleanExpression(0); } } @@ -671,7 +666,7 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 175; + this.state = 171; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 6, this._ctx) ) { case 1: @@ -680,9 +675,9 @@ export default class esql_parser extends Parser { this._ctx = localctx; _prevctx = localctx; - this.state = 148; + this.state = 144; this.match(esql_parser.NOT); - this.state = 149; + this.state = 145; this.booleanExpression(7); } break; @@ -691,7 +686,7 @@ export default class esql_parser extends Parser { localctx = new BooleanDefaultContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 150; + this.state = 146; this.valueExpression(); } break; @@ -700,7 +695,7 @@ export default class esql_parser extends Parser { localctx = new RegexExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 151; + this.state = 147; this.regexBooleanExpression(); } break; @@ -709,41 +704,41 @@ export default class esql_parser extends Parser { localctx = new LogicalInContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 152; + this.state = 148; this.valueExpression(); - this.state = 154; + this.state = 150; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===45) { { - this.state = 153; + this.state = 149; this.match(esql_parser.NOT); } } - this.state = 156; + this.state = 152; this.match(esql_parser.IN); - this.state = 157; + this.state = 153; this.match(esql_parser.LP); - this.state = 158; + this.state = 154; this.valueExpression(); - this.state = 163; + this.state = 159; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===35) { { { - this.state = 159; + this.state = 155; this.match(esql_parser.COMMA); - this.state = 160; + this.state = 156; this.valueExpression(); } } - this.state = 165; + this.state = 161; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 166; + this.state = 162; this.match(esql_parser.RP); } break; @@ -752,27 +747,27 @@ export default class esql_parser extends Parser { localctx = new IsNullContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 168; + this.state = 164; this.valueExpression(); - this.state = 169; + this.state = 165; this.match(esql_parser.IS); - this.state = 171; + this.state = 167; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===45) { { - this.state = 170; + this.state = 166; this.match(esql_parser.NOT); } } - this.state = 173; + this.state = 169; this.match(esql_parser.NULL); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 185; + this.state = 181; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 8, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -782,7 +777,7 @@ export default class esql_parser extends Parser { } _prevctx = localctx; { - this.state = 183; + this.state = 179; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 7, this._ctx) ) { case 1: @@ -790,13 +785,13 @@ export default class esql_parser extends Parser { localctx = new LogicalBinaryContext(this, new BooleanExpressionContext(this, _parentctx, _parentState)); (localctx as LogicalBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 177; + this.state = 173; if (!(this.precpred(this._ctx, 4))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 4)"); } - this.state = 178; + this.state = 174; (localctx as LogicalBinaryContext)._operator = this.match(esql_parser.AND); - this.state = 179; + this.state = 175; (localctx as LogicalBinaryContext)._right = this.booleanExpression(5); } break; @@ -805,20 +800,20 @@ export default class esql_parser extends Parser { localctx = new LogicalBinaryContext(this, new BooleanExpressionContext(this, _parentctx, _parentState)); (localctx as LogicalBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 180; + this.state = 176; if (!(this.precpred(this._ctx, 3))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 3)"); } - this.state = 181; + this.state = 177; (localctx as LogicalBinaryContext)._operator = this.match(esql_parser.OR); - this.state = 182; + this.state = 178; (localctx as LogicalBinaryContext)._right = this.booleanExpression(4); } break; } } } - this.state = 187; + this.state = 183; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 8, this._ctx); } @@ -844,48 +839,48 @@ export default class esql_parser extends Parser { this.enterRule(localctx, 12, esql_parser.RULE_regexBooleanExpression); let _la: number; try { - this.state = 202; + this.state = 198; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 11, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 188; + this.state = 184; this.valueExpression(); - this.state = 190; + this.state = 186; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===45) { { - this.state = 189; + this.state = 185; this.match(esql_parser.NOT); } } - this.state = 192; + this.state = 188; localctx._kind = this.match(esql_parser.LIKE); - this.state = 193; + this.state = 189; localctx._pattern = this.string_(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 195; + this.state = 191; this.valueExpression(); - this.state = 197; + this.state = 193; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===45) { { - this.state = 196; + this.state = 192; this.match(esql_parser.NOT); } } - this.state = 199; + this.state = 195; localctx._kind = this.match(esql_parser.RLIKE); - this.state = 200; + this.state = 196; localctx._pattern = this.string_(); } break; @@ -910,14 +905,14 @@ export default class esql_parser extends Parser { let localctx: ValueExpressionContext = new ValueExpressionContext(this, this._ctx, this.state); this.enterRule(localctx, 14, esql_parser.RULE_valueExpression); try { - this.state = 209; + this.state = 205; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 12, this._ctx) ) { case 1: localctx = new ValueExpressionDefaultContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 204; + this.state = 200; this.operatorExpression(0); } break; @@ -925,11 +920,11 @@ export default class esql_parser extends Parser { localctx = new ComparisonContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 205; + this.state = 201; (localctx as ComparisonContext)._left = this.operatorExpression(0); - this.state = 206; + this.state = 202; this.comparisonOperator(); - this.state = 207; + this.state = 203; (localctx as ComparisonContext)._right = this.operatorExpression(0); } break; @@ -969,7 +964,7 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 215; + this.state = 211; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 13, this._ctx) ) { case 1: @@ -978,7 +973,7 @@ export default class esql_parser extends Parser { this._ctx = localctx; _prevctx = localctx; - this.state = 212; + this.state = 208; this.primaryExpression(0); } break; @@ -987,7 +982,7 @@ export default class esql_parser extends Parser { localctx = new ArithmeticUnaryContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 213; + this.state = 209; (localctx as ArithmeticUnaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); if(!(_la===60 || _la===61)) { @@ -997,13 +992,13 @@ export default class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 214; + this.state = 210; this.operatorExpression(3); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 225; + this.state = 221; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 15, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -1013,7 +1008,7 @@ export default class esql_parser extends Parser { } _prevctx = localctx; { - this.state = 223; + this.state = 219; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 14, this._ctx) ) { case 1: @@ -1021,11 +1016,11 @@ export default class esql_parser extends Parser { localctx = new ArithmeticBinaryContext(this, new OperatorExpressionContext(this, _parentctx, _parentState)); (localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 217; + this.state = 213; if (!(this.precpred(this._ctx, 2))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 2)"); } - this.state = 218; + this.state = 214; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); if(!(((((_la - 62)) & ~0x1F) === 0 && ((1 << (_la - 62)) & 7) !== 0))) { @@ -1035,7 +1030,7 @@ export default class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 219; + this.state = 215; (localctx as ArithmeticBinaryContext)._right = this.operatorExpression(3); } break; @@ -1044,11 +1039,11 @@ export default class esql_parser extends Parser { localctx = new ArithmeticBinaryContext(this, new OperatorExpressionContext(this, _parentctx, _parentState)); (localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 220; + this.state = 216; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 221; + this.state = 217; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); if(!(_la===60 || _la===61)) { @@ -1058,14 +1053,14 @@ export default class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 222; + this.state = 218; (localctx as ArithmeticBinaryContext)._right = this.operatorExpression(2); } break; } } } - this.state = 227; + this.state = 223; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 15, this._ctx); } @@ -1104,7 +1099,7 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 236; + this.state = 232; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 16, this._ctx) ) { case 1: @@ -1113,7 +1108,7 @@ export default class esql_parser extends Parser { this._ctx = localctx; _prevctx = localctx; - this.state = 229; + this.state = 225; this.constant(); } break; @@ -1122,7 +1117,7 @@ export default class esql_parser extends Parser { localctx = new DereferenceContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 230; + this.state = 226; this.qualifiedName(); } break; @@ -1131,7 +1126,7 @@ export default class esql_parser extends Parser { localctx = new FunctionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 231; + this.state = 227; this.functionExpression(); } break; @@ -1140,17 +1135,17 @@ export default class esql_parser extends Parser { localctx = new ParenthesizedExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 232; + this.state = 228; this.match(esql_parser.LP); - this.state = 233; + this.state = 229; this.booleanExpression(0); - this.state = 234; + this.state = 230; this.match(esql_parser.RP); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 243; + this.state = 239; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 17, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -1163,18 +1158,18 @@ export default class esql_parser extends Parser { { localctx = new InlineCastContext(this, new PrimaryExpressionContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_primaryExpression); - this.state = 238; + this.state = 234; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 239; + this.state = 235; this.match(esql_parser.CAST_OP); - this.state = 240; + this.state = 236; this.dataType(); } } } - this.state = 245; + this.state = 241; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 17, this._ctx); } @@ -1202,16 +1197,16 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 246; + this.state = 242; this.identifier(); - this.state = 247; + this.state = 243; this.match(esql_parser.LP); - this.state = 257; + this.state = 253; this._errHandler.sync(this); switch (this._input.LA(1)) { case 62: { - this.state = 248; + this.state = 244; this.match(esql_parser.ASTERISK); } break; @@ -1231,21 +1226,21 @@ export default class esql_parser extends Parser { case 68: { { - this.state = 249; + this.state = 245; this.booleanExpression(0); - this.state = 254; + this.state = 250; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===35) { { { - this.state = 250; + this.state = 246; this.match(esql_parser.COMMA); - this.state = 251; + this.state = 247; this.booleanExpression(0); } } - this.state = 256; + this.state = 252; this._errHandler.sync(this); _la = this._input.LA(1); } @@ -1257,7 +1252,7 @@ export default class esql_parser extends Parser { default: break; } - this.state = 259; + this.state = 255; this.match(esql_parser.RP); } } @@ -1283,7 +1278,7 @@ export default class esql_parser extends Parser { localctx = new ToDataTypeContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 261; + this.state = 257; this.identifier(); } } @@ -1308,9 +1303,9 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 263; + this.state = 259; this.match(esql_parser.ROW); - this.state = 264; + this.state = 260; this.fields(); } } @@ -1336,23 +1331,23 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 266; + this.state = 262; this.field(); - this.state = 271; + this.state = 267; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 267; + this.state = 263; this.match(esql_parser.COMMA); - this.state = 268; + this.state = 264; this.field(); } } } - this.state = 273; + this.state = 269; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 20, this._ctx); } @@ -1377,24 +1372,24 @@ export default class esql_parser extends Parser { let localctx: FieldContext = new FieldContext(this, this._ctx, this.state); this.enterRule(localctx, 28, esql_parser.RULE_field); try { - this.state = 279; + this.state = 275; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 21, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 274; + this.state = 270; this.booleanExpression(0); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 275; + this.state = 271; this.qualifiedName(); - this.state = 276; + this.state = 272; this.match(esql_parser.ASSIGN); - this.state = 277; + this.state = 273; this.booleanExpression(0); } break; @@ -1422,48 +1417,38 @@ export default class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 281; + this.state = 277; this.match(esql_parser.FROM); - this.state = 282; + this.state = 278; this.fromIdentifier(); - this.state = 287; + this.state = 283; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 283; + this.state = 279; this.match(esql_parser.COMMA); - this.state = 284; + this.state = 280; this.fromIdentifier(); } } } - this.state = 289; + this.state = 285; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 22, this._ctx); } - this.state = 291; + this.state = 287; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 23, this._ctx) ) { case 1: { - this.state = 290; + this.state = 286; this.metadata(); } break; } - this.state = 294; - this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 24, this._ctx) ) { - case 1: - { - this.state = 293; - this.fromOptions(); - } - break; - } } } catch (re) { @@ -1487,7 +1472,7 @@ export default class esql_parser extends Parser { try { this.enterOuterAlt(localctx, 1); { - this.state = 296; + this.state = 289; this.match(esql_parser.FROM_UNQUOTED_IDENTIFIER); } } @@ -1506,99 +1491,24 @@ export default class esql_parser extends Parser { return localctx; } // @RuleVersion(0) - public fromOptions(): FromOptionsContext { - let localctx: FromOptionsContext = new FromOptionsContext(this, this._ctx, this.state); - this.enterRule(localctx, 34, esql_parser.RULE_fromOptions); - try { - let _alt: number; - this.enterOuterAlt(localctx, 1); - { - this.state = 298; - this.match(esql_parser.OPTIONS); - this.state = 299; - this.configOption(); - this.state = 304; - this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); - while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { - if (_alt === 1) { - { - { - this.state = 300; - this.match(esql_parser.COMMA); - this.state = 301; - this.configOption(); - } - } - } - this.state = 306; - this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); - } - } - } - catch (re) { - if (re instanceof RecognitionException) { - localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return localctx; - } - // @RuleVersion(0) - public configOption(): ConfigOptionContext { - let localctx: ConfigOptionContext = new ConfigOptionContext(this, this._ctx, this.state); - this.enterRule(localctx, 36, esql_parser.RULE_configOption); - try { - this.enterOuterAlt(localctx, 1); - { - this.state = 307; - this.string_(); - this.state = 308; - this.match(esql_parser.ASSIGN); - this.state = 309; - this.string_(); - } - } - catch (re) { - if (re instanceof RecognitionException) { - localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return localctx; - } - // @RuleVersion(0) public metadata(): MetadataContext { let localctx: MetadataContext = new MetadataContext(this, this._ctx, this.state); - this.enterRule(localctx, 38, esql_parser.RULE_metadata); + this.enterRule(localctx, 34, esql_parser.RULE_metadata); try { - this.state = 313; + this.state = 293; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 73: + case 72: this.enterOuterAlt(localctx, 1); { - this.state = 311; + this.state = 291; this.metadataOption(); } break; case 65: this.enterOuterAlt(localctx, 2); { - this.state = 312; + this.state = 292; this.deprecated_metadata(); } break; @@ -1623,32 +1533,32 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public metadataOption(): MetadataOptionContext { let localctx: MetadataOptionContext = new MetadataOptionContext(this, this._ctx, this.state); - this.enterRule(localctx, 40, esql_parser.RULE_metadataOption); + this.enterRule(localctx, 36, esql_parser.RULE_metadataOption); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 315; + this.state = 295; this.match(esql_parser.METADATA); - this.state = 316; + this.state = 296; this.fromIdentifier(); - this.state = 321; + this.state = 301; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 317; + this.state = 297; this.match(esql_parser.COMMA); - this.state = 318; + this.state = 298; this.fromIdentifier(); } } } - this.state = 323; + this.state = 303; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 27, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); } } } @@ -1669,15 +1579,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public deprecated_metadata(): Deprecated_metadataContext { let localctx: Deprecated_metadataContext = new Deprecated_metadataContext(this, this._ctx, this.state); - this.enterRule(localctx, 42, esql_parser.RULE_deprecated_metadata); + this.enterRule(localctx, 38, esql_parser.RULE_deprecated_metadata); try { this.enterOuterAlt(localctx, 1); { - this.state = 324; + this.state = 304; this.match(esql_parser.OPENING_BRACKET); - this.state = 325; + this.state = 305; this.metadataOption(); - this.state = 326; + this.state = 306; this.match(esql_parser.CLOSING_BRACKET); } } @@ -1698,13 +1608,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public evalCommand(): EvalCommandContext { let localctx: EvalCommandContext = new EvalCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 44, esql_parser.RULE_evalCommand); + this.enterRule(localctx, 40, esql_parser.RULE_evalCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 328; + this.state = 308; this.match(esql_parser.EVAL); - this.state = 329; + this.state = 309; this.fields(); } } @@ -1725,30 +1635,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public statsCommand(): StatsCommandContext { let localctx: StatsCommandContext = new StatsCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 46, esql_parser.RULE_statsCommand); + this.enterRule(localctx, 42, esql_parser.RULE_statsCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 331; + this.state = 311; this.match(esql_parser.STATS); - this.state = 333; + this.state = 313; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 28, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 26, this._ctx) ) { case 1: { - this.state = 332; + this.state = 312; localctx._stats = this.fields(); } break; } - this.state = 337; + this.state = 317; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 29, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 27, this._ctx) ) { case 1: { - this.state = 335; + this.state = 315; this.match(esql_parser.BY); - this.state = 336; + this.state = 316; localctx._grouping = this.fields(); } break; @@ -1772,22 +1682,22 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public inlinestatsCommand(): InlinestatsCommandContext { let localctx: InlinestatsCommandContext = new InlinestatsCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 48, esql_parser.RULE_inlinestatsCommand); + this.enterRule(localctx, 44, esql_parser.RULE_inlinestatsCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 339; + this.state = 319; this.match(esql_parser.INLINESTATS); - this.state = 340; + this.state = 320; localctx._stats = this.fields(); - this.state = 343; + this.state = 323; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 28, this._ctx) ) { case 1: { - this.state = 341; + this.state = 321; this.match(esql_parser.BY); - this.state = 342; + this.state = 322; localctx._grouping = this.fields(); } break; @@ -1811,30 +1721,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public qualifiedName(): QualifiedNameContext { let localctx: QualifiedNameContext = new QualifiedNameContext(this, this._ctx, this.state); - this.enterRule(localctx, 50, esql_parser.RULE_qualifiedName); + this.enterRule(localctx, 46, esql_parser.RULE_qualifiedName); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 345; + this.state = 325; this.identifier(); - this.state = 350; + this.state = 330; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 31, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 29, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 346; + this.state = 326; this.match(esql_parser.DOT); - this.state = 347; + this.state = 327; this.identifier(); } } } - this.state = 352; + this.state = 332; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 31, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 29, this._ctx); } } } @@ -1855,30 +1765,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public qualifiedNamePattern(): QualifiedNamePatternContext { let localctx: QualifiedNamePatternContext = new QualifiedNamePatternContext(this, this._ctx, this.state); - this.enterRule(localctx, 52, esql_parser.RULE_qualifiedNamePattern); + this.enterRule(localctx, 48, esql_parser.RULE_qualifiedNamePattern); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 353; + this.state = 333; this.identifierPattern(); - this.state = 358; + this.state = 338; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 30, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 354; + this.state = 334; this.match(esql_parser.DOT); - this.state = 355; + this.state = 335; this.identifierPattern(); } } } - this.state = 360; + this.state = 340; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 32, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 30, this._ctx); } } } @@ -1899,12 +1809,12 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public identifier(): IdentifierContext { let localctx: IdentifierContext = new IdentifierContext(this, this._ctx, this.state); - this.enterRule(localctx, 54, esql_parser.RULE_identifier); + this.enterRule(localctx, 50, esql_parser.RULE_identifier); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 361; + this.state = 341; _la = this._input.LA(1); if(!(_la===67 || _la===68)) { this._errHandler.recoverInline(this); @@ -1932,11 +1842,11 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public identifierPattern(): IdentifierPatternContext { let localctx: IdentifierPatternContext = new IdentifierPatternContext(this, this._ctx, this.state); - this.enterRule(localctx, 56, esql_parser.RULE_identifierPattern); + this.enterRule(localctx, 52, esql_parser.RULE_identifierPattern); try { this.enterOuterAlt(localctx, 1); { - this.state = 363; + this.state = 343; this.match(esql_parser.ID_PATTERN); } } @@ -1957,17 +1867,17 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public constant(): ConstantContext { let localctx: ConstantContext = new ConstantContext(this, this._ctx, this.state); - this.enterRule(localctx, 58, esql_parser.RULE_constant); + this.enterRule(localctx, 54, esql_parser.RULE_constant); let _la: number; try { - this.state = 407; + this.state = 387; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 36, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 34, this._ctx) ) { case 1: localctx = new NullLiteralContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 365; + this.state = 345; this.match(esql_parser.NULL); } break; @@ -1975,9 +1885,9 @@ export default class esql_parser extends Parser { localctx = new QualifiedIntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 366; + this.state = 346; this.integerValue(); - this.state = 367; + this.state = 347; this.match(esql_parser.UNQUOTED_IDENTIFIER); } break; @@ -1985,7 +1895,7 @@ export default class esql_parser extends Parser { localctx = new DecimalLiteralContext(this, localctx); this.enterOuterAlt(localctx, 3); { - this.state = 369; + this.state = 349; this.decimalValue(); } break; @@ -1993,7 +1903,7 @@ export default class esql_parser extends Parser { localctx = new IntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 4); { - this.state = 370; + this.state = 350; this.integerValue(); } break; @@ -2001,7 +1911,7 @@ export default class esql_parser extends Parser { localctx = new BooleanLiteralContext(this, localctx); this.enterOuterAlt(localctx, 5); { - this.state = 371; + this.state = 351; this.booleanValue(); } break; @@ -2009,7 +1919,7 @@ export default class esql_parser extends Parser { localctx = new InputParamContext(this, localctx); this.enterOuterAlt(localctx, 6); { - this.state = 372; + this.state = 352; this.match(esql_parser.PARAM); } break; @@ -2017,7 +1927,7 @@ export default class esql_parser extends Parser { localctx = new StringLiteralContext(this, localctx); this.enterOuterAlt(localctx, 7); { - this.state = 373; + this.state = 353; this.string_(); } break; @@ -2025,27 +1935,27 @@ export default class esql_parser extends Parser { localctx = new NumericArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 8); { - this.state = 374; + this.state = 354; this.match(esql_parser.OPENING_BRACKET); - this.state = 375; + this.state = 355; this.numericValue(); - this.state = 380; + this.state = 360; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===35) { { { - this.state = 376; + this.state = 356; this.match(esql_parser.COMMA); - this.state = 377; + this.state = 357; this.numericValue(); } } - this.state = 382; + this.state = 362; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 383; + this.state = 363; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2053,27 +1963,27 @@ export default class esql_parser extends Parser { localctx = new BooleanArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 9); { - this.state = 385; + this.state = 365; this.match(esql_parser.OPENING_BRACKET); - this.state = 386; + this.state = 366; this.booleanValue(); - this.state = 391; + this.state = 371; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===35) { { { - this.state = 387; + this.state = 367; this.match(esql_parser.COMMA); - this.state = 388; + this.state = 368; this.booleanValue(); } } - this.state = 393; + this.state = 373; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 394; + this.state = 374; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2081,27 +1991,27 @@ export default class esql_parser extends Parser { localctx = new StringArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 10); { - this.state = 396; + this.state = 376; this.match(esql_parser.OPENING_BRACKET); - this.state = 397; + this.state = 377; this.string_(); - this.state = 402; + this.state = 382; this._errHandler.sync(this); _la = this._input.LA(1); while (_la===35) { { { - this.state = 398; + this.state = 378; this.match(esql_parser.COMMA); - this.state = 399; + this.state = 379; this.string_(); } } - this.state = 404; + this.state = 384; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 405; + this.state = 385; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2124,13 +2034,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public limitCommand(): LimitCommandContext { let localctx: LimitCommandContext = new LimitCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 60, esql_parser.RULE_limitCommand); + this.enterRule(localctx, 56, esql_parser.RULE_limitCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 409; + this.state = 389; this.match(esql_parser.LIMIT); - this.state = 410; + this.state = 390; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2151,32 +2061,32 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public sortCommand(): SortCommandContext { let localctx: SortCommandContext = new SortCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 62, esql_parser.RULE_sortCommand); + this.enterRule(localctx, 58, esql_parser.RULE_sortCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 412; + this.state = 392; this.match(esql_parser.SORT); - this.state = 413; + this.state = 393; this.orderExpression(); - this.state = 418; + this.state = 398; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 37, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 414; + this.state = 394; this.match(esql_parser.COMMA); - this.state = 415; + this.state = 395; this.orderExpression(); } } } - this.state = 420; + this.state = 400; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 37, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 35, this._ctx); } } } @@ -2197,19 +2107,19 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public orderExpression(): OrderExpressionContext { let localctx: OrderExpressionContext = new OrderExpressionContext(this, this._ctx, this.state); - this.enterRule(localctx, 64, esql_parser.RULE_orderExpression); + this.enterRule(localctx, 60, esql_parser.RULE_orderExpression); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 421; + this.state = 401; this.booleanExpression(0); - this.state = 423; + this.state = 403; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 38, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 36, this._ctx) ) { case 1: { - this.state = 422; + this.state = 402; localctx._ordering = this._input.LT(1); _la = this._input.LA(1); if(!(_la===32 || _la===36)) { @@ -2222,14 +2132,14 @@ export default class esql_parser extends Parser { } break; } - this.state = 427; + this.state = 407; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 39, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 37, this._ctx) ) { case 1: { - this.state = 425; + this.state = 405; this.match(esql_parser.NULLS); - this.state = 426; + this.state = 406; localctx._nullOrdering = this._input.LT(1); _la = this._input.LA(1); if(!(_la===39 || _la===40)) { @@ -2261,32 +2171,32 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public keepCommand(): KeepCommandContext { let localctx: KeepCommandContext = new KeepCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 66, esql_parser.RULE_keepCommand); + this.enterRule(localctx, 62, esql_parser.RULE_keepCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 429; + this.state = 409; this.match(esql_parser.KEEP); - this.state = 430; + this.state = 410; this.qualifiedNamePattern(); - this.state = 435; + this.state = 415; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 40, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 38, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 431; + this.state = 411; this.match(esql_parser.COMMA); - this.state = 432; + this.state = 412; this.qualifiedNamePattern(); } } } - this.state = 437; + this.state = 417; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 40, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 38, this._ctx); } } } @@ -2307,32 +2217,32 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public dropCommand(): DropCommandContext { let localctx: DropCommandContext = new DropCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 68, esql_parser.RULE_dropCommand); + this.enterRule(localctx, 64, esql_parser.RULE_dropCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 438; + this.state = 418; this.match(esql_parser.DROP); - this.state = 439; + this.state = 419; this.qualifiedNamePattern(); - this.state = 444; + this.state = 424; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 41, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 39, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 440; + this.state = 420; this.match(esql_parser.COMMA); - this.state = 441; + this.state = 421; this.qualifiedNamePattern(); } } } - this.state = 446; + this.state = 426; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 41, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 39, this._ctx); } } } @@ -2353,32 +2263,32 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public renameCommand(): RenameCommandContext { let localctx: RenameCommandContext = new RenameCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 70, esql_parser.RULE_renameCommand); + this.enterRule(localctx, 66, esql_parser.RULE_renameCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 447; + this.state = 427; this.match(esql_parser.RENAME); - this.state = 448; + this.state = 428; this.renameClause(); - this.state = 453; + this.state = 433; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 42, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 40, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 449; + this.state = 429; this.match(esql_parser.COMMA); - this.state = 450; + this.state = 430; this.renameClause(); } } } - this.state = 455; + this.state = 435; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 42, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 40, this._ctx); } } } @@ -2399,15 +2309,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public renameClause(): RenameClauseContext { let localctx: RenameClauseContext = new RenameClauseContext(this, this._ctx, this.state); - this.enterRule(localctx, 72, esql_parser.RULE_renameClause); + this.enterRule(localctx, 68, esql_parser.RULE_renameClause); try { this.enterOuterAlt(localctx, 1); { - this.state = 456; + this.state = 436; localctx._oldName = this.qualifiedNamePattern(); - this.state = 457; + this.state = 437; this.match(esql_parser.AS); - this.state = 458; + this.state = 438; localctx._newName = this.qualifiedNamePattern(); } } @@ -2428,22 +2338,22 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public dissectCommand(): DissectCommandContext { let localctx: DissectCommandContext = new DissectCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 74, esql_parser.RULE_dissectCommand); + this.enterRule(localctx, 70, esql_parser.RULE_dissectCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 460; + this.state = 440; this.match(esql_parser.DISSECT); - this.state = 461; + this.state = 441; this.primaryExpression(0); - this.state = 462; + this.state = 442; this.string_(); - this.state = 464; + this.state = 444; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 43, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 41, this._ctx) ) { case 1: { - this.state = 463; + this.state = 443; this.commandOptions(); } break; @@ -2467,15 +2377,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public grokCommand(): GrokCommandContext { let localctx: GrokCommandContext = new GrokCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 76, esql_parser.RULE_grokCommand); + this.enterRule(localctx, 72, esql_parser.RULE_grokCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 466; + this.state = 446; this.match(esql_parser.GROK); - this.state = 467; + this.state = 447; this.primaryExpression(0); - this.state = 468; + this.state = 448; this.string_(); } } @@ -2496,13 +2406,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public mvExpandCommand(): MvExpandCommandContext { let localctx: MvExpandCommandContext = new MvExpandCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 78, esql_parser.RULE_mvExpandCommand); + this.enterRule(localctx, 74, esql_parser.RULE_mvExpandCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 470; + this.state = 450; this.match(esql_parser.MV_EXPAND); - this.state = 471; + this.state = 451; this.qualifiedName(); } } @@ -2523,30 +2433,30 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public commandOptions(): CommandOptionsContext { let localctx: CommandOptionsContext = new CommandOptionsContext(this, this._ctx, this.state); - this.enterRule(localctx, 80, esql_parser.RULE_commandOptions); + this.enterRule(localctx, 76, esql_parser.RULE_commandOptions); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 473; + this.state = 453; this.commandOption(); - this.state = 478; + this.state = 458; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 44, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 42, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 474; + this.state = 454; this.match(esql_parser.COMMA); - this.state = 475; + this.state = 455; this.commandOption(); } } } - this.state = 480; + this.state = 460; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 44, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 42, this._ctx); } } } @@ -2567,15 +2477,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public commandOption(): CommandOptionContext { let localctx: CommandOptionContext = new CommandOptionContext(this, this._ctx, this.state); - this.enterRule(localctx, 82, esql_parser.RULE_commandOption); + this.enterRule(localctx, 78, esql_parser.RULE_commandOption); try { this.enterOuterAlt(localctx, 1); { - this.state = 481; + this.state = 461; this.identifier(); - this.state = 482; + this.state = 462; this.match(esql_parser.ASSIGN); - this.state = 483; + this.state = 463; this.constant(); } } @@ -2596,12 +2506,12 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public booleanValue(): BooleanValueContext { let localctx: BooleanValueContext = new BooleanValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 84, esql_parser.RULE_booleanValue); + this.enterRule(localctx, 80, esql_parser.RULE_booleanValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 485; + this.state = 465; _la = this._input.LA(1); if(!(_la===38 || _la===52)) { this._errHandler.recoverInline(this); @@ -2629,22 +2539,22 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public numericValue(): NumericValueContext { let localctx: NumericValueContext = new NumericValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 86, esql_parser.RULE_numericValue); + this.enterRule(localctx, 82, esql_parser.RULE_numericValue); try { - this.state = 489; + this.state = 469; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 45, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 43, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 487; + this.state = 467; this.decimalValue(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 488; + this.state = 468; this.integerValue(); } break; @@ -2667,17 +2577,17 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public decimalValue(): DecimalValueContext { let localctx: DecimalValueContext = new DecimalValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 88, esql_parser.RULE_decimalValue); + this.enterRule(localctx, 84, esql_parser.RULE_decimalValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 492; + this.state = 472; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===60 || _la===61) { { - this.state = 491; + this.state = 471; _la = this._input.LA(1); if(!(_la===60 || _la===61)) { this._errHandler.recoverInline(this); @@ -2689,7 +2599,7 @@ export default class esql_parser extends Parser { } } - this.state = 494; + this.state = 474; this.match(esql_parser.DECIMAL_LITERAL); } } @@ -2710,17 +2620,17 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public integerValue(): IntegerValueContext { let localctx: IntegerValueContext = new IntegerValueContext(this, this._ctx, this.state); - this.enterRule(localctx, 90, esql_parser.RULE_integerValue); + this.enterRule(localctx, 86, esql_parser.RULE_integerValue); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 497; + this.state = 477; this._errHandler.sync(this); _la = this._input.LA(1); if (_la===60 || _la===61) { { - this.state = 496; + this.state = 476; _la = this._input.LA(1); if(!(_la===60 || _la===61)) { this._errHandler.recoverInline(this); @@ -2732,7 +2642,7 @@ export default class esql_parser extends Parser { } } - this.state = 499; + this.state = 479; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2753,11 +2663,11 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public string_(): StringContext { let localctx: StringContext = new StringContext(this, this._ctx, this.state); - this.enterRule(localctx, 92, esql_parser.RULE_string); + this.enterRule(localctx, 88, esql_parser.RULE_string); try { this.enterOuterAlt(localctx, 1); { - this.state = 501; + this.state = 481; this.match(esql_parser.QUOTED_STRING); } } @@ -2778,12 +2688,12 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public comparisonOperator(): ComparisonOperatorContext { let localctx: ComparisonOperatorContext = new ComparisonOperatorContext(this, this._ctx, this.state); - this.enterRule(localctx, 94, esql_parser.RULE_comparisonOperator); + this.enterRule(localctx, 90, esql_parser.RULE_comparisonOperator); let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 503; + this.state = 483; _la = this._input.LA(1); if(!(((((_la - 53)) & ~0x1F) === 0 && ((1 << (_la - 53)) & 125) !== 0))) { this._errHandler.recoverInline(this); @@ -2811,13 +2721,13 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public explainCommand(): ExplainCommandContext { let localctx: ExplainCommandContext = new ExplainCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 96, esql_parser.RULE_explainCommand); + this.enterRule(localctx, 92, esql_parser.RULE_explainCommand); try { this.enterOuterAlt(localctx, 1); { - this.state = 505; + this.state = 485; this.match(esql_parser.EXPLAIN); - this.state = 506; + this.state = 486; this.subqueryExpression(); } } @@ -2838,15 +2748,15 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public subqueryExpression(): SubqueryExpressionContext { let localctx: SubqueryExpressionContext = new SubqueryExpressionContext(this, this._ctx, this.state); - this.enterRule(localctx, 98, esql_parser.RULE_subqueryExpression); + this.enterRule(localctx, 94, esql_parser.RULE_subqueryExpression); try { this.enterOuterAlt(localctx, 1); { - this.state = 508; + this.state = 488; this.match(esql_parser.OPENING_BRACKET); - this.state = 509; + this.state = 489; this.query(0); - this.state = 510; + this.state = 490; this.match(esql_parser.CLOSING_BRACKET); } } @@ -2867,14 +2777,14 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public showCommand(): ShowCommandContext { let localctx: ShowCommandContext = new ShowCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 100, esql_parser.RULE_showCommand); + this.enterRule(localctx, 96, esql_parser.RULE_showCommand); try { localctx = new ShowInfoContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 512; + this.state = 492; this.match(esql_parser.SHOW); - this.state = 513; + this.state = 493; this.match(esql_parser.INFO); } } @@ -2895,14 +2805,14 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public metaCommand(): MetaCommandContext { let localctx: MetaCommandContext = new MetaCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 102, esql_parser.RULE_metaCommand); + this.enterRule(localctx, 98, esql_parser.RULE_metaCommand); try { localctx = new MetaFunctionsContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 515; + this.state = 495; this.match(esql_parser.META); - this.state = 516; + this.state = 496; this.match(esql_parser.FUNCTIONS); } } @@ -2923,53 +2833,53 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public enrichCommand(): EnrichCommandContext { let localctx: EnrichCommandContext = new EnrichCommandContext(this, this._ctx, this.state); - this.enterRule(localctx, 104, esql_parser.RULE_enrichCommand); + this.enterRule(localctx, 100, esql_parser.RULE_enrichCommand); try { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 518; + this.state = 498; this.match(esql_parser.ENRICH); - this.state = 519; + this.state = 499; localctx._policyName = this.match(esql_parser.ENRICH_POLICY_NAME); - this.state = 522; + this.state = 502; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 48, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 46, this._ctx) ) { case 1: { - this.state = 520; + this.state = 500; this.match(esql_parser.ON); - this.state = 521; + this.state = 501; localctx._matchField = this.qualifiedNamePattern(); } break; } - this.state = 533; + this.state = 513; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 50, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 48, this._ctx) ) { case 1: { - this.state = 524; + this.state = 504; this.match(esql_parser.WITH); - this.state = 525; + this.state = 505; this.enrichWithClause(); - this.state = 530; + this.state = 510; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 49, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 47, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 526; + this.state = 506; this.match(esql_parser.COMMA); - this.state = 527; + this.state = 507; this.enrichWithClause(); } } } - this.state = 532; + this.state = 512; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 49, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 47, this._ctx); } } break; @@ -2993,23 +2903,23 @@ export default class esql_parser extends Parser { // @RuleVersion(0) public enrichWithClause(): EnrichWithClauseContext { let localctx: EnrichWithClauseContext = new EnrichWithClauseContext(this, this._ctx, this.state); - this.enterRule(localctx, 106, esql_parser.RULE_enrichWithClause); + this.enterRule(localctx, 102, esql_parser.RULE_enrichWithClause); try { this.enterOuterAlt(localctx, 1); { - this.state = 538; + this.state = 518; this._errHandler.sync(this); - switch ( this._interp.adaptivePredict(this._input, 51, this._ctx) ) { + switch ( this._interp.adaptivePredict(this._input, 49, this._ctx) ) { case 1: { - this.state = 535; + this.state = 515; localctx._newName = this.qualifiedNamePattern(); - this.state = 536; + this.state = 516; this.match(esql_parser.ASSIGN); } break; } - this.state = 540; + this.state = 520; localctx._enrichField = this.qualifiedNamePattern(); } } @@ -3074,183 +2984,176 @@ export default class esql_parser extends Parser { return true; } - public static readonly _serializedATN: number[] = [4,1,110,543,2,0,7,0, + public static readonly _serializedATN: number[] = [4,1,109,523,2,0,7,0, 2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9, 2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2, 17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24, 7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7, 31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38, 2,39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,45,2, - 46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2,52,7,52,2,53, - 7,53,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,5,1,118,8,1,10,1,12,1,121,9,1, - 1,2,1,2,1,2,1,2,1,2,3,2,128,8,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3, - 1,3,1,3,1,3,3,3,143,8,3,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,155, - 8,5,1,5,1,5,1,5,1,5,1,5,5,5,162,8,5,10,5,12,5,165,9,5,1,5,1,5,1,5,1,5,1, - 5,3,5,172,8,5,1,5,1,5,3,5,176,8,5,1,5,1,5,1,5,1,5,1,5,1,5,5,5,184,8,5,10, - 5,12,5,187,9,5,1,6,1,6,3,6,191,8,6,1,6,1,6,1,6,1,6,1,6,3,6,198,8,6,1,6, - 1,6,1,6,3,6,203,8,6,1,7,1,7,1,7,1,7,1,7,3,7,210,8,7,1,8,1,8,1,8,1,8,3,8, - 216,8,8,1,8,1,8,1,8,1,8,1,8,1,8,5,8,224,8,8,10,8,12,8,227,9,8,1,9,1,9,1, - 9,1,9,1,9,1,9,1,9,1,9,3,9,237,8,9,1,9,1,9,1,9,5,9,242,8,9,10,9,12,9,245, - 9,9,1,10,1,10,1,10,1,10,1,10,1,10,5,10,253,8,10,10,10,12,10,256,9,10,3, - 10,258,8,10,1,10,1,10,1,11,1,11,1,12,1,12,1,12,1,13,1,13,1,13,5,13,270, - 8,13,10,13,12,13,273,9,13,1,14,1,14,1,14,1,14,1,14,3,14,280,8,14,1,15,1, - 15,1,15,1,15,5,15,286,8,15,10,15,12,15,289,9,15,1,15,3,15,292,8,15,1,15, - 3,15,295,8,15,1,16,1,16,1,17,1,17,1,17,1,17,5,17,303,8,17,10,17,12,17,306, - 9,17,1,18,1,18,1,18,1,18,1,19,1,19,3,19,314,8,19,1,20,1,20,1,20,1,20,5, - 20,320,8,20,10,20,12,20,323,9,20,1,21,1,21,1,21,1,21,1,22,1,22,1,22,1,23, - 1,23,3,23,334,8,23,1,23,1,23,3,23,338,8,23,1,24,1,24,1,24,1,24,3,24,344, - 8,24,1,25,1,25,1,25,5,25,349,8,25,10,25,12,25,352,9,25,1,26,1,26,1,26,5, - 26,357,8,26,10,26,12,26,360,9,26,1,27,1,27,1,28,1,28,1,29,1,29,1,29,1,29, - 1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,1,29,5,29,379,8,29,10,29,12,29, - 382,9,29,1,29,1,29,1,29,1,29,1,29,1,29,5,29,390,8,29,10,29,12,29,393,9, - 29,1,29,1,29,1,29,1,29,1,29,1,29,5,29,401,8,29,10,29,12,29,404,9,29,1,29, - 1,29,3,29,408,8,29,1,30,1,30,1,30,1,31,1,31,1,31,1,31,5,31,417,8,31,10, - 31,12,31,420,9,31,1,32,1,32,3,32,424,8,32,1,32,1,32,3,32,428,8,32,1,33, - 1,33,1,33,1,33,5,33,434,8,33,10,33,12,33,437,9,33,1,34,1,34,1,34,1,34,5, - 34,443,8,34,10,34,12,34,446,9,34,1,35,1,35,1,35,1,35,5,35,452,8,35,10,35, - 12,35,455,9,35,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,3,37,465,8,37,1, - 38,1,38,1,38,1,38,1,39,1,39,1,39,1,40,1,40,1,40,5,40,477,8,40,10,40,12, - 40,480,9,40,1,41,1,41,1,41,1,41,1,42,1,42,1,43,1,43,3,43,490,8,43,1,44, - 3,44,493,8,44,1,44,1,44,1,45,3,45,498,8,45,1,45,1,45,1,46,1,46,1,47,1,47, - 1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,50,1,50,1,50,1,51,1,51,1,51,1,52,1, - 52,1,52,1,52,3,52,523,8,52,1,52,1,52,1,52,1,52,5,52,529,8,52,10,52,12,52, - 532,9,52,3,52,534,8,52,1,53,1,53,1,53,3,53,539,8,53,1,53,1,53,1,53,0,4, - 2,10,16,18,54,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40, - 42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88, - 90,92,94,96,98,100,102,104,106,0,7,1,0,60,61,1,0,62,64,1,0,67,68,2,0,32, - 32,36,36,1,0,39,40,2,0,38,38,52,52,2,0,53,53,55,59,568,0,108,1,0,0,0,2, - 111,1,0,0,0,4,127,1,0,0,0,6,142,1,0,0,0,8,144,1,0,0,0,10,175,1,0,0,0,12, - 202,1,0,0,0,14,209,1,0,0,0,16,215,1,0,0,0,18,236,1,0,0,0,20,246,1,0,0,0, - 22,261,1,0,0,0,24,263,1,0,0,0,26,266,1,0,0,0,28,279,1,0,0,0,30,281,1,0, - 0,0,32,296,1,0,0,0,34,298,1,0,0,0,36,307,1,0,0,0,38,313,1,0,0,0,40,315, - 1,0,0,0,42,324,1,0,0,0,44,328,1,0,0,0,46,331,1,0,0,0,48,339,1,0,0,0,50, - 345,1,0,0,0,52,353,1,0,0,0,54,361,1,0,0,0,56,363,1,0,0,0,58,407,1,0,0,0, - 60,409,1,0,0,0,62,412,1,0,0,0,64,421,1,0,0,0,66,429,1,0,0,0,68,438,1,0, - 0,0,70,447,1,0,0,0,72,456,1,0,0,0,74,460,1,0,0,0,76,466,1,0,0,0,78,470, - 1,0,0,0,80,473,1,0,0,0,82,481,1,0,0,0,84,485,1,0,0,0,86,489,1,0,0,0,88, - 492,1,0,0,0,90,497,1,0,0,0,92,501,1,0,0,0,94,503,1,0,0,0,96,505,1,0,0,0, - 98,508,1,0,0,0,100,512,1,0,0,0,102,515,1,0,0,0,104,518,1,0,0,0,106,538, - 1,0,0,0,108,109,3,2,1,0,109,110,5,0,0,1,110,1,1,0,0,0,111,112,6,1,-1,0, - 112,113,3,4,2,0,113,119,1,0,0,0,114,115,10,1,0,0,115,116,5,26,0,0,116,118, - 3,6,3,0,117,114,1,0,0,0,118,121,1,0,0,0,119,117,1,0,0,0,119,120,1,0,0,0, - 120,3,1,0,0,0,121,119,1,0,0,0,122,128,3,96,48,0,123,128,3,30,15,0,124,128, - 3,24,12,0,125,128,3,100,50,0,126,128,3,102,51,0,127,122,1,0,0,0,127,123, - 1,0,0,0,127,124,1,0,0,0,127,125,1,0,0,0,127,126,1,0,0,0,128,5,1,0,0,0,129, - 143,3,44,22,0,130,143,3,48,24,0,131,143,3,60,30,0,132,143,3,66,33,0,133, - 143,3,62,31,0,134,143,3,46,23,0,135,143,3,8,4,0,136,143,3,68,34,0,137,143, - 3,70,35,0,138,143,3,74,37,0,139,143,3,76,38,0,140,143,3,104,52,0,141,143, - 3,78,39,0,142,129,1,0,0,0,142,130,1,0,0,0,142,131,1,0,0,0,142,132,1,0,0, - 0,142,133,1,0,0,0,142,134,1,0,0,0,142,135,1,0,0,0,142,136,1,0,0,0,142,137, - 1,0,0,0,142,138,1,0,0,0,142,139,1,0,0,0,142,140,1,0,0,0,142,141,1,0,0,0, - 143,7,1,0,0,0,144,145,5,18,0,0,145,146,3,10,5,0,146,9,1,0,0,0,147,148,6, - 5,-1,0,148,149,5,45,0,0,149,176,3,10,5,7,150,176,3,14,7,0,151,176,3,12, - 6,0,152,154,3,14,7,0,153,155,5,45,0,0,154,153,1,0,0,0,154,155,1,0,0,0,155, - 156,1,0,0,0,156,157,5,42,0,0,157,158,5,41,0,0,158,163,3,14,7,0,159,160, - 5,35,0,0,160,162,3,14,7,0,161,159,1,0,0,0,162,165,1,0,0,0,163,161,1,0,0, - 0,163,164,1,0,0,0,164,166,1,0,0,0,165,163,1,0,0,0,166,167,5,51,0,0,167, - 176,1,0,0,0,168,169,3,14,7,0,169,171,5,43,0,0,170,172,5,45,0,0,171,170, - 1,0,0,0,171,172,1,0,0,0,172,173,1,0,0,0,173,174,5,46,0,0,174,176,1,0,0, - 0,175,147,1,0,0,0,175,150,1,0,0,0,175,151,1,0,0,0,175,152,1,0,0,0,175,168, - 1,0,0,0,176,185,1,0,0,0,177,178,10,4,0,0,178,179,5,31,0,0,179,184,3,10, - 5,5,180,181,10,3,0,0,181,182,5,48,0,0,182,184,3,10,5,4,183,177,1,0,0,0, - 183,180,1,0,0,0,184,187,1,0,0,0,185,183,1,0,0,0,185,186,1,0,0,0,186,11, - 1,0,0,0,187,185,1,0,0,0,188,190,3,14,7,0,189,191,5,45,0,0,190,189,1,0,0, - 0,190,191,1,0,0,0,191,192,1,0,0,0,192,193,5,44,0,0,193,194,3,92,46,0,194, - 203,1,0,0,0,195,197,3,14,7,0,196,198,5,45,0,0,197,196,1,0,0,0,197,198,1, - 0,0,0,198,199,1,0,0,0,199,200,5,50,0,0,200,201,3,92,46,0,201,203,1,0,0, - 0,202,188,1,0,0,0,202,195,1,0,0,0,203,13,1,0,0,0,204,210,3,16,8,0,205,206, - 3,16,8,0,206,207,3,94,47,0,207,208,3,16,8,0,208,210,1,0,0,0,209,204,1,0, - 0,0,209,205,1,0,0,0,210,15,1,0,0,0,211,212,6,8,-1,0,212,216,3,18,9,0,213, - 214,7,0,0,0,214,216,3,16,8,3,215,211,1,0,0,0,215,213,1,0,0,0,216,225,1, - 0,0,0,217,218,10,2,0,0,218,219,7,1,0,0,219,224,3,16,8,3,220,221,10,1,0, - 0,221,222,7,0,0,0,222,224,3,16,8,2,223,217,1,0,0,0,223,220,1,0,0,0,224, - 227,1,0,0,0,225,223,1,0,0,0,225,226,1,0,0,0,226,17,1,0,0,0,227,225,1,0, - 0,0,228,229,6,9,-1,0,229,237,3,58,29,0,230,237,3,50,25,0,231,237,3,20,10, - 0,232,233,5,41,0,0,233,234,3,10,5,0,234,235,5,51,0,0,235,237,1,0,0,0,236, - 228,1,0,0,0,236,230,1,0,0,0,236,231,1,0,0,0,236,232,1,0,0,0,237,243,1,0, - 0,0,238,239,10,1,0,0,239,240,5,34,0,0,240,242,3,22,11,0,241,238,1,0,0,0, - 242,245,1,0,0,0,243,241,1,0,0,0,243,244,1,0,0,0,244,19,1,0,0,0,245,243, - 1,0,0,0,246,247,3,54,27,0,247,257,5,41,0,0,248,258,5,62,0,0,249,254,3,10, - 5,0,250,251,5,35,0,0,251,253,3,10,5,0,252,250,1,0,0,0,253,256,1,0,0,0,254, - 252,1,0,0,0,254,255,1,0,0,0,255,258,1,0,0,0,256,254,1,0,0,0,257,248,1,0, - 0,0,257,249,1,0,0,0,257,258,1,0,0,0,258,259,1,0,0,0,259,260,5,51,0,0,260, - 21,1,0,0,0,261,262,3,54,27,0,262,23,1,0,0,0,263,264,5,14,0,0,264,265,3, - 26,13,0,265,25,1,0,0,0,266,271,3,28,14,0,267,268,5,35,0,0,268,270,3,28, - 14,0,269,267,1,0,0,0,270,273,1,0,0,0,271,269,1,0,0,0,271,272,1,0,0,0,272, - 27,1,0,0,0,273,271,1,0,0,0,274,280,3,10,5,0,275,276,3,50,25,0,276,277,5, - 33,0,0,277,278,3,10,5,0,278,280,1,0,0,0,279,274,1,0,0,0,279,275,1,0,0,0, - 280,29,1,0,0,0,281,282,5,6,0,0,282,287,3,32,16,0,283,284,5,35,0,0,284,286, - 3,32,16,0,285,283,1,0,0,0,286,289,1,0,0,0,287,285,1,0,0,0,287,288,1,0,0, - 0,288,291,1,0,0,0,289,287,1,0,0,0,290,292,3,38,19,0,291,290,1,0,0,0,291, - 292,1,0,0,0,292,294,1,0,0,0,293,295,3,34,17,0,294,293,1,0,0,0,294,295,1, - 0,0,0,295,31,1,0,0,0,296,297,5,74,0,0,297,33,1,0,0,0,298,299,5,72,0,0,299, - 304,3,36,18,0,300,301,5,35,0,0,301,303,3,36,18,0,302,300,1,0,0,0,303,306, - 1,0,0,0,304,302,1,0,0,0,304,305,1,0,0,0,305,35,1,0,0,0,306,304,1,0,0,0, - 307,308,3,92,46,0,308,309,5,33,0,0,309,310,3,92,46,0,310,37,1,0,0,0,311, - 314,3,40,20,0,312,314,3,42,21,0,313,311,1,0,0,0,313,312,1,0,0,0,314,39, - 1,0,0,0,315,316,5,73,0,0,316,321,3,32,16,0,317,318,5,35,0,0,318,320,3,32, - 16,0,319,317,1,0,0,0,320,323,1,0,0,0,321,319,1,0,0,0,321,322,1,0,0,0,322, - 41,1,0,0,0,323,321,1,0,0,0,324,325,5,65,0,0,325,326,3,40,20,0,326,327,5, - 66,0,0,327,43,1,0,0,0,328,329,5,4,0,0,329,330,3,26,13,0,330,45,1,0,0,0, - 331,333,5,17,0,0,332,334,3,26,13,0,333,332,1,0,0,0,333,334,1,0,0,0,334, - 337,1,0,0,0,335,336,5,30,0,0,336,338,3,26,13,0,337,335,1,0,0,0,337,338, - 1,0,0,0,338,47,1,0,0,0,339,340,5,8,0,0,340,343,3,26,13,0,341,342,5,30,0, - 0,342,344,3,26,13,0,343,341,1,0,0,0,343,344,1,0,0,0,344,49,1,0,0,0,345, - 350,3,54,27,0,346,347,5,37,0,0,347,349,3,54,27,0,348,346,1,0,0,0,349,352, - 1,0,0,0,350,348,1,0,0,0,350,351,1,0,0,0,351,51,1,0,0,0,352,350,1,0,0,0, - 353,358,3,56,28,0,354,355,5,37,0,0,355,357,3,56,28,0,356,354,1,0,0,0,357, - 360,1,0,0,0,358,356,1,0,0,0,358,359,1,0,0,0,359,53,1,0,0,0,360,358,1,0, - 0,0,361,362,7,2,0,0,362,55,1,0,0,0,363,364,5,78,0,0,364,57,1,0,0,0,365, - 408,5,46,0,0,366,367,3,90,45,0,367,368,5,67,0,0,368,408,1,0,0,0,369,408, - 3,88,44,0,370,408,3,90,45,0,371,408,3,84,42,0,372,408,5,49,0,0,373,408, - 3,92,46,0,374,375,5,65,0,0,375,380,3,86,43,0,376,377,5,35,0,0,377,379,3, - 86,43,0,378,376,1,0,0,0,379,382,1,0,0,0,380,378,1,0,0,0,380,381,1,0,0,0, - 381,383,1,0,0,0,382,380,1,0,0,0,383,384,5,66,0,0,384,408,1,0,0,0,385,386, - 5,65,0,0,386,391,3,84,42,0,387,388,5,35,0,0,388,390,3,84,42,0,389,387,1, - 0,0,0,390,393,1,0,0,0,391,389,1,0,0,0,391,392,1,0,0,0,392,394,1,0,0,0,393, - 391,1,0,0,0,394,395,5,66,0,0,395,408,1,0,0,0,396,397,5,65,0,0,397,402,3, - 92,46,0,398,399,5,35,0,0,399,401,3,92,46,0,400,398,1,0,0,0,401,404,1,0, - 0,0,402,400,1,0,0,0,402,403,1,0,0,0,403,405,1,0,0,0,404,402,1,0,0,0,405, - 406,5,66,0,0,406,408,1,0,0,0,407,365,1,0,0,0,407,366,1,0,0,0,407,369,1, - 0,0,0,407,370,1,0,0,0,407,371,1,0,0,0,407,372,1,0,0,0,407,373,1,0,0,0,407, - 374,1,0,0,0,407,385,1,0,0,0,407,396,1,0,0,0,408,59,1,0,0,0,409,410,5,10, - 0,0,410,411,5,28,0,0,411,61,1,0,0,0,412,413,5,16,0,0,413,418,3,64,32,0, - 414,415,5,35,0,0,415,417,3,64,32,0,416,414,1,0,0,0,417,420,1,0,0,0,418, - 416,1,0,0,0,418,419,1,0,0,0,419,63,1,0,0,0,420,418,1,0,0,0,421,423,3,10, - 5,0,422,424,7,3,0,0,423,422,1,0,0,0,423,424,1,0,0,0,424,427,1,0,0,0,425, - 426,5,47,0,0,426,428,7,4,0,0,427,425,1,0,0,0,427,428,1,0,0,0,428,65,1,0, - 0,0,429,430,5,9,0,0,430,435,3,52,26,0,431,432,5,35,0,0,432,434,3,52,26, - 0,433,431,1,0,0,0,434,437,1,0,0,0,435,433,1,0,0,0,435,436,1,0,0,0,436,67, - 1,0,0,0,437,435,1,0,0,0,438,439,5,2,0,0,439,444,3,52,26,0,440,441,5,35, - 0,0,441,443,3,52,26,0,442,440,1,0,0,0,443,446,1,0,0,0,444,442,1,0,0,0,444, - 445,1,0,0,0,445,69,1,0,0,0,446,444,1,0,0,0,447,448,5,13,0,0,448,453,3,72, - 36,0,449,450,5,35,0,0,450,452,3,72,36,0,451,449,1,0,0,0,452,455,1,0,0,0, - 453,451,1,0,0,0,453,454,1,0,0,0,454,71,1,0,0,0,455,453,1,0,0,0,456,457, - 3,52,26,0,457,458,5,82,0,0,458,459,3,52,26,0,459,73,1,0,0,0,460,461,5,1, - 0,0,461,462,3,18,9,0,462,464,3,92,46,0,463,465,3,80,40,0,464,463,1,0,0, - 0,464,465,1,0,0,0,465,75,1,0,0,0,466,467,5,7,0,0,467,468,3,18,9,0,468,469, - 3,92,46,0,469,77,1,0,0,0,470,471,5,12,0,0,471,472,3,50,25,0,472,79,1,0, - 0,0,473,478,3,82,41,0,474,475,5,35,0,0,475,477,3,82,41,0,476,474,1,0,0, - 0,477,480,1,0,0,0,478,476,1,0,0,0,478,479,1,0,0,0,479,81,1,0,0,0,480,478, - 1,0,0,0,481,482,3,54,27,0,482,483,5,33,0,0,483,484,3,58,29,0,484,83,1,0, - 0,0,485,486,7,5,0,0,486,85,1,0,0,0,487,490,3,88,44,0,488,490,3,90,45,0, - 489,487,1,0,0,0,489,488,1,0,0,0,490,87,1,0,0,0,491,493,7,0,0,0,492,491, - 1,0,0,0,492,493,1,0,0,0,493,494,1,0,0,0,494,495,5,29,0,0,495,89,1,0,0,0, - 496,498,7,0,0,0,497,496,1,0,0,0,497,498,1,0,0,0,498,499,1,0,0,0,499,500, - 5,28,0,0,500,91,1,0,0,0,501,502,5,27,0,0,502,93,1,0,0,0,503,504,7,6,0,0, - 504,95,1,0,0,0,505,506,5,5,0,0,506,507,3,98,49,0,507,97,1,0,0,0,508,509, - 5,65,0,0,509,510,3,2,1,0,510,511,5,66,0,0,511,99,1,0,0,0,512,513,5,15,0, - 0,513,514,5,98,0,0,514,101,1,0,0,0,515,516,5,11,0,0,516,517,5,102,0,0,517, - 103,1,0,0,0,518,519,5,3,0,0,519,522,5,88,0,0,520,521,5,86,0,0,521,523,3, - 52,26,0,522,520,1,0,0,0,522,523,1,0,0,0,523,533,1,0,0,0,524,525,5,87,0, - 0,525,530,3,106,53,0,526,527,5,35,0,0,527,529,3,106,53,0,528,526,1,0,0, - 0,529,532,1,0,0,0,530,528,1,0,0,0,530,531,1,0,0,0,531,534,1,0,0,0,532,530, - 1,0,0,0,533,524,1,0,0,0,533,534,1,0,0,0,534,105,1,0,0,0,535,536,3,52,26, - 0,536,537,5,33,0,0,537,539,1,0,0,0,538,535,1,0,0,0,538,539,1,0,0,0,539, - 540,1,0,0,0,540,541,3,52,26,0,541,107,1,0,0,0,52,119,127,142,154,163,171, - 175,183,185,190,197,202,209,215,223,225,236,243,254,257,271,279,287,291, - 294,304,313,321,333,337,343,350,358,380,391,402,407,418,423,427,435,444, - 453,464,478,489,492,497,522,530,533,538]; + 46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,1,0,1,0,1,0,1, + 1,1,1,1,1,1,1,1,1,1,1,5,1,114,8,1,10,1,12,1,117,9,1,1,2,1,2,1,2,1,2,1,2, + 3,2,124,8,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,3,3,139, + 8,3,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,151,8,5,1,5,1,5,1,5,1,5, + 1,5,5,5,158,8,5,10,5,12,5,161,9,5,1,5,1,5,1,5,1,5,1,5,3,5,168,8,5,1,5,1, + 5,3,5,172,8,5,1,5,1,5,1,5,1,5,1,5,1,5,5,5,180,8,5,10,5,12,5,183,9,5,1,6, + 1,6,3,6,187,8,6,1,6,1,6,1,6,1,6,1,6,3,6,194,8,6,1,6,1,6,1,6,3,6,199,8,6, + 1,7,1,7,1,7,1,7,1,7,3,7,206,8,7,1,8,1,8,1,8,1,8,3,8,212,8,8,1,8,1,8,1,8, + 1,8,1,8,1,8,5,8,220,8,8,10,8,12,8,223,9,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1, + 9,3,9,233,8,9,1,9,1,9,1,9,5,9,238,8,9,10,9,12,9,241,9,9,1,10,1,10,1,10, + 1,10,1,10,1,10,5,10,249,8,10,10,10,12,10,252,9,10,3,10,254,8,10,1,10,1, + 10,1,11,1,11,1,12,1,12,1,12,1,13,1,13,1,13,5,13,266,8,13,10,13,12,13,269, + 9,13,1,14,1,14,1,14,1,14,1,14,3,14,276,8,14,1,15,1,15,1,15,1,15,5,15,282, + 8,15,10,15,12,15,285,9,15,1,15,3,15,288,8,15,1,16,1,16,1,17,1,17,3,17,294, + 8,17,1,18,1,18,1,18,1,18,5,18,300,8,18,10,18,12,18,303,9,18,1,19,1,19,1, + 19,1,19,1,20,1,20,1,20,1,21,1,21,3,21,314,8,21,1,21,1,21,3,21,318,8,21, + 1,22,1,22,1,22,1,22,3,22,324,8,22,1,23,1,23,1,23,5,23,329,8,23,10,23,12, + 23,332,9,23,1,24,1,24,1,24,5,24,337,8,24,10,24,12,24,340,9,24,1,25,1,25, + 1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1,27,1, + 27,5,27,359,8,27,10,27,12,27,362,9,27,1,27,1,27,1,27,1,27,1,27,1,27,5,27, + 370,8,27,10,27,12,27,373,9,27,1,27,1,27,1,27,1,27,1,27,1,27,5,27,381,8, + 27,10,27,12,27,384,9,27,1,27,1,27,3,27,388,8,27,1,28,1,28,1,28,1,29,1,29, + 1,29,1,29,5,29,397,8,29,10,29,12,29,400,9,29,1,30,1,30,3,30,404,8,30,1, + 30,1,30,3,30,408,8,30,1,31,1,31,1,31,1,31,5,31,414,8,31,10,31,12,31,417, + 9,31,1,32,1,32,1,32,1,32,5,32,423,8,32,10,32,12,32,426,9,32,1,33,1,33,1, + 33,1,33,5,33,432,8,33,10,33,12,33,435,9,33,1,34,1,34,1,34,1,34,1,35,1,35, + 1,35,1,35,3,35,445,8,35,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,38,1,38,1, + 38,5,38,457,8,38,10,38,12,38,460,9,38,1,39,1,39,1,39,1,39,1,40,1,40,1,41, + 1,41,3,41,470,8,41,1,42,3,42,473,8,42,1,42,1,42,1,43,3,43,478,8,43,1,43, + 1,43,1,44,1,44,1,45,1,45,1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,48,1,48,1, + 48,1,49,1,49,1,49,1,50,1,50,1,50,1,50,3,50,503,8,50,1,50,1,50,1,50,1,50, + 5,50,509,8,50,10,50,12,50,512,9,50,3,50,514,8,50,1,51,1,51,1,51,3,51,519, + 8,51,1,51,1,51,1,51,0,4,2,10,16,18,52,0,2,4,6,8,10,12,14,16,18,20,22,24, + 26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72, + 74,76,78,80,82,84,86,88,90,92,94,96,98,100,102,0,7,1,0,60,61,1,0,62,64, + 1,0,67,68,2,0,32,32,36,36,1,0,39,40,2,0,38,38,52,52,2,0,53,53,55,59,548, + 0,104,1,0,0,0,2,107,1,0,0,0,4,123,1,0,0,0,6,138,1,0,0,0,8,140,1,0,0,0,10, + 171,1,0,0,0,12,198,1,0,0,0,14,205,1,0,0,0,16,211,1,0,0,0,18,232,1,0,0,0, + 20,242,1,0,0,0,22,257,1,0,0,0,24,259,1,0,0,0,26,262,1,0,0,0,28,275,1,0, + 0,0,30,277,1,0,0,0,32,289,1,0,0,0,34,293,1,0,0,0,36,295,1,0,0,0,38,304, + 1,0,0,0,40,308,1,0,0,0,42,311,1,0,0,0,44,319,1,0,0,0,46,325,1,0,0,0,48, + 333,1,0,0,0,50,341,1,0,0,0,52,343,1,0,0,0,54,387,1,0,0,0,56,389,1,0,0,0, + 58,392,1,0,0,0,60,401,1,0,0,0,62,409,1,0,0,0,64,418,1,0,0,0,66,427,1,0, + 0,0,68,436,1,0,0,0,70,440,1,0,0,0,72,446,1,0,0,0,74,450,1,0,0,0,76,453, + 1,0,0,0,78,461,1,0,0,0,80,465,1,0,0,0,82,469,1,0,0,0,84,472,1,0,0,0,86, + 477,1,0,0,0,88,481,1,0,0,0,90,483,1,0,0,0,92,485,1,0,0,0,94,488,1,0,0,0, + 96,492,1,0,0,0,98,495,1,0,0,0,100,498,1,0,0,0,102,518,1,0,0,0,104,105,3, + 2,1,0,105,106,5,0,0,1,106,1,1,0,0,0,107,108,6,1,-1,0,108,109,3,4,2,0,109, + 115,1,0,0,0,110,111,10,1,0,0,111,112,5,26,0,0,112,114,3,6,3,0,113,110,1, + 0,0,0,114,117,1,0,0,0,115,113,1,0,0,0,115,116,1,0,0,0,116,3,1,0,0,0,117, + 115,1,0,0,0,118,124,3,92,46,0,119,124,3,30,15,0,120,124,3,24,12,0,121,124, + 3,96,48,0,122,124,3,98,49,0,123,118,1,0,0,0,123,119,1,0,0,0,123,120,1,0, + 0,0,123,121,1,0,0,0,123,122,1,0,0,0,124,5,1,0,0,0,125,139,3,40,20,0,126, + 139,3,44,22,0,127,139,3,56,28,0,128,139,3,62,31,0,129,139,3,58,29,0,130, + 139,3,42,21,0,131,139,3,8,4,0,132,139,3,64,32,0,133,139,3,66,33,0,134,139, + 3,70,35,0,135,139,3,72,36,0,136,139,3,100,50,0,137,139,3,74,37,0,138,125, + 1,0,0,0,138,126,1,0,0,0,138,127,1,0,0,0,138,128,1,0,0,0,138,129,1,0,0,0, + 138,130,1,0,0,0,138,131,1,0,0,0,138,132,1,0,0,0,138,133,1,0,0,0,138,134, + 1,0,0,0,138,135,1,0,0,0,138,136,1,0,0,0,138,137,1,0,0,0,139,7,1,0,0,0,140, + 141,5,18,0,0,141,142,3,10,5,0,142,9,1,0,0,0,143,144,6,5,-1,0,144,145,5, + 45,0,0,145,172,3,10,5,7,146,172,3,14,7,0,147,172,3,12,6,0,148,150,3,14, + 7,0,149,151,5,45,0,0,150,149,1,0,0,0,150,151,1,0,0,0,151,152,1,0,0,0,152, + 153,5,42,0,0,153,154,5,41,0,0,154,159,3,14,7,0,155,156,5,35,0,0,156,158, + 3,14,7,0,157,155,1,0,0,0,158,161,1,0,0,0,159,157,1,0,0,0,159,160,1,0,0, + 0,160,162,1,0,0,0,161,159,1,0,0,0,162,163,5,51,0,0,163,172,1,0,0,0,164, + 165,3,14,7,0,165,167,5,43,0,0,166,168,5,45,0,0,167,166,1,0,0,0,167,168, + 1,0,0,0,168,169,1,0,0,0,169,170,5,46,0,0,170,172,1,0,0,0,171,143,1,0,0, + 0,171,146,1,0,0,0,171,147,1,0,0,0,171,148,1,0,0,0,171,164,1,0,0,0,172,181, + 1,0,0,0,173,174,10,4,0,0,174,175,5,31,0,0,175,180,3,10,5,5,176,177,10,3, + 0,0,177,178,5,48,0,0,178,180,3,10,5,4,179,173,1,0,0,0,179,176,1,0,0,0,180, + 183,1,0,0,0,181,179,1,0,0,0,181,182,1,0,0,0,182,11,1,0,0,0,183,181,1,0, + 0,0,184,186,3,14,7,0,185,187,5,45,0,0,186,185,1,0,0,0,186,187,1,0,0,0,187, + 188,1,0,0,0,188,189,5,44,0,0,189,190,3,88,44,0,190,199,1,0,0,0,191,193, + 3,14,7,0,192,194,5,45,0,0,193,192,1,0,0,0,193,194,1,0,0,0,194,195,1,0,0, + 0,195,196,5,50,0,0,196,197,3,88,44,0,197,199,1,0,0,0,198,184,1,0,0,0,198, + 191,1,0,0,0,199,13,1,0,0,0,200,206,3,16,8,0,201,202,3,16,8,0,202,203,3, + 90,45,0,203,204,3,16,8,0,204,206,1,0,0,0,205,200,1,0,0,0,205,201,1,0,0, + 0,206,15,1,0,0,0,207,208,6,8,-1,0,208,212,3,18,9,0,209,210,7,0,0,0,210, + 212,3,16,8,3,211,207,1,0,0,0,211,209,1,0,0,0,212,221,1,0,0,0,213,214,10, + 2,0,0,214,215,7,1,0,0,215,220,3,16,8,3,216,217,10,1,0,0,217,218,7,0,0,0, + 218,220,3,16,8,2,219,213,1,0,0,0,219,216,1,0,0,0,220,223,1,0,0,0,221,219, + 1,0,0,0,221,222,1,0,0,0,222,17,1,0,0,0,223,221,1,0,0,0,224,225,6,9,-1,0, + 225,233,3,54,27,0,226,233,3,46,23,0,227,233,3,20,10,0,228,229,5,41,0,0, + 229,230,3,10,5,0,230,231,5,51,0,0,231,233,1,0,0,0,232,224,1,0,0,0,232,226, + 1,0,0,0,232,227,1,0,0,0,232,228,1,0,0,0,233,239,1,0,0,0,234,235,10,1,0, + 0,235,236,5,34,0,0,236,238,3,22,11,0,237,234,1,0,0,0,238,241,1,0,0,0,239, + 237,1,0,0,0,239,240,1,0,0,0,240,19,1,0,0,0,241,239,1,0,0,0,242,243,3,50, + 25,0,243,253,5,41,0,0,244,254,5,62,0,0,245,250,3,10,5,0,246,247,5,35,0, + 0,247,249,3,10,5,0,248,246,1,0,0,0,249,252,1,0,0,0,250,248,1,0,0,0,250, + 251,1,0,0,0,251,254,1,0,0,0,252,250,1,0,0,0,253,244,1,0,0,0,253,245,1,0, + 0,0,253,254,1,0,0,0,254,255,1,0,0,0,255,256,5,51,0,0,256,21,1,0,0,0,257, + 258,3,50,25,0,258,23,1,0,0,0,259,260,5,14,0,0,260,261,3,26,13,0,261,25, + 1,0,0,0,262,267,3,28,14,0,263,264,5,35,0,0,264,266,3,28,14,0,265,263,1, + 0,0,0,266,269,1,0,0,0,267,265,1,0,0,0,267,268,1,0,0,0,268,27,1,0,0,0,269, + 267,1,0,0,0,270,276,3,10,5,0,271,272,3,46,23,0,272,273,5,33,0,0,273,274, + 3,10,5,0,274,276,1,0,0,0,275,270,1,0,0,0,275,271,1,0,0,0,276,29,1,0,0,0, + 277,278,5,6,0,0,278,283,3,32,16,0,279,280,5,35,0,0,280,282,3,32,16,0,281, + 279,1,0,0,0,282,285,1,0,0,0,283,281,1,0,0,0,283,284,1,0,0,0,284,287,1,0, + 0,0,285,283,1,0,0,0,286,288,3,34,17,0,287,286,1,0,0,0,287,288,1,0,0,0,288, + 31,1,0,0,0,289,290,5,73,0,0,290,33,1,0,0,0,291,294,3,36,18,0,292,294,3, + 38,19,0,293,291,1,0,0,0,293,292,1,0,0,0,294,35,1,0,0,0,295,296,5,72,0,0, + 296,301,3,32,16,0,297,298,5,35,0,0,298,300,3,32,16,0,299,297,1,0,0,0,300, + 303,1,0,0,0,301,299,1,0,0,0,301,302,1,0,0,0,302,37,1,0,0,0,303,301,1,0, + 0,0,304,305,5,65,0,0,305,306,3,36,18,0,306,307,5,66,0,0,307,39,1,0,0,0, + 308,309,5,4,0,0,309,310,3,26,13,0,310,41,1,0,0,0,311,313,5,17,0,0,312,314, + 3,26,13,0,313,312,1,0,0,0,313,314,1,0,0,0,314,317,1,0,0,0,315,316,5,30, + 0,0,316,318,3,26,13,0,317,315,1,0,0,0,317,318,1,0,0,0,318,43,1,0,0,0,319, + 320,5,8,0,0,320,323,3,26,13,0,321,322,5,30,0,0,322,324,3,26,13,0,323,321, + 1,0,0,0,323,324,1,0,0,0,324,45,1,0,0,0,325,330,3,50,25,0,326,327,5,37,0, + 0,327,329,3,50,25,0,328,326,1,0,0,0,329,332,1,0,0,0,330,328,1,0,0,0,330, + 331,1,0,0,0,331,47,1,0,0,0,332,330,1,0,0,0,333,338,3,52,26,0,334,335,5, + 37,0,0,335,337,3,52,26,0,336,334,1,0,0,0,337,340,1,0,0,0,338,336,1,0,0, + 0,338,339,1,0,0,0,339,49,1,0,0,0,340,338,1,0,0,0,341,342,7,2,0,0,342,51, + 1,0,0,0,343,344,5,77,0,0,344,53,1,0,0,0,345,388,5,46,0,0,346,347,3,86,43, + 0,347,348,5,67,0,0,348,388,1,0,0,0,349,388,3,84,42,0,350,388,3,86,43,0, + 351,388,3,80,40,0,352,388,5,49,0,0,353,388,3,88,44,0,354,355,5,65,0,0,355, + 360,3,82,41,0,356,357,5,35,0,0,357,359,3,82,41,0,358,356,1,0,0,0,359,362, + 1,0,0,0,360,358,1,0,0,0,360,361,1,0,0,0,361,363,1,0,0,0,362,360,1,0,0,0, + 363,364,5,66,0,0,364,388,1,0,0,0,365,366,5,65,0,0,366,371,3,80,40,0,367, + 368,5,35,0,0,368,370,3,80,40,0,369,367,1,0,0,0,370,373,1,0,0,0,371,369, + 1,0,0,0,371,372,1,0,0,0,372,374,1,0,0,0,373,371,1,0,0,0,374,375,5,66,0, + 0,375,388,1,0,0,0,376,377,5,65,0,0,377,382,3,88,44,0,378,379,5,35,0,0,379, + 381,3,88,44,0,380,378,1,0,0,0,381,384,1,0,0,0,382,380,1,0,0,0,382,383,1, + 0,0,0,383,385,1,0,0,0,384,382,1,0,0,0,385,386,5,66,0,0,386,388,1,0,0,0, + 387,345,1,0,0,0,387,346,1,0,0,0,387,349,1,0,0,0,387,350,1,0,0,0,387,351, + 1,0,0,0,387,352,1,0,0,0,387,353,1,0,0,0,387,354,1,0,0,0,387,365,1,0,0,0, + 387,376,1,0,0,0,388,55,1,0,0,0,389,390,5,10,0,0,390,391,5,28,0,0,391,57, + 1,0,0,0,392,393,5,16,0,0,393,398,3,60,30,0,394,395,5,35,0,0,395,397,3,60, + 30,0,396,394,1,0,0,0,397,400,1,0,0,0,398,396,1,0,0,0,398,399,1,0,0,0,399, + 59,1,0,0,0,400,398,1,0,0,0,401,403,3,10,5,0,402,404,7,3,0,0,403,402,1,0, + 0,0,403,404,1,0,0,0,404,407,1,0,0,0,405,406,5,47,0,0,406,408,7,4,0,0,407, + 405,1,0,0,0,407,408,1,0,0,0,408,61,1,0,0,0,409,410,5,9,0,0,410,415,3,48, + 24,0,411,412,5,35,0,0,412,414,3,48,24,0,413,411,1,0,0,0,414,417,1,0,0,0, + 415,413,1,0,0,0,415,416,1,0,0,0,416,63,1,0,0,0,417,415,1,0,0,0,418,419, + 5,2,0,0,419,424,3,48,24,0,420,421,5,35,0,0,421,423,3,48,24,0,422,420,1, + 0,0,0,423,426,1,0,0,0,424,422,1,0,0,0,424,425,1,0,0,0,425,65,1,0,0,0,426, + 424,1,0,0,0,427,428,5,13,0,0,428,433,3,68,34,0,429,430,5,35,0,0,430,432, + 3,68,34,0,431,429,1,0,0,0,432,435,1,0,0,0,433,431,1,0,0,0,433,434,1,0,0, + 0,434,67,1,0,0,0,435,433,1,0,0,0,436,437,3,48,24,0,437,438,5,81,0,0,438, + 439,3,48,24,0,439,69,1,0,0,0,440,441,5,1,0,0,441,442,3,18,9,0,442,444,3, + 88,44,0,443,445,3,76,38,0,444,443,1,0,0,0,444,445,1,0,0,0,445,71,1,0,0, + 0,446,447,5,7,0,0,447,448,3,18,9,0,448,449,3,88,44,0,449,73,1,0,0,0,450, + 451,5,12,0,0,451,452,3,46,23,0,452,75,1,0,0,0,453,458,3,78,39,0,454,455, + 5,35,0,0,455,457,3,78,39,0,456,454,1,0,0,0,457,460,1,0,0,0,458,456,1,0, + 0,0,458,459,1,0,0,0,459,77,1,0,0,0,460,458,1,0,0,0,461,462,3,50,25,0,462, + 463,5,33,0,0,463,464,3,54,27,0,464,79,1,0,0,0,465,466,7,5,0,0,466,81,1, + 0,0,0,467,470,3,84,42,0,468,470,3,86,43,0,469,467,1,0,0,0,469,468,1,0,0, + 0,470,83,1,0,0,0,471,473,7,0,0,0,472,471,1,0,0,0,472,473,1,0,0,0,473,474, + 1,0,0,0,474,475,5,29,0,0,475,85,1,0,0,0,476,478,7,0,0,0,477,476,1,0,0,0, + 477,478,1,0,0,0,478,479,1,0,0,0,479,480,5,28,0,0,480,87,1,0,0,0,481,482, + 5,27,0,0,482,89,1,0,0,0,483,484,7,6,0,0,484,91,1,0,0,0,485,486,5,5,0,0, + 486,487,3,94,47,0,487,93,1,0,0,0,488,489,5,65,0,0,489,490,3,2,1,0,490,491, + 5,66,0,0,491,95,1,0,0,0,492,493,5,15,0,0,493,494,5,97,0,0,494,97,1,0,0, + 0,495,496,5,11,0,0,496,497,5,101,0,0,497,99,1,0,0,0,498,499,5,3,0,0,499, + 502,5,87,0,0,500,501,5,85,0,0,501,503,3,48,24,0,502,500,1,0,0,0,502,503, + 1,0,0,0,503,513,1,0,0,0,504,505,5,86,0,0,505,510,3,102,51,0,506,507,5,35, + 0,0,507,509,3,102,51,0,508,506,1,0,0,0,509,512,1,0,0,0,510,508,1,0,0,0, + 510,511,1,0,0,0,511,514,1,0,0,0,512,510,1,0,0,0,513,504,1,0,0,0,513,514, + 1,0,0,0,514,101,1,0,0,0,515,516,3,48,24,0,516,517,5,33,0,0,517,519,1,0, + 0,0,518,515,1,0,0,0,518,519,1,0,0,0,519,520,1,0,0,0,520,521,3,48,24,0,521, + 103,1,0,0,0,50,115,123,138,150,159,167,171,179,181,186,193,198,205,211, + 219,221,232,239,250,253,267,275,283,287,293,301,313,317,323,330,338,360, + 371,382,387,398,403,407,415,424,433,444,458,469,472,477,502,510,513,518]; private static __ATN: ATN; public static get _ATN(): ATN { @@ -4156,9 +4059,6 @@ export class FromCommandContext extends ParserRuleContext { public metadata(): MetadataContext { return this.getTypedRuleContext(MetadataContext, 0) as MetadataContext; } - public fromOptions(): FromOptionsContext { - return this.getTypedRuleContext(FromOptionsContext, 0) as FromOptionsContext; - } public get ruleIndex(): number { return esql_parser.RULE_fromCommand; } @@ -4199,72 +4099,6 @@ export class FromIdentifierContext extends ParserRuleContext { } -export class FromOptionsContext extends ParserRuleContext { - constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { - super(parent, invokingState); - this.parser = parser; - } - public OPTIONS(): TerminalNode { - return this.getToken(esql_parser.OPTIONS, 0); - } - public configOption_list(): ConfigOptionContext[] { - return this.getTypedRuleContexts(ConfigOptionContext) as ConfigOptionContext[]; - } - public configOption(i: number): ConfigOptionContext { - return this.getTypedRuleContext(ConfigOptionContext, i) as ConfigOptionContext; - } - public COMMA_list(): TerminalNode[] { - return this.getTokens(esql_parser.COMMA); - } - public COMMA(i: number): TerminalNode { - return this.getToken(esql_parser.COMMA, i); - } - public get ruleIndex(): number { - return esql_parser.RULE_fromOptions; - } - public enterRule(listener: esql_parserListener): void { - if(listener.enterFromOptions) { - listener.enterFromOptions(this); - } - } - public exitRule(listener: esql_parserListener): void { - if(listener.exitFromOptions) { - listener.exitFromOptions(this); - } - } -} - - -export class ConfigOptionContext extends ParserRuleContext { - constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { - super(parent, invokingState); - this.parser = parser; - } - public string__list(): StringContext[] { - return this.getTypedRuleContexts(StringContext) as StringContext[]; - } - public string_(i: number): StringContext { - return this.getTypedRuleContext(StringContext, i) as StringContext; - } - public ASSIGN(): TerminalNode { - return this.getToken(esql_parser.ASSIGN, 0); - } - public get ruleIndex(): number { - return esql_parser.RULE_configOption; - } - public enterRule(listener: esql_parserListener): void { - if(listener.enterConfigOption) { - listener.enterConfigOption(this); - } - } - public exitRule(listener: esql_parserListener): void { - if(listener.exitConfigOption) { - listener.exitConfigOption(this); - } - } -} - - export class MetadataContext extends ParserRuleContext { constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); diff --git a/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts b/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts index 82bd908fa2b80..75847ae8805ac 100644 --- a/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts +++ b/packages/kbn-esql-ast/src/antlr/esql_parser_listener.ts @@ -34,8 +34,6 @@ import { FieldsContext } from "./esql_parser"; import { FieldContext } from "./esql_parser"; import { FromCommandContext } from "./esql_parser"; import { FromIdentifierContext } from "./esql_parser"; -import { FromOptionsContext } from "./esql_parser"; -import { ConfigOptionContext } from "./esql_parser"; import { MetadataContext } from "./esql_parser"; import { MetadataOptionContext } from "./esql_parser"; import { Deprecated_metadataContext } from "./esql_parser"; @@ -425,26 +423,6 @@ export default class esql_parserListener extends ParseTreeListener { * @param ctx the parse tree */ exitFromIdentifier?: (ctx: FromIdentifierContext) => void; - /** - * Enter a parse tree produced by `esql_parser.fromOptions`. - * @param ctx the parse tree - */ - enterFromOptions?: (ctx: FromOptionsContext) => void; - /** - * Exit a parse tree produced by `esql_parser.fromOptions`. - * @param ctx the parse tree - */ - exitFromOptions?: (ctx: FromOptionsContext) => void; - /** - * Enter a parse tree produced by `esql_parser.configOption`. - * @param ctx the parse tree - */ - enterConfigOption?: (ctx: ConfigOptionContext) => void; - /** - * Exit a parse tree produced by `esql_parser.configOption`. - * @param ctx the parse tree - */ - exitConfigOption?: (ctx: ConfigOptionContext) => void; /** * Enter a parse tree produced by `esql_parser.metadata`. * @param ctx the parse tree diff --git a/packages/kbn-esql-ast/src/ast_helpers.ts b/packages/kbn-esql-ast/src/ast_helpers.ts index d2ac5a026e316..7104eef95a062 100644 --- a/packages/kbn-esql-ast/src/ast_helpers.ts +++ b/packages/kbn-esql-ast/src/ast_helpers.ts @@ -218,7 +218,7 @@ function getQuotedText(ctx: ParserRuleContext) { } function getUnquotedText(ctx: ParserRuleContext) { - return [67 /* esql_parser.UNQUOTED_IDENTIFIER */, 74 /* esql_parser.FROM_UNQUOTED_IDENTIFIER */] + return [67 /* esql_parser.UNQUOTED_IDENTIFIER */, 73 /* esql_parser.FROM_UNQUOTED_IDENTIFIER */] .map((keyCode) => ctx.getToken(keyCode, 0)) .filter(nonNullable)[0]; } From b5c3cb60b8b2cb986f295aa764b8bb80adcb45bb Mon Sep 17 00:00:00 2001 From: Joe McElroy Date: Thu, 16 May 2024 19:56:41 +0100 Subject: [PATCH 21/71] [Search] [Playground] Index: Include aliases (#183629) ## Summary Include aliases in the indices search ### Checklist Delete any items that are not applicable to this PR. - [ ] 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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] 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)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- .../server/lib/fetch_indices.test.ts | 14 +++++++++++--- .../server/lib/fetch_indices.ts | 19 +++++++++++++++++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/search_playground/server/lib/fetch_indices.test.ts b/x-pack/plugins/search_playground/server/lib/fetch_indices.test.ts index 8613609b38fc4..af9c6adfc8ae2 100644 --- a/x-pack/plugins/search_playground/server/lib/fetch_indices.test.ts +++ b/x-pack/plugins/search_playground/server/lib/fetch_indices.test.ts @@ -19,8 +19,8 @@ describe('fetch indices', () => { }, 'index-2': { aliases: { - 'search-alias-1': {}, - 'search-alias-2': {}, + 'search-alias-3': {}, + 'search-alias-4': {}, }, }, 'index-3': { @@ -48,7 +48,15 @@ describe('fetch indices', () => { ); expect(indexData).toEqual({ - indexNames: ['index-1', 'index-2', 'index-3'], + indexNames: [ + 'index-1', + 'index-2', + 'index-3', + 'search-alias-1', + 'search-alias-2', + 'search-alias-3', + 'search-alias-4', + ], }); }); }); diff --git a/x-pack/plugins/search_playground/server/lib/fetch_indices.ts b/x-pack/plugins/search_playground/server/lib/fetch_indices.ts index 953e7a980a6a9..51b72790028c2 100644 --- a/x-pack/plugins/search_playground/server/lib/fetch_indices.ts +++ b/x-pack/plugins/search_playground/server/lib/fetch_indices.ts @@ -42,9 +42,24 @@ export const fetchIndices = async ( !isHidden(allIndexMatches[indexName]) && !isClosed(allIndexMatches[indexName]) ); + + const allAliases = allIndexNames.reduce((acc, indexName) => { + const aliases = allIndexMatches[indexName].aliases; + if (aliases) { + Object.keys(aliases).forEach((alias) => { + if (!acc.includes(alias)) { + acc.push(alias); + } + }); + } + return acc; + }, []); + + const allOptions = [...allIndexNames, ...allAliases]; + const indexNames = searchQuery - ? allIndexNames.filter((indexName) => indexName.includes(searchQuery.toLowerCase())) - : allIndexNames; + ? allOptions.filter((indexName) => indexName.includes(searchQuery.toLowerCase())) + : allOptions; return { indexNames, From ea23d8d93ad609189ffa0260bb7c12f1fd4fc508 Mon Sep 17 00:00:00 2001 From: Joe McElroy Date: Thu, 16 May 2024 19:57:33 +0100 Subject: [PATCH 22/71] [Search] [Playground] Add Dev console (#183647) ## Summary Add dev console to playground on es3 ### Checklist Delete any items that are not applicable to this PR. - [ ] 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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] 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)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/search_playground/kibana.jsonc | 1 + .../public/chat_playground_overview.tsx | 13 ++++++++++++- x-pack/plugins/search_playground/public/types.ts | 3 +++ x-pack/plugins/search_playground/tsconfig.json | 3 ++- .../test_suites/search/playground_overview.ts | 5 +++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/search_playground/kibana.jsonc b/x-pack/plugins/search_playground/kibana.jsonc index 221d5bf9e1873..28a775eca341e 100644 --- a/x-pack/plugins/search_playground/kibana.jsonc +++ b/x-pack/plugins/search_playground/kibana.jsonc @@ -21,6 +21,7 @@ ], "optionalPlugins": [ "cloud", + "console", "usageCollection", ], "requiredBundles": [ diff --git a/x-pack/plugins/search_playground/public/chat_playground_overview.tsx b/x-pack/plugins/search_playground/public/chat_playground_overview.tsx index b750a9828021d..d40996ba91455 100644 --- a/x-pack/plugins/search_playground/public/chat_playground_overview.tsx +++ b/x-pack/plugins/search_playground/public/chat_playground_overview.tsx @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useMemo } from 'react'; import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiPageTemplate } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { PlaygroundProvider } from './providers/playground_provider'; @@ -14,8 +14,18 @@ import { PlaygroundProvider } from './providers/playground_provider'; import { App } from './components/app'; import { PlaygroundToolbar } from './embeddable'; import { PlaygroundHeaderDocs } from './components/playground_header_docs'; +import { useKibana } from './hooks/use_kibana'; export const ChatPlaygroundOverview: React.FC = () => { + const { + services: { console: consolePlugin }, + } = useKibana(); + + const embeddableConsole = useMemo( + () => (consolePlugin?.EmbeddableConsole ? : null), + [consolePlugin] + ); + return ( { rightSideItems={[, ]} /> + {embeddableConsole} ); diff --git a/x-pack/plugins/search_playground/public/types.ts b/x-pack/plugins/search_playground/public/types.ts index 6f8552d753bbb..84edcf1b07e05 100644 --- a/x-pack/plugins/search_playground/public/types.ts +++ b/x-pack/plugins/search_playground/public/types.ts @@ -21,6 +21,7 @@ import { CloudSetup } from '@kbn/cloud-plugin/public'; import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; import { AppMountParameters } from '@kbn/core/public'; import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; +import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import { ChatRequestData } from '../common/types'; import type { App } from './components/app'; import type { PlaygroundProvider as PlaygroundProviderComponent } from './providers/playground_provider'; @@ -44,6 +45,7 @@ export interface AppPluginStartDependencies { navigation: NavigationPublicPluginStart; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; share: SharePluginStart; + console?: ConsolePluginStart; } export interface AppServicesContext { @@ -53,6 +55,7 @@ export interface AppServicesContext { cloud?: CloudSetup; triggersActionsUi: TriggersAndActionsUIPublicPluginStart; usageCollection?: UsageCollectionStart; + console?: ConsolePluginStart; } export enum ChatFormFields { diff --git a/x-pack/plugins/search_playground/tsconfig.json b/x-pack/plugins/search_playground/tsconfig.json index 2fc516bc2ca46..96c2c1681008a 100644 --- a/x-pack/plugins/search_playground/tsconfig.json +++ b/x-pack/plugins/search_playground/tsconfig.json @@ -39,7 +39,8 @@ "@kbn/core-logging-server-mocks", "@kbn/analytics", "@kbn/usage-collection-plugin", - "@kbn/analytics-client" + "@kbn/analytics-client", + "@kbn/console-plugin" ], "exclude": [ "target/**/*", diff --git a/x-pack/test_serverless/functional/test_suites/search/playground_overview.ts b/x-pack/test_serverless/functional/test_suites/search/playground_overview.ts index 17d9d81c1014c..f726287de0fb7 100644 --- a/x-pack/test_serverless/functional/test_suites/search/playground_overview.ts +++ b/x-pack/test_serverless/functional/test_suites/search/playground_overview.ts @@ -6,6 +6,7 @@ */ import { FtrProviderContext } from '../../ftr_provider_context'; +import { testHasEmbeddedConsole } from './embedded_console'; export default function ({ getPageObjects }: FtrProviderContext) { const pageObjects = getPageObjects(['svlCommonPage', 'svlCommonNavigation', 'svlPlaygroundUI']); @@ -23,5 +24,9 @@ export default function ({ getPageObjects }: FtrProviderContext) { await pageObjects.svlPlaygroundUI.PlaygrounStartChatPage.expectPlaygroundStartChatPageComponentsToExist(); await pageObjects.svlPlaygroundUI.PlaygrounStartChatPage.expectPlaygroundHeaderComponentsToExist(); }); + + it('has embedded console', async () => { + await testHasEmbeddedConsole(pageObjects); + }); }); } From fd4de545caf796a87de8472df96d1f1e47c068be Mon Sep 17 00:00:00 2001 From: Andrew Macri Date: Thu, 16 May 2024 16:14:48 -0400 Subject: [PATCH 23/71] [Security Solution] [Attack discovery] Fixes Attack discovery loading popover issues (#183675) ## [Security Solution] [Attack discovery] Fixes Attack discovery loading popover issues ### Summary This PR fixes issues related to the Attack discovery loading popover, where it may not be displayed, or get stuck in an open state ### Desk testing 1) Configure at least two generative AI connectors 2) Clear local storage 3) Close all open browser tabs with sessions to Kibana 4) Navigate to Security > Attack discovery 5) Select the first connector 6) Click Generate **Expected results** - While loading, the information (i) icon (popover anchor) will NOT be displayed, because it's the first generation interval for the selected connector - Attack discoveries are generated for the selected connector 7) Once again, click Generate **Expected result** - While loading, the information (i) icon (popover anchor) IS displayed, because there's now at least one generation interval available in local storage 8) Click the information (i) icon **Expected result** - The popover containing the text `Remaining time is based on the average speed...` is displayed - The popover includes the time of the last generation 9) Click anywhere outside the popover **Expected results** - The popover closes 10) Keep opening and closing the popover while the selected connector is loading **Expected results** - The popover continues to open and close as expected - Attack discoveries are once again generated for the currently selected connector 11) Select the other (2nd) connector **Expected results** - The `Up to 20 alerts will be analyzed` empty state is displayed 12) Click Generate for the newly-selected connector **Expected results** - While loading, the information (i) icon (popover anchor) will NOT be displayed, because it's the first generation interval for the newly selected connector - Attack discoveries are generated for the newly selected connector 13) Once again, click Generate **Expected result** - While loading, the information (i) icon (popover anchor) IS displayed, because there's now at least one generation interval available in local storage for the selected (2nd) connector 14) Click on the information (i) icon (popover anchor) **Expected result** - The popover containing the text `Remaining time is based on the average speed...` is displayed - The popover contains one date (for the previous generation) 15) Click anywhere outside the popover **Expected results** - The popover closes 16) While the 2nd connector is STILL loading, select the first connector **Expected result** - The cached attack discovery results for the first connector are displayed - The loading callout is hidden - The Generate button is disabled, and instead displays the text `Loading...` 17) While the 2nd connector is STILL loading, re-select the 2nd connector **Expected result** - The countdown timer is once again displayed, because the 2nd connector's was still loading - Attack discoveries are generated for the 2nd connector 18) Close the browser 19) Open the browser 20) Navigate to Security > Attack discovery **Expected result** - The 2nd connector is still selected 21) Click Generate **Expected result** - While loading, the information (i) icon (popover anchor) will be displayed, because it's using the results from local storage 22) Click on the information (i) icon (popover anchor) **Expected result** - The popover containing the text `Remaining time is based on the average speed...` is displayed - The popover contains two datetimes, for the two previous runs of the 2nd connector 23) Click anywhere outside the popover **Expected results** - The popover closes --- .../pages/loading_callout/countdown/index.tsx | 21 +- .../pages/session_storage/index.test.ts | 180 ++++++++++++++++++ .../pages/session_storage/index.ts | 14 +- 3 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.test.ts diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx index e49050c54c954..8a61704ef7361 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/loading_callout/countdown/index.tsx @@ -9,6 +9,7 @@ import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, + EuiOutsideClickDetector, EuiPopover, EuiText, useEuiTheme, @@ -80,15 +81,17 @@ const CountdownComponent: React.FC = ({ approximateFutureTime, connectorI justifyContent="spaceBetween" > - - - + closePopover()}> + + + + diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.test.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.test.ts new file mode 100644 index 0000000000000..dd5932bbb3dd7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.test.ts @@ -0,0 +1,180 @@ +/* + * 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 { GenerationInterval } from '../../types'; +import { + encodeGenerationIntervals, + decodeGenerationIntervals, + getLocalStorageGenerationIntervals, + setLocalStorageGenerationIntervals, +} from '.'; + +const key = 'elasticAssistantDefault.attackDiscovery.default.generationIntervals'; + +const generationIntervals: Record = { + 'test-connector-1': [ + { + connectorId: 'test-connector-1', + date: new Date('2024-05-16T14:13:09.838Z'), + durationMs: 173648, + }, + { + connectorId: 'test-connector-1', + date: new Date('2024-05-16T13:59:49.620Z'), + durationMs: 146605, + }, + { + connectorId: 'test-connector-1', + date: new Date('2024-05-16T13:47:00.629Z'), + durationMs: 255163, + }, + ], + testConnector2: [ + { + connectorId: 'testConnector2', + date: new Date('2024-05-16T14:26:25.273Z'), + durationMs: 130447, + }, + ], + testConnector3: [ + { + connectorId: 'testConnector3', + date: new Date('2024-05-16T14:36:53.171Z'), + durationMs: 46614, + }, + { + connectorId: 'testConnector3', + date: new Date('2024-05-16T14:27:17.187Z'), + durationMs: 44129, + }, + ], +}; + +describe('storage', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('encodeGenerationIntervals', () => { + it('returns null when generationIntervals is invalid', () => { + const invalidGenerationIntervals: Record = + 1n as unknown as Record; // <-- invalid + + const result = encodeGenerationIntervals(invalidGenerationIntervals); + + expect(result).toBeNull(); + }); + + it('returns the expected encoded generationIntervals', () => { + const result = encodeGenerationIntervals(generationIntervals); + + expect(result).toEqual(JSON.stringify(generationIntervals)); + }); + }); + + describe('decodeGenerationIntervals', () => { + it('returns null when generationIntervals is invalid', () => { + const invalidGenerationIntervals = 'invalid generation intervals'; // <-- invalid + + const result = decodeGenerationIntervals(invalidGenerationIntervals); + + expect(result).toBeNull(); + }); + + it('returns the expected decoded generation intervals', () => { + const encoded = encodeGenerationIntervals(generationIntervals) ?? ''; // <-- valid intervals + + const result = decodeGenerationIntervals(encoded); + + expect(result).toEqual(generationIntervals); + }); + + it('parses date strings into Date objects', () => { + const encoded = JSON.stringify({ + 'test-connector-1': [ + { + connectorId: 'test-connector-1', + date: '2024-05-16T14:13:09.838Z', + durationMs: 173648, + }, + ], + }); + + const result = decodeGenerationIntervals(encoded); + + expect(result).toEqual({ + 'test-connector-1': [ + { + connectorId: 'test-connector-1', + date: new Date('2024-05-16T14:13:09.838Z'), + durationMs: 173648, + }, + ], + }); + }); + + it('returns null when date is not a string', () => { + const encoded = JSON.stringify({ + 'test-connector-1': [ + { + connectorId: 'test-connector-1', + date: 1234, // <-- invalid + durationMs: 173648, + }, + ], + }); + + const result = decodeGenerationIntervals(encoded); + + expect(result).toBeNull(); + }); + }); + + describe('getLocalStorageGenerationIntervals', () => { + it('returns null when the key is empty', () => { + const result = getLocalStorageGenerationIntervals(''); // <-- empty key + + expect(result).toBeNull(); + }); + + it('returns null the key is unknown', () => { + const result = getLocalStorageGenerationIntervals('unknown key'); // <-- unknown key + + expect(result).toBeNull(); + }); + + it('returns null when the generation intervals are invalid', () => { + localStorage.setItem(key, 'invalid generation intervals'); // <-- invalid + + const result = getLocalStorageGenerationIntervals(key); + + expect(result).toBeNull(); + }); + + it('returns the expected decoded generation intervals', () => { + const encoded = encodeGenerationIntervals(generationIntervals) ?? ''; // <-- valid intervals + localStorage.setItem(key, encoded); + + const decoded = decodeGenerationIntervals(encoded); + const result = getLocalStorageGenerationIntervals(key); + + expect(result).toEqual(decoded); + }); + }); + + describe('setLocalStorageGenerationIntervals', () => { + const localStorageSetItemSpy = jest.spyOn(Storage.prototype, 'setItem'); + + it('sets the encoded generation intervals in localStorage', () => { + const encoded = encodeGenerationIntervals(generationIntervals) ?? ''; + + setLocalStorageGenerationIntervals({ key, generationIntervals }); + + expect(localStorageSetItemSpy).toHaveBeenCalledWith(key, encoded); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts index c959374167504..8c8c49b482650 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/session_storage/index.ts @@ -76,8 +76,18 @@ export const encodeGenerationIntervals = ( export const decodeGenerationIntervals = ( generationIntervals: string ): Record | null => { + const parseDate = (key: string, value: unknown) => { + if (key === 'date' && typeof value === 'string') { + return new Date(value); + } else if (key === 'date' && typeof value !== 'string') { + throw new Error('Invalid date'); + } else { + return value; + } + }; + try { - return JSON.parse(generationIntervals); + return JSON.parse(generationIntervals, parseDate); } catch { return null; } @@ -87,7 +97,7 @@ export const getLocalStorageGenerationIntervals = ( key: string ): Record | null => { if (!isEmpty(key)) { - return decodeGenerationIntervals(sessionStorage.getItem(key) ?? ''); + return decodeGenerationIntervals(localStorage.getItem(key) ?? ''); } return null; From e6a91bcceca8a1c44bbcf499bc87c6e894f55d12 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 16 May 2024 22:52:57 +0100 Subject: [PATCH 24/71] skip flaky suite (#183566) --- .../functional/apps/dashboard/group3/reporting/screenshots.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts b/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts index e250d5972247b..d7bd90c5e74d6 100644 --- a/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts +++ b/x-pack/test/functional/apps/dashboard/group3/reporting/screenshots.ts @@ -155,7 +155,8 @@ export default function ({ }); }); - describe('Preserve Layout', () => { + // FLAKY: https://github.com/elastic/kibana/issues/183566 + describe.skip('Preserve Layout', () => { before(async () => { await loadEcommerce(); }); From 12874e4deca193d91f7a17c19a35c4f25168dcd1 Mon Sep 17 00:00:00 2001 From: Saikat Sarkar <132922331+saikatsarkar056@users.noreply.github.com> Date: Thu, 16 May 2024 17:22:07 -0600 Subject: [PATCH 25/71] [ES3] Add ingest pipeline section to Index management view (#182604) In this PR, we've incorporated ingest pipeline selection into the "Ingest Data" section for the serverless solution. When a user chooses an ingest pipeline, the corresponding code snippet will adjust accordingly. ## Testing steps - Start elasticsearch in serverless env - yarn es serverless --projectType es - Start kibana in serverless env - yarn serverless-es ## Screen-recording https://github.com/elastic/kibana/assets/132922331/d2f5b74f-91a6-4be2-8323-ee3eda5afa02 --- .../components/ingest_data.tsx | 16 +++ .../ingest_pipeline_options.tsx | 126 ++++++++++++++++++ .../ingest_pipeline_panel.test.tsx | 118 ++++++++++++++++ .../ingest_pipeline_panel.tsx | 89 +++++++++++++ packages/kbn-search-api-panels/index.tsx | 1 + .../languages/console.ts | 4 +- packages/kbn-search-api-panels/tsconfig.json | 3 +- packages/kbn-search-api-panels/utils.test.ts | 4 +- .../panels/add_data_panel_content.tsx | 2 +- .../index_management/api_empty_prompt.tsx | 18 +++ .../application/components/languages/curl.ts | 12 +- .../components/languages/dotnet.ts | 12 +- .../application/components/languages/go.ts | 8 +- .../application/components/languages/java.ts | 10 +- .../components/languages/javascript.ts | 7 +- .../application/components/languages/php.ts | 28 ++-- .../components/languages/python.ts | 7 +- .../application/components/languages/ruby.ts | 13 +- .../application/components/overview.tsx | 12 +- .../hooks/api/use_ingest_pipelines.tsx | 21 +++ .../serverless_search/server/plugin.ts | 2 + .../server/routes/ingest_pipeline_routes.ts | 28 ++++ 22 files changed, 501 insertions(+), 40 deletions(-) create mode 100644 packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_options.tsx create mode 100644 packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.test.tsx create mode 100644 packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx create mode 100644 x-pack/plugins/serverless_search/public/application/hooks/api/use_ingest_pipelines.tsx create mode 100644 x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts diff --git a/packages/kbn-search-api-panels/components/ingest_data.tsx b/packages/kbn-search-api-panels/components/ingest_data.tsx index 0700d2d56d661..e842c499630bb 100644 --- a/packages/kbn-search-api-panels/components/ingest_data.tsx +++ b/packages/kbn-search-api-panels/components/ingest_data.tsx @@ -13,6 +13,8 @@ import { i18n } from '@kbn/i18n'; import type { ApplicationStart } from '@kbn/core-application-browser'; import type { ConsolePluginStart } from '@kbn/console-plugin/public'; import type { SharePluginStart } from '@kbn/share-plugin/public'; +import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/types'; +import { IngestPipelinePanel } from './ingest_pipelines/ingest_pipeline_panel'; import { CodeBox } from './code_box'; import { LanguageDefinition } from '../types'; import { OverviewPanel } from './overview_panel'; @@ -32,11 +34,16 @@ interface IngestDataProps { languages: LanguageDefinition[]; consoleRequest?: string; additionalIngestionPanel?: React.ReactNode; + ingestPipelineData?: IngestGetPipelineResponse; + selectedPipeline: string; + setSelectedPipeline: (pipelineId: string) => void; + defaultIngestPipeline: string; } export const IngestData: React.FC = ({ codeSnippet, selectedLanguage, + selectedPipeline, setSelectedLanguage, docLinks, assetBasePath, @@ -46,6 +53,9 @@ export const IngestData: React.FC = ({ languages, consoleRequest, additionalIngestionPanel, + ingestPipelineData, + setSelectedPipeline, + defaultIngestPipeline, }) => { return ( = ({ })} > +

{i18n.translate('searchApiPanels.welcomeBanner.ingestData.alternativeOptions', { diff --git a/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_options.tsx b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_options.tsx new file mode 100644 index 0000000000000..6a4888a643d4f --- /dev/null +++ b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_options.tsx @@ -0,0 +1,126 @@ +/* + * 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 React, { Fragment } from 'react'; +import { EuiFlexItem, EuiText, EuiBadge, EuiFlexGroup } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; +import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/types'; + +interface OptionItem { + value: string; + inputDisplay: string; + dropdownDisplay: JSX.Element; +} + +export interface IngestPipelineWithDeprecated extends IngestPipeline { + deprecated?: boolean; +} + +const ProcessorCount = ({ item }: { item: IngestPipelineWithDeprecated | undefined }) => ( + + +

+ {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.processorCount', { + defaultMessage: '{count} {count, plural, one {processor} other {processors}}', + values: { count: item?.processors?.length }, + })} +

+
+
+); + +const ManagedBadge = ({ item }: { item: IngestPipelineWithDeprecated | undefined }) => { + if (!item?._meta?.managed) return null; + return ( + + + {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.managedBadge', { + defaultMessage: 'Managed', + })} + + + ); +}; + +const RecommendedBadge = ({ + pipelineName, + defaultIngestPipeline, +}: { + pipelineName: string; + defaultIngestPipeline: string; +}) => { + if (pipelineName !== defaultIngestPipeline) return null; + return ( + + + {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.recommendedBadge', { + defaultMessage: 'Recommended', + })} + + + ); +}; + +const createOptionItem = ( + pipelineName: string, + item: IngestPipelineWithDeprecated | undefined, + defaultIngestPipeline: string +): OptionItem => { + return { + value: pipelineName, + inputDisplay: pipelineName, + dropdownDisplay: ( + + {pipelineName} + + + + + + + ), + }; +}; + +export const createIngestPipelineOptions = ( + ingestPipelinesData: IngestGetPipelineResponse | undefined, + defaultIngestPipeline: string +) => { + if (!ingestPipelinesData) return []; + + let options = Object.keys(ingestPipelinesData) + .filter( + (pipelineName: string) => + !(ingestPipelinesData[pipelineName] as IngestPipelineWithDeprecated)?.deprecated + ) + .map((pipelineName) => + createOptionItem(pipelineName, ingestPipelinesData[pipelineName], defaultIngestPipeline) + ); + + if (ingestPipelinesData[defaultIngestPipeline]) { + const defaultOption = createOptionItem( + defaultIngestPipeline, + ingestPipelinesData[defaultIngestPipeline], + defaultIngestPipeline + ); + options = [ + defaultOption, + ...options.filter((option) => option.value !== defaultIngestPipeline), + ]; + } + + return options; +}; diff --git a/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.test.tsx b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.test.tsx new file mode 100644 index 0000000000000..040136864f295 --- /dev/null +++ b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.test.tsx @@ -0,0 +1,118 @@ +/* + * 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 { registerTestBed } from '@kbn/test-jest-helpers'; +import { act } from 'react-dom/test-utils'; +import { IngestPipelinePanel } from './ingest_pipeline_panel'; + +const DEFAULT_INGESTION_PIPELINE = 'default-ingestion-pipeline'; + +describe('IngestPipelinePanel', () => { + const setSelectedPipelineMock = jest.fn(); + + const mockPipelineData = { + pipeline1: { + processors: ['processor1', 'processor2'], + _meta: { + managed: true, + }, + }, + pipeline2: { + processors: ['processor1'], + _meta: { + managed: false, + }, + }, + [DEFAULT_INGESTION_PIPELINE]: { + processors: ['processor1', 'processor2', 'processor3'], + _meta: { + managed: true, + }, + }, + deprecated_pipeline: { + processors: ['processor1'], + _meta: { + managed: false, + }, + deprecated: true, + }, + } as any; + + let exists: any; + let find: any; + + beforeAll(async () => { + const setup = registerTestBed(IngestPipelinePanel, { + defaultProps: { + setSelectedPipeline: setSelectedPipelineMock, + ingestPipelinesData: mockPipelineData, + defaultIngestPipeline: DEFAULT_INGESTION_PIPELINE, + }, + memoryRouter: { wrapComponent: false }, + }); + + await act(async () => { + const testBed = setup(); + exists = testBed.exists; + find = testBed.find; + }); + }); + + it('should display Process Data section', () => { + expect(exists('ingestPipelinePanelTitle')).toBe(true); + expect(find('ingestPipelinePanelTitle').contains('Preprocess your data')).toBe(true); + expect( + find('ingestPipelinePanelBody').contains( + 'You can use ingest pipelines to preprocess data before indexing into Elasticsearch.' + ) + ).toBe(true); + expect(find('ingestPipelinePanelTitle').find('.euiBadge__text').contains('Optional')).toBe( + true + ); + }); + + it('should display number of processors', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect(find('ingestPipelinePanelOptions').at(0).contains('3 processors')).toBe(true); + expect(find('ingestPipelinePanelOptions').at(1).contains('2 processors')).toBe(true); + expect(find('ingestPipelinePanelOptions').at(2).contains('1 processor')).toBe(true); + }); + + it('should display the badges correctly', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect( + find('ingestPipelinePanelOptions').at(0).find('.euiBadge__text').contains('Recommended') + ).toBe(true); + expect( + find('ingestPipelinePanelOptions').at(1).find('.euiBadge__text').contains('Managed') + ).toBe(true); + expect( + find('ingestPipelinePanelOptions').at(2).find('.euiBadge__text').contains('Managed') + ).toBe(false); + }); + + it('should display only active pipelines', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect(find('ingestPipelinePanelOptionTitle').contains('pipeline1')).toBe(true); + expect(find('ingestPipelinePanelOptionTitle').contains('deprecated_pipeline')).toBe(false); + }); + + it('should display the recommended pipeline at the beginning', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect(find('ingestPipelinePanelOptionTitle').at(0).contains(DEFAULT_INGESTION_PIPELINE)).toBe( + true + ); + }); + + describe('when there exists no ingest pipeline', () => { + it('should display an empty list of pipelines', () => { + find('ingestPipelinePanelSelect').simulate('click'); + expect(exists('ingestPipelinePanelOptions')).toBe(false); + }); + }); +}); diff --git a/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx new file mode 100644 index 0000000000000..a746b2bc0e61e --- /dev/null +++ b/packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx @@ -0,0 +1,89 @@ +/* + * 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 React, { useMemo } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + EuiTitle, + EuiBadge, + EuiSuperSelect, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { IngestGetPipelineResponse } from '@elastic/elasticsearch/lib/api/types'; +import { createIngestPipelineOptions } from './ingest_pipeline_options'; + +interface IngestPipelinePanelProps { + selectedPipeline: string; + setSelectedPipeline: (pipeline: string) => void; + ingestPipelinesData?: IngestGetPipelineResponse; + defaultIngestPipeline: string; +} + +export const IngestPipelinePanel: React.FC = ({ + selectedPipeline, + setSelectedPipeline, + ingestPipelinesData, + defaultIngestPipeline, +}) => { + const options = useMemo( + () => createIngestPipelineOptions(ingestPipelinesData, defaultIngestPipeline), + [ingestPipelinesData, defaultIngestPipeline] + ); + + return ( + <> + + + + + {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.title', { + defaultMessage: 'Preprocess your data', + })} + + + + + + {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.optionalBadge', { + defaultMessage: 'Optional', + })} + + + + + +

+ {i18n.translate('searchApiPanels.welcomeBanner.ingestPipelinePanel.description', { + defaultMessage: + 'You can use ingest pipelines to preprocess data before indexing into Elasticsearch.', + })} +

+
+ + + + + ); +}; diff --git a/packages/kbn-search-api-panels/index.tsx b/packages/kbn-search-api-panels/index.tsx index 10276bc9c5325..f0a64685348de 100644 --- a/packages/kbn-search-api-panels/index.tsx +++ b/packages/kbn-search-api-panels/index.tsx @@ -13,6 +13,7 @@ import { AuthenticatedUser } from '@kbn/security-plugin/common'; export * from './components/cloud_details'; export * from './components/code_box'; +export * from './components/ingest_pipelines/ingest_pipeline_panel'; export * from './components/github_link'; export * from './components/ingest_data'; export * from './components/ingestions_panel'; diff --git a/packages/kbn-search-api-panels/languages/console.ts b/packages/kbn-search-api-panels/languages/console.ts index e156409239242..27aeb96b93ba4 100644 --- a/packages/kbn-search-api-panels/languages/console.ts +++ b/packages/kbn-search-api-panels/languages/console.ts @@ -19,7 +19,9 @@ export const consoleDefinition: Partial = { } } }`, - ingestData: `POST _bulk?pretty + ingestData: ({ ingestPipeline }) => `POST _bulk?pretty${ + ingestPipeline ? `&pipeline=${ingestPipeline}` : '' + } { "index" : { "_index" : "books" } } {"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470} { "index" : { "_index" : "books" } } diff --git a/packages/kbn-search-api-panels/tsconfig.json b/packages/kbn-search-api-panels/tsconfig.json index 20294566e4cc3..a3b9c16a04512 100644 --- a/packages/kbn-search-api-panels/tsconfig.json +++ b/packages/kbn-search-api-panels/tsconfig.json @@ -23,6 +23,7 @@ "@kbn/security-plugin", "@kbn/console-plugin", "@kbn/ui-theme", - "@kbn/try-in-console" + "@kbn/try-in-console", + "@kbn/test-jest-helpers" ] } diff --git a/packages/kbn-search-api-panels/utils.test.ts b/packages/kbn-search-api-panels/utils.test.ts index c842dd03cf275..ef77838250312 100644 --- a/packages/kbn-search-api-panels/utils.test.ts +++ b/packages/kbn-search-api-panels/utils.test.ts @@ -12,8 +12,8 @@ import { getConsoleRequest } from './utils'; describe('utils', () => { describe('getConsoleRequest()', () => { test('accepts string values', () => { - const consoleRequest = getConsoleRequest('ingestData'); - expect(consoleRequest).toEqual(consoleDefinition.ingestData); + const consoleRequest = getConsoleRequest('buildSearchQuery'); + expect(consoleRequest).toEqual(consoleDefinition.buildSearchQuery); }); test('accepts function values', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx index 5d2d8cecf8466..55420cc17895b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx @@ -38,7 +38,7 @@ export const AddDataPanelContent: React.FC = ({ (javaDefinition); + + const [selectedPipeline, setSelectedPipeline] = React.useState(''); const [clientApiKey, setClientApiKey] = useState(API_KEY_PLACEHOLDER); const { elasticsearchURL, cloudId } = useMemo(() => { return { @@ -67,8 +74,11 @@ export const APIIndexEmptyPrompt = ({ indexName, onBackClick }: APIIndexEmptyPro apiKey: clientApiKey, cloudId, indexName, + ingestPipeline: selectedPipeline, }; + const { data: pipelineData } = useIngestPipelines(); + const apiIngestSteps: EuiContainedStepProps[] = [ { title: i18n.translate( @@ -85,6 +95,14 @@ export const APIIndexEmptyPrompt = ({ indexName, onBackClick }: APIIndexEmptyPro selectedLanguage={selectedLanguage.id} /> + + + `curl -X POST "\$\{ES_URL\}/_bulk?pretty"${ + ingestPipeline ? `&pipeline=${ingestPipeline}` : '' + }" \\ -H "Authorization: ApiKey "\$\{API_KEY\}"" \\ -H "Content-Type: application/json" \\ -d' @@ -49,7 +51,13 @@ export API_KEY="${apiKey}"`, { "index" : { "_index" : "books" } } {"name": "The Handmaid'"'"'s Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311} '`, - ingestDataIndex: ({ apiKey, url, indexName }) => `curl -X POST ${url}/_bulk?pretty \\ + ingestDataIndex: ({ + apiKey, + indexName, + ingestPipeline, + }) => `curl -X POST "\$\{url\}/_bulk?pretty${ + ingestPipeline ? `&pipeline=${ingestPipeline}` : '' + }" \\ -H "Authorization: ApiKey ${apiKey}" \\ -H "Content-Type: application/json" \\ -d' diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/dotnet.ts b/x-pack/plugins/serverless_search/public/application/components/languages/dotnet.ts index 1ac3641f67fe9..0d191f2b1e5b6 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/dotnet.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/dotnet.ts @@ -27,7 +27,7 @@ using Elastic.Clients.Elasticsearch.Serverless.QueryDsl; var client = new ElasticsearchClient("${cloudId}", new ApiKey("${apiKey}"));`, testConnection: `var info = await client.InfoAsync();`, - ingestData: `var doc = new Book + ingestData: ({ ingestPipeline }) => `var doc = new Book { Id = "9780553351927", Name = "Snow Crash", @@ -36,8 +36,10 @@ var client = new ElasticsearchClient("${cloudId}", new ApiKey("${apiKey}"));`, PageCount = 470 }; -var response = await client.IndexAsync(doc, "books");`, - ingestDataIndex: ({ apiKey, cloudId, indexName }) => `using System; +var response = await client.IndexAsync(doc, index: "books"${ + ingestPipeline ? `, x => x.Pipeline("${ingestPipeline}")` : '' + }));`, + ingestDataIndex: ({ apiKey, cloudId, indexName, ingestPipeline }) => `using System; using Elastic.Clients.Elasticsearch.Serverless; using Elastic.Clients.Elasticsearch.Serverless.QueryDsl; @@ -52,7 +54,9 @@ var doc = new Book PageCount = 470 }; -var response = await client.IndexAsync(doc, "${indexName}");`, +var response = await client.IndexAsync(doc, index: "${indexName}"${ + ingestPipeline ? `, x => x.Pipeline("${ingestPipeline}")` : '' + }));`, buildSearchQuery: `var response = await client.SearchAsync(s => s .Index("books") .From(0) diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/go.ts b/x-pack/plugins/serverless_search/public/application/components/languages/go.ts index d92195ad6d82e..ea328f0c9ff6c 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/go.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/go.ts @@ -46,8 +46,8 @@ func main() { }, iconType: 'go.svg', id: Languages.GO, - ingestData: `ingestResult, err := es.Bulk(). - Index("books"). + ingestData: ({ ingestPipeline }) => `ingestResult, err := es.Bulk(). + Index("books").${ingestPipeline ? `\n Pipeline("${ingestPipeline}").` : ''} Raw(strings.NewReader(\` {"index":{"_id":"9780553351927"}} {"name":"Snow Crash","author":"Neal Stephenson","release_date":"1992-06-01","page_count": 470} @@ -64,7 +64,7 @@ func main() { Do(context.Background()) fmt.Println(ingestResult, err)`, - ingestDataIndex: ({ apiKey, url, indexName }) => `import ( + ingestDataIndex: ({ apiKey, url, indexName, ingestPipeline }) => `import ( "context" "fmt" "log" @@ -83,7 +83,7 @@ func main() { log.Fatalf("Error creating the client: %s", err) } res, err := es.Bulk(). - Index("${indexName}"). + Index("${indexName}").${ingestPipeline ? `\n Pipeline("${ingestPipeline}").` : ''} Raw(strings.NewReader(\` { "index": { "_id": "1"}} {"name": "foo", "title": "bar"}\n\`)). diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/java.ts b/x-pack/plugins/serverless_search/public/application/components/languages/java.ts index c247eb978c15b..4d83be5bd4fca 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/java.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/java.ts @@ -44,7 +44,7 @@ ElasticsearchClient esClient = new ElasticsearchClient(transport);`, testConnection: `InfoResponse info = esClient.info(); logger.info(info.toString());`, - ingestData: `List books = new ArrayList<>(); + ingestData: ({ ingestPipeline }) => `List books = new ArrayList<>(); books.add(new Book("9780553351927", "Snow Crash", "Neal Stephenson", "1992-06-01", 470)); books.add(new Book("9780441017225", "Revelation Space", "Alastair Reynolds", "2000-03-15", 585)); books.add(new Book("9780451524935", "1984", "George Orwell", "1985-06-01", 328)); @@ -57,7 +57,7 @@ BulkRequest.Builder br = new BulkRequest.Builder(); for (Book book : books) { br.operations(op -> op .index(idx -> idx - .index("books") + .index("books")${ingestPipeline ? `\n .pipeline("${ingestPipeline}")` : ''} .id(product.getId()) .document(book) ) @@ -75,7 +75,7 @@ if (result.errors()) { } } }`, - ingestDataIndex: ({ apiKey, indexName, url }) => `// URL and API key + ingestDataIndex: ({ apiKey, indexName, url, ingestPipeline }) => `// URL and API key String serverUrl = "${url}"; String apiKey = "${apiKey}"; @@ -107,7 +107,9 @@ BulkRequest.Builder br = new BulkRequest.Builder(); for (Book book : books) { br.operations(op -> op .index(idx -> idx - .index("${indexName}") + .index("${indexName}")${ + ingestPipeline ? `\n .pipeline("${ingestPipeline}")` : '' + } .id(product.getId()) .document(book) ) diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/javascript.ts b/x-pack/plugins/serverless_search/public/application/components/languages/javascript.ts index 72d865f1030bc..aefd1c71f467d 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/javascript.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/javascript.ts @@ -39,7 +39,7 @@ const client = new Client({ }, iconType: 'javascript.svg', id: Languages.JAVASCRIPT, - ingestData: `// Sample books data + ingestData: ({ ingestPipeline }) => `// Sample books data const dataset = [ {"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470}, {"name": "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585}, @@ -51,7 +51,7 @@ const dataset = [ // Index with the bulk helper const result = await client.helpers.bulk({ - datasource: dataset, + datasource: dataset,${ingestPipeline ? `\n pipeline: "${ingestPipeline}",` : ''} onDocument (doc) { return { index: { _index: 'my-index-name' }}; } @@ -74,6 +74,7 @@ console.log(result); apiKey, url, indexName, + ingestPipeline, }) => `const { Client } = require('@elastic/elasticsearch-serverless'); const client = new Client({ node: '${url}', @@ -87,7 +88,7 @@ const dataset = [ // Index with the bulk helper const result = await client.helpers.bulk({ - datasource: dataset, + datasource: dataset,${ingestPipeline ? `\n pipeline: "${ingestPipeline}",` : ''} onDocument (doc) { return { index: { _index: '${indexName ?? 'index_name'}' }}; } diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/php.ts b/x-pack/plugins/serverless_search/public/application/components/languages/php.ts index 0a7dbe5758dd9..c156165683a6a 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/php.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/php.ts @@ -29,7 +29,10 @@ export const phpDefinition: LanguageDefinition = { }, iconType: 'php.svg', id: Languages.PHP, - ingestData: `$body = [ + ingestData: ({ ingestPipeline }) => `$params =[${ + ingestPipeline ? `\n 'pipeline' => '${ingestPipeline}',` : '' + } + 'body' => [ [ "index" => [ "_index" => "books" ]], [ "name" => "Snow Crash", "author" => "Neal Stephenson", "release_date" => "1992-06-01", "page_count" => 470], [ "index" => [ "_index" => "books" ]], @@ -42,22 +45,27 @@ export const phpDefinition: LanguageDefinition = { [ "name" => "Brave New World", "author" => "Aldous Huxley", "release_date" => "1932-06-01", "page_count" => 268], [ "index" => [ "_index" => "books" ]], [ "name" => "The Handmaid's Tale", "author" => "Margaret Atwood", "release_date" => "1985-06-01", "page_count" => 311] -]; +]]; -$response = $client->bulk(body: $body); +$response = $client->bulk($params); echo $response->getStatusCode(); echo (string) $response->getBody();`, - ingestDataIndex: ({ apiKey, url, indexName }) => `$client = ClientBuilder::create() + ingestDataIndex: ({ + apiKey, + url, + indexName, + ingestPipeline, + }) => `$client = ClientBuilder::create() ->setEndpoint('${url}') ->setApiKey('${apiKey}') ->build(); +$params =[${ingestPipeline ? `\n 'pipeline' => '${ingestPipeline}',` : ''} + 'body' => [ + [ 'index' => [ '_index' => '${indexName ?? INDEX_NAME_PLACEHOLDER}', '_id' => '1' ]], + [ 'name' => 'foo', 'title' => 'bar' ] +]]; -$body = [ - [ 'index' => [ '_index' => '${indexName ?? INDEX_NAME_PLACEHOLDER}', '_id' => '1' ]], - [ 'name' => 'foo', 'title' => 'bar' ] -]; - -$response = $client->bulk(body: $body); +$response = $client->bulk($params); echo $response->getStatusCode(); echo (string) $response->getBody(); `, diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/python.ts b/x-pack/plugins/serverless_search/public/application/components/languages/python.ts index dbed5f124e617..266a28b12c181 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/python.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/python.ts @@ -29,7 +29,7 @@ client = Elasticsearch( }, iconType: 'python.svg', id: Languages.PYTHON, - ingestData: `documents = [ + ingestData: ({ ingestPipeline }) => `documents = [ { "index": { "_index": "books", "_id": "9780553351927"}}, {"name": "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470}, { "index": { "_index": "books", "_id": "9780441017225"}}, @@ -44,11 +44,12 @@ client = Elasticsearch( {"name": "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311}, ] -client.bulk(operations=documents)`, +client.bulk(operations=documents${ingestPipeline ? `, pipeline="${ingestPipeline}"` : ''})`, ingestDataIndex: ({ apiKey, url, indexName, + ingestPipeline, }) => `from elasticsearch_serverless import Elasticsearch client = Elasticsearch( @@ -61,7 +62,7 @@ documents = [ {"name": "foo", "title": "bar"}, ] -client.bulk(operations=documents) +client.bulk(operations=documents${ingestPipeline ? `, pipeline="${ingestPipeline}"` : ''}) `, installClient: `python -m pip install elasticsearch-serverless diff --git a/x-pack/plugins/serverless_search/public/application/components/languages/ruby.ts b/x-pack/plugins/serverless_search/public/application/components/languages/ruby.ts index f0553b5d7ec76..19b926878024d 100644 --- a/x-pack/plugins/serverless_search/public/application/components/languages/ruby.ts +++ b/x-pack/plugins/serverless_search/public/application/components/languages/ruby.ts @@ -28,7 +28,7 @@ export const rubyDefinition: LanguageDefinition = { }, iconType: 'ruby.svg', id: Languages.RUBY, - ingestData: `documents = [ + ingestData: ({ ingestPipeline }) => `documents = [ { index: { _index: 'books', data: {name: "Snow Crash", "author": "Neal Stephenson", "release_date": "1992-06-01", "page_count": 470} } }, { index: { _index: 'books', data: {name: "Revelation Space", "author": "Alastair Reynolds", "release_date": "2000-03-15", "page_count": 585} } }, { index: { _index: 'books', data: {name: "1984", "author": "George Orwell", "release_date": "1985-06-01", "page_count": 328} } }, @@ -36,8 +36,13 @@ export const rubyDefinition: LanguageDefinition = { { index: { _index: 'books', data: {name: "Brave New World", "author": "Aldous Huxley", "release_date": "1932-06-01", "page_count": 268} } }, { index: { _index: 'books', data: {name: "The Handmaid's Tale", "author": "Margaret Atwood", "release_date": "1985-06-01", "page_count": 311} } } ] -client.bulk(body: documents)`, - ingestDataIndex: ({ apiKey, url, indexName }) => `client = ElasticsearchServerless::Client.new( +client.bulk(body: documents${ingestPipeline ? `, pipeline: "${ingestPipeline}"` : ''})`, + ingestDataIndex: ({ + apiKey, + url, + indexName, + ingestPipeline, + }) => `client = ElasticsearchServerless::Client.new( api_key: '${apiKey}', url: '${url}' ) @@ -47,7 +52,7 @@ documents = [ indexName ?? INDEX_NAME_PLACEHOLDER }', data: {name: "foo", "title": "bar"} } }, ] -client.bulk(body: documents) +client.bulk(body: documents${ingestPipeline ? `, pipeline: "${ingestPipeline}"` : ''}) `, installClient: `# Requires Ruby version 3.0 or higher diff --git a/x-pack/plugins/serverless_search/public/application/components/overview.tsx b/x-pack/plugins/serverless_search/public/application/components/overview.tsx index 318bcc0b13125..2a5beea0ef2e3 100644 --- a/x-pack/plugins/serverless_search/public/application/components/overview.tsx +++ b/x-pack/plugins/serverless_search/public/application/components/overview.tsx @@ -44,6 +44,7 @@ import { API_KEY_PLACEHOLDER, CLOUD_ID_PLACEHOLDER, ELASTICSEARCH_URL_PLACEHOLDER, + DEFAULT_INGESTION_PIPELINE, } from '../constants'; import { javaDefinition } from './languages/java'; import { languageDefinitions } from './languages/languages'; @@ -55,6 +56,7 @@ import { PipelineOverviewButton } from './pipeline_overview_button'; import { SelectClientCallouts } from './select_client_callouts'; import { PipelineManageButton } from './pipeline_manage_button'; import { OPTIONAL_LABEL } from '../../../common/i18n_string'; +import { useIngestPipelines } from '../hooks/api/use_ingest_pipelines'; export const ElasticsearchOverview = () => { const [selectedLanguage, setSelectedLanguage] = useState(javaDefinition); @@ -82,13 +84,17 @@ export const ElasticsearchOverview = () => { () => (consolePlugin?.EmbeddableConsole ? : null), [consolePlugin] ); + const [selectedPipeline, setSelectedPipeline] = React.useState(''); const codeSnippetArguments: LanguageDefinitionSnippetArguments = { url: elasticsearchURL, apiKey: clientApiKey, cloudId, + ingestPipeline: selectedPipeline, }; + const { data: pipelineData } = useIngestPipelines(); + return ( @@ -302,7 +308,8 @@ export const ElasticsearchOverview = () => { 'ingestData', codeSnippetArguments )} - consoleRequest={getConsoleRequest('ingestData')} + ingestPipelineData={pipelineData?.pipelines} + consoleRequest={getConsoleRequest('ingestData', codeSnippetArguments)} languages={languageDefinitions} selectedLanguage={selectedLanguage} setSelectedLanguage={setSelectedLanguage} @@ -312,6 +319,9 @@ export const ElasticsearchOverview = () => { consolePlugin={consolePlugin} sharePlugin={share} additionalIngestionPanel={} + selectedPipeline={selectedPipeline} + setSelectedPipeline={setSelectedPipeline} + defaultIngestPipeline={DEFAULT_INGESTION_PIPELINE} /> { + const { http } = useKibanaServices(); + return useQuery({ + queryKey: ['fetchIngestPipelines'], + queryFn: async () => + http.fetch>( + `/internal/serverless_search/ingest_pipelines/` + ), + }); +}; diff --git a/x-pack/plugins/serverless_search/server/plugin.ts b/x-pack/plugins/serverless_search/server/plugin.ts index 0601b099d8a0d..8963a3c78f917 100644 --- a/x-pack/plugins/serverless_search/server/plugin.ts +++ b/x-pack/plugins/serverless_search/server/plugin.ts @@ -29,6 +29,7 @@ import type { import { registerConnectorsRoutes } from './routes/connectors_routes'; import { registerTelemetryUsageCollector } from './collectors/connectors/telemetry'; import { registerMappingRoutes } from './routes/mapping_routes'; +import { registerIngestPipelineRoutes } from './routes/ingest_pipeline_routes'; export interface RouteDependencies { http: CoreSetup['http']; @@ -95,6 +96,7 @@ export class ServerlessSearchPlugin registerConnectorsRoutes(dependencies); registerIndicesRoutes(dependencies); registerMappingRoutes(dependencies); + registerIngestPipelineRoutes(dependencies); if (usageCollection) { registerTelemetryUsageCollector(usageCollection, this.logger); diff --git a/x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts b/x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts new file mode 100644 index 0000000000000..4a2720c8712d9 --- /dev/null +++ b/x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts @@ -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 { RouteDependencies } from '../plugin'; + +export const registerIngestPipelineRoutes = ({ router }: RouteDependencies) => { + router.get( + { + path: '/internal/serverless_search/ingest_pipelines/', + validate: {}, + }, + async (context, request, response) => { + const { client } = (await context.core).elasticsearch; + const pipelines = await client.asCurrentUser.ingest.getPipeline(); + + return response.ok({ + body: { + pipelines, + }, + headers: { 'content-type': 'application/json' }, + }); + } + ); +}; From 513b81f15d5265fe64559ddcf95afc4d6805e97a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 May 2024 04:17:59 +0100 Subject: [PATCH 26/71] skip flaky suite (#183671) --- .../e2e/artifacts/artifact_tabs_in_policy_details.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts index dedbbf607b3f0..184f045888249 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts @@ -83,7 +83,8 @@ describe( }); for (const testData of getArtifactsListTestsData()) { - describe(`${testData.title} tab`, () => { + // FLAKY: https://github.com/elastic/kibana/issues/183671 + describe.skip(`${testData.title} tab`, () => { beforeEach(() => { login(); removeExceptionsList(testData.createRequestBody.list_id); From 9fe2d62b5d3fb143acb14ac22edbccf711004687 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 May 2024 04:19:08 +0100 Subject: [PATCH 27/71] skip flaky suite (#183670) --- .../cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts index 184f045888249..4aa8085fbf571 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifact_tabs_in_policy_details.cy.ts @@ -83,6 +83,7 @@ describe( }); for (const testData of getArtifactsListTestsData()) { + // FLAKY: https://github.com/elastic/kibana/issues/183670 // FLAKY: https://github.com/elastic/kibana/issues/183671 describe.skip(`${testData.title} tab`, () => { beforeEach(() => { From 8e457bf254ed38955b0e8f7153cc0f167195ef92 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 17 May 2024 01:14:56 -0400 Subject: [PATCH 28/71] [api-docs] 2024-05-17 Daily api_docs build (#183695) Generated by https://buildkite.com/elastic/kibana-api-docs-daily/builds/709 --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- .../ai_assistant_management_selection.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/apm_data_access.mdx | 2 +- api_docs/asset_manager.mdx | 2 +- api_docs/assets_data_access.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_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.mdx | 2 +- 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/dataset_quality.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/discover_shared.mdx | 2 +- api_docs/ecs_data_quality_dashboard.mdx | 2 +- api_docs/elastic_assistant.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_annotation_listing.mdx | 2 +- 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.mdx | 2 +- 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/ingest_pipelines.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_actions_types.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_log_pattern_analysis.mdx | 2 +- api_docs/kbn_aiops_log_rate_analysis.mdx | 2 +- .../kbn_alerting_api_integration_helpers.mdx | 2 +- api_docs/kbn_alerting_state_types.mdx | 2 +- api_docs/kbn_alerting_types.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.mdx | 2 +- api_docs/kbn_analytics_collection_utils.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_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_data_view.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- .../kbn_apm_synthtrace_client.devdocs.json | 51 +++- 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_bfetch_error.mdx | 2 +- api_docs/kbn_calculate_auto.mdx | 2 +- .../kbn_calculate_width_from_char_count.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_mock.mdx | 2 +- api_docs/kbn_code_owners.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 +- ...tent_management_tabbed_table_list_view.mdx | 2 +- ...kbn_content_management_table_list_view.mdx | 2 +- ...tent_management_table_list_view_common.mdx | 2 +- ...ntent_management_table_list_view_table.mdx | 2 +- api_docs/kbn_content_management_utils.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.devdocs.json | 12 +- 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 +- .../kbn_core_plugins_contracts_browser.mdx | 2 +- .../kbn_core_plugins_contracts_server.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 +- ...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_security_browser.mdx | 2 +- .../kbn_core_security_browser_internal.mdx | 2 +- api_docs/kbn_core_security_browser_mocks.mdx | 2 +- api_docs/kbn_core_security_common.mdx | 2 +- api_docs/kbn_core_security_server.mdx | 2 +- .../kbn_core_security_server_internal.mdx | 2 +- api_docs/kbn_core_security_server_mocks.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 +- .../kbn_core_test_helpers_model_versions.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_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_core_user_profile_browser.mdx | 2 +- ...kbn_core_user_profile_browser_internal.mdx | 2 +- .../kbn_core_user_profile_browser_mocks.mdx | 2 +- api_docs/kbn_core_user_profile_common.mdx | 2 +- api_docs/kbn_core_user_profile_server.mdx | 2 +- .../kbn_core_user_profile_server_internal.mdx | 2 +- .../kbn_core_user_profile_server_mocks.mdx | 2 +- api_docs/kbn_core_user_settings_server.mdx | 2 +- .../kbn_core_user_settings_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_custom_icons.mdx | 2 +- api_docs/kbn_custom_integrations.mdx | 2 +- api_docs/kbn_cypress_config.mdx | 2 +- api_docs/kbn_data_forge.mdx | 2 +- api_docs/kbn_data_service.mdx | 2 +- api_docs/kbn_data_stream_adapter.mdx | 2 +- api_docs/kbn_data_view_utils.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_deeplinks_analytics.mdx | 2 +- api_docs/kbn_deeplinks_devtools.mdx | 2 +- api_docs/kbn_deeplinks_fleet.mdx | 2 +- api_docs/kbn_deeplinks_management.mdx | 2 +- api_docs/kbn_deeplinks_ml.mdx | 2 +- api_docs/kbn_deeplinks_observability.mdx | 2 +- api_docs/kbn_deeplinks_search.mdx | 2 +- api_docs/kbn_deeplinks_security.mdx | 2 +- api_docs/kbn_deeplinks_shared.mdx | 2 +- api_docs/kbn_default_nav_analytics.mdx | 2 +- api_docs/kbn_default_nav_devtools.mdx | 2 +- api_docs/kbn_default_nav_management.mdx | 2 +- api_docs/kbn_default_nav_ml.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_discover_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_data_quality_dashboard.mdx | 2 +- api_docs/kbn_elastic_agent_utils.mdx | 2 +- api_docs/kbn_elastic_assistant.mdx | 2 +- api_docs/kbn_elastic_assistant_common.mdx | 2 +- api_docs/kbn_es.devdocs.json | 6 +- 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.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_esql_ast.mdx | 2 +- api_docs/kbn_esql_utils.mdx | 2 +- api_docs/kbn_esql_validation_autocomplete.mdx | 2 +- api_docs/kbn_event_annotation_common.mdx | 2 +- api_docs/kbn_event_annotation_components.mdx | 2 +- api_docs/kbn_expandable_flyout.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_field_utils.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- api_docs/kbn_formatters.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- .../kbn_ftr_common_functional_ui_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_generate_console_definitions.mdx | 2 +- api_docs/kbn_generate_csv.mdx | 2 +- api_docs/kbn_guided_onboarding.mdx | 2 +- 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_index_management.mdx | 2 +- api_docs/kbn_inference_integration_flyout.mdx | 2 +- api_docs/kbn_infra_forge.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_ipynb.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_lens_embeddable_utils.mdx | 2 +- api_docs/kbn_lens_formula_docs.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_content_badge.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_management_cards_navigation.mdx | 2 +- .../kbn_management_settings_application.mdx | 2 +- ...ent_settings_components_field_category.mdx | 2 +- ...gement_settings_components_field_input.mdx | 2 +- ...nagement_settings_components_field_row.mdx | 2 +- ...bn_management_settings_components_form.mdx | 2 +- ...n_management_settings_field_definition.mdx | 2 +- .../kbn_management_settings_ids.devdocs.json | 15 ++ api_docs/kbn_management_settings_ids.mdx | 4 +- ...n_management_settings_section_registry.mdx | 2 +- api_docs/kbn_management_settings_types.mdx | 2 +- .../kbn_management_settings_utilities.mdx | 2 +- api_docs/kbn_management_storybook_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_maps_vector_tile_utils.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_anomaly_utils.mdx | 2 +- api_docs/kbn_ml_cancellable_search.mdx | 2 +- api_docs/kbn_ml_category_validator.mdx | 2 +- api_docs/kbn_ml_chi2test.mdx | 2 +- .../kbn_ml_data_frame_analytics_utils.mdx | 2 +- api_docs/kbn_ml_data_grid.mdx | 2 +- api_docs/kbn_ml_date_picker.mdx | 2 +- api_docs/kbn_ml_date_utils.mdx | 2 +- api_docs/kbn_ml_error_utils.mdx | 2 +- api_docs/kbn_ml_in_memory_table.mdx | 2 +- api_docs/kbn_ml_is_defined.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_kibana_theme.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_runtime_field_utils.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_ml_time_buckets.mdx | 2 +- api_docs/kbn_ml_trained_models_utils.mdx | 2 +- api_docs/kbn_ml_ui_actions.mdx | 2 +- api_docs/kbn_ml_url_state.mdx | 2 +- api_docs/kbn_mock_idp_utils.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_object_versioning.mdx | 2 +- api_docs/kbn_observability_alert_details.mdx | 2 +- .../kbn_observability_alerting_test_data.mdx | 2 +- ...ility_get_padded_alert_time_range_util.mdx | 2 +- api_docs/kbn_openapi_bundler.mdx | 2 +- api_docs/kbn_openapi_generator.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 +- api_docs/kbn_panel_loader.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_check.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_presentation_containers.mdx | 2 +- api_docs/kbn_presentation_publishing.mdx | 2 +- api_docs/kbn_profiling_utils.mdx | 2 +- api_docs/kbn_random_sampling.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_react_hooks.mdx | 2 +- api_docs/kbn_react_kibana_context_common.mdx | 2 +- api_docs/kbn_react_kibana_context_render.mdx | 2 +- api_docs/kbn_react_kibana_context_root.mdx | 2 +- api_docs/kbn_react_kibana_context_styled.mdx | 2 +- api_docs/kbn_react_kibana_context_theme.mdx | 2 +- api_docs/kbn_react_kibana_mount.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_reporting_csv_share_panel.mdx | 2 +- api_docs/kbn_reporting_export_types_csv.mdx | 2 +- .../kbn_reporting_export_types_csv_common.mdx | 2 +- api_docs/kbn_reporting_export_types_pdf.mdx | 2 +- .../kbn_reporting_export_types_pdf_common.mdx | 2 +- api_docs/kbn_reporting_export_types_png.mdx | 2 +- .../kbn_reporting_export_types_png_common.mdx | 2 +- api_docs/kbn_reporting_mocks_server.mdx | 2 +- api_docs/kbn_reporting_public.mdx | 2 +- api_docs/kbn_reporting_server.mdx | 2 +- api_docs/kbn_resizable_layout.mdx | 2 +- api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_router_to_openapispec.mdx | 2 +- api_docs/kbn_router_utils.mdx | 2 +- api_docs/kbn_rrule.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- api_docs/kbn_saved_objects_settings.mdx | 2 +- api_docs/kbn_search_api_panels.devdocs.json | 37 ++- api_docs/kbn_search_api_panels.mdx | 4 +- api_docs/kbn_search_connectors.devdocs.json | 255 ++++++++++++++---- api_docs/kbn_search_connectors.mdx | 4 +- api_docs/kbn_search_errors.mdx | 2 +- api_docs/kbn_search_index_documents.mdx | 2 +- api_docs/kbn_search_response_warnings.mdx | 2 +- api_docs/kbn_search_types.mdx | 2 +- api_docs/kbn_security_hardening.mdx | 2 +- api_docs/kbn_security_plugin_types_common.mdx | 2 +- api_docs/kbn_security_plugin_types_public.mdx | 2 +- api_docs/kbn_security_plugin_types_server.mdx | 2 +- api_docs/kbn_security_solution_features.mdx | 2 +- api_docs/kbn_security_solution_navigation.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 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- 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_serverless_common_settings.mdx | 2 +- .../kbn_serverless_observability_settings.mdx | 2 +- api_docs/kbn_serverless_project_switcher.mdx | 2 +- api_docs/kbn_serverless_search_settings.mdx | 2 +- api_docs/kbn_serverless_security_settings.mdx | 2 +- api_docs/kbn_serverless_storybook_config.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_solution.mdx | 2 +- .../kbn_shared_ux_button_exit_full_screen.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_chrome_navigation.mdx | 2 +- api_docs/kbn_shared_ux_error_boundary.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_tabbed_modal.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.devdocs.json | 34 +-- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_solution_nav_es.mdx | 2 +- api_docs/kbn_solution_nav_oblt.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_predicates.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_eui_helpers.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_text_based_editor.mdx | 2 +- api_docs/kbn_timerange.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_triggers_actions_ui_types.mdx | 2 +- api_docs/kbn_try_in_console.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_unified_data_table.mdx | 2 +- api_docs/kbn_unified_doc_viewer.mdx | 2 +- api_docs/kbn_unified_field_list.mdx | 2 +- api_docs/kbn_unsaved_changes_badge.mdx | 2 +- api_docs/kbn_use_tracked_promise.mdx | 2 +- 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_visualization_ui_components.mdx | 2 +- api_docs/kbn_visualization_utils.mdx | 2 +- api_docs/kbn_xstate_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kbn_zod_helpers.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/links.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/logs_data_access.mdx | 2 +- api_docs/logs_explorer.mdx | 2 +- api_docs/logs_shared.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/metrics_data_access.devdocs.json | 102 ++++++- api_docs/metrics_data_access.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/mock_idp_plugin.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/no_data_page.mdx | 2 +- api_docs/notifications.mdx | 2 +- api_docs/observability.devdocs.json | 114 ++++++++ api_docs/observability.mdx | 4 +- api_docs/observability_a_i_assistant.mdx | 2 +- api_docs/observability_a_i_assistant_app.mdx | 2 +- .../observability_ai_assistant_management.mdx | 2 +- api_docs/observability_logs_explorer.mdx | 2 +- api_docs/observability_onboarding.mdx | 2 +- api_docs/observability_shared.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/painless_lab.mdx | 2 +- api_docs/plugin_directory.mdx | 14 +- api_docs/presentation_panel.mdx | 2 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/profiling_data_access.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/search_connectors.mdx | 2 +- api_docs/search_notebooks.mdx | 2 +- api_docs/search_playground.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/security_solution_ess.mdx | 2 +- api_docs/security_solution_serverless.mdx | 2 +- api_docs/serverless.mdx | 2 +- api_docs/serverless_observability.mdx | 2 +- api_docs/serverless_search.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/slo.devdocs.json | 2 +- api_docs/slo.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/text_based_languages.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_doc_viewer.mdx | 2 +- api_docs/unified_histogram.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/uptime.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 +- 701 files changed, 1234 insertions(+), 798 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 6d677b0619e15..bd4c6daf1fec6 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: 2024-05-16 +date: 2024-05-17 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 bc6c6c7e388fa..09e4690e3fdd6 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index f04c826eef9a3..bc1c1d15d84a6 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index e73af6333db7e..3aace4647dc51 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 9903181b6fa6a..e7bd15cb4ae46 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index b2b1e56b77543..a64ba108febbb 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 6ed0b95daa8ad..3eb0fb077b621 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index b872e815d58a7..6a9910f2e96be 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/assets_data_access.mdx b/api_docs/assets_data_access.mdx index 43ad957a1dbd6..3fee86c44aa02 100644 --- a/api_docs/assets_data_access.mdx +++ b/api_docs/assets_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetsDataAccess title: "assetsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the assetsDataAccess plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetsDataAccess'] --- import assetsDataAccessObj from './assets_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 63df940e00978..c6ab529c57b83 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: 2024-05-16 +date: 2024-05-17 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 627a850f3d091..c2b9f25fab6bb 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: 2024-05-16 +date: 2024-05-17 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 5a1e7b6f469a8..63cc7252173ce 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: 2024-05-16 +date: 2024-05-17 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 9a483c551c21a..6f8b7d61134c7 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: 2024-05-16 +date: 2024-05-17 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 f4e7cb47fcbe8..fe35f776247fc 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: 2024-05-16 +date: 2024-05-17 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 6aec0b9d434c3..23e62de0022b3 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 67c7128daecdf..7feb497f580a1 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: 2024-05-16 +date: 2024-05-17 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 3d7d64c78461a..6b18520da551e 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: 2024-05-16 +date: 2024-05-17 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 f9e1df34e94a2..b21626de958e8 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: 2024-05-16 +date: 2024-05-17 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 0413b5651fbb2..e140dabf9ab3e 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: 2024-05-16 +date: 2024-05-17 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 08883611c4695..59c564b3f8047 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index 15204bfbaaef6..6116675bb8397 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index ccd3afd2d7b93..c21fd05767f82 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: 2024-05-16 +date: 2024-05-17 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 fae2434757ca3..57bc015bc7f73 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: 2024-05-16 +date: 2024-05-17 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 f583cf499cd61..9f709be16a1c4 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: 2024-05-16 +date: 2024-05-17 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 41def7fc2c39a..6be582e182c80 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: 2024-05-16 +date: 2024-05-17 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 3460a1db5878c..7e8e9eed0172e 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: 2024-05-16 +date: 2024-05-17 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 814f2e6e33409..4cb3068d7056a 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: 2024-05-16 +date: 2024-05-17 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 7816b8061baa8..f69f1c34659a9 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: 2024-05-16 +date: 2024-05-17 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 357d0c5ec4331..31b7853e199cd 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: 2024-05-16 +date: 2024-05-17 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 af308874cf2f5..e7837b9a482cb 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: 2024-05-16 +date: 2024-05-17 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 9297b18e77dba..936c8ac9463bc 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: 2024-05-16 +date: 2024-05-17 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 49f3fa11de8e3..d9f103a31e961 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: 2024-05-16 +date: 2024-05-17 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 8894797458689..3aa500f2d848e 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index 611341551695d..1fbe2ddba8870 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index a75ee0249320a..3484b1ce59e17 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 6d48bf1b4664b..d3e015c7dfb5e 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 2ad775a309d1e..2f894b503ca2e 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index b1e08d753d260..c5d5c207bb7e5 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: 2024-05-16 +date: 2024-05-17 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 a51c6e121bc7c..a573d3acffed7 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: 2024-05-16 +date: 2024-05-17 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 941e41441a33c..39a7df34b14a9 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index baf360a03c07e..9bb1f8432c587 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 53b45384bf92f..6f35f8bcd50ff 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index c44e9b4170178..d19fec0143b78 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 66e0d04cfa183..31852010bdf93 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: 2024-05-16 +date: 2024-05-17 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 04015ef4a968d..8b5df61a78310 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: 2024-05-16 +date: 2024-05-17 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 5f50b6d2a374f..f873b8b7ea452 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: 2024-05-16 +date: 2024-05-17 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 a56395e3551fd..12f63298f1e8a 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: 2024-05-16 +date: 2024-05-17 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 350d16f962335..38f793e6e3cf3 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: 2024-05-16 +date: 2024-05-17 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 1dc957e6d2d6a..4b58558077372 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index b60e428341af6..c4e36759ebcb0 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index b63dba1e6cf2e..aa4cac38c3367 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: 2024-05-16 +date: 2024-05-17 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 220f4967a081d..d5c33b0355bfd 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: 2024-05-16 +date: 2024-05-17 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 73045b4468e1e..92fb61102ed82 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: 2024-05-16 +date: 2024-05-17 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 61a1d71f765d6..64c30900fea0e 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: 2024-05-16 +date: 2024-05-17 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 523a114e1e2cb..97ce394d13297 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: 2024-05-16 +date: 2024-05-17 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 6afb86526b829..f999ef8c264a3 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: 2024-05-16 +date: 2024-05-17 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 6ffb985dd5a99..3e85f5c33d4f7 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: 2024-05-16 +date: 2024-05-17 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 980a58c757f0b..f44bd480a43a8 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: 2024-05-16 +date: 2024-05-17 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 829a73965f30f..9fde2f242ea24 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: 2024-05-16 +date: 2024-05-17 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 73fd6d03e5ecb..9b2d015678da7 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: 2024-05-16 +date: 2024-05-17 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 94526867e6148..ee9bed795bece 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: 2024-05-16 +date: 2024-05-17 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 fa5f044da787a..4ff15f37e6ce1 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: 2024-05-16 +date: 2024-05-17 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 d9e44a4da3710..ba12b8c7a6dbe 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: 2024-05-16 +date: 2024-05-17 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 5939e46f6936f..da8162d419510 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: 2024-05-16 +date: 2024-05-17 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 f5b7aa5cd5845..b365a68b9e3ba 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: 2024-05-16 +date: 2024-05-17 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 16e98c2ae7717..24d787922f307 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: 2024-05-16 +date: 2024-05-17 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 6923aa2754f6f..af9e00aea4fa7 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: 2024-05-16 +date: 2024-05-17 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 4bf756dd1d889..e28bd87d9bf89 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: 2024-05-16 +date: 2024-05-17 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 f5de1a1f85ada..ae49a90838b4f 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: 2024-05-16 +date: 2024-05-17 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 182acfe1b6390..bacb2f50b06c8 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: 2024-05-16 +date: 2024-05-17 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 a6e62076dc23d..ae74197ed4f54 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: 2024-05-16 +date: 2024-05-17 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 b2f676f7a579b..751cbae29e833 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: 2024-05-16 +date: 2024-05-17 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 671520298f10b..5c7dc5d236ad4 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 0be56866276c1..ed6a32669c69a 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index c4c76a646a278..d926b11b5e58b 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: 2024-05-16 +date: 2024-05-17 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 226c0c9d79faa..3b22ff101a23f 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: 2024-05-16 +date: 2024-05-17 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 ac1ed0d7ef227..b828ecd95544b 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: 2024-05-16 +date: 2024-05-17 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 be5ab07e36a86..b04192c743b00 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: 2024-05-16 +date: 2024-05-17 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 5deea1da49242..61b4b7618a6c7 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index 0c0d2ca29758b..a5715b95db34c 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 91f4cc6bbec82..c5cd5225f7637 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: 2024-05-16 +date: 2024-05-17 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 e281d483c7394..ddd13e1a073ba 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: 2024-05-16 +date: 2024-05-17 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 484093a43dd35..30f7ce873c9ae 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index 3dadb854124c5..03d955a3b37de 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index b5e1c5457fa94..46642c9d949dc 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index 1f94eddb6a674..6f0457631edbe 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index 0a2b8d0ffda8b..24fc602af53b6 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index c80416028ed38..2670f7c9dd54a 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 1f9abea4f1533..0cc112d0e9d9d 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index 5451c5ddcd328..f2d2d22d9d8e7 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 22579c3f822d2..7732611408b01 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: 2024-05-16 +date: 2024-05-17 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 41281828da834..8d6ebe5c7762f 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: 2024-05-16 +date: 2024-05-17 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 167d70babc516..9fcffd5e7be8b 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 8af754a8a8134..5a88ed83b0aba 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 4bd2e6f8f9de8..81601781c2317 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.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 5e94d8b84521b..0d56eae4685f7 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: 2024-05-16 +date: 2024-05-17 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 b7f15489d259b..f786c68a5ff22 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: 2024-05-16 +date: 2024-05-17 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 778d494d098d6..9fc226fb8f7cc 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: 2024-05-16 +date: 2024-05-17 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 e74508d4deb8d..f32cc8f9bd387 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 88eefe81fb0e1..e2ea3e17b41fd 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: 2024-05-16 +date: 2024-05-17 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_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index 1d825bbf4cb1c..f8eb7ad7e13ac 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 0d07b1a2e3948..a36fffb38f5ac 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: 2024-05-16 +date: 2024-05-17 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 8b1bb3e1fbd0f..935496013d4fc 100644 --- a/api_docs/kbn_apm_synthtrace_client.devdocs.json +++ b/api_docs/kbn_apm_synthtrace_client.devdocs.json @@ -2632,7 +2632,9 @@ " | ", "PodMetricsDocument", " | ", - "ContainerMetricsDocument" + "DockerContainerMetricsDocument", + " | ", + "K8sContainerMetricsDocument" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts", "deprecated": false, @@ -2926,14 +2928,43 @@ }, { "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.infra.container", + "id": "def-common.infra.dockerContainer", + "type": "Function", + "tags": [], + "label": "dockerContainer", + "description": [], + "signature": [ + "(id: string) => ", + "DockerContainer" + ], + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.infra.dockerContainer.$1", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/docker_container.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/apm-synthtrace-client", + "id": "def-common.infra.k8sContainer", "type": "Function", "tags": [], - "label": "container", + "label": "k8sContainer", "description": [], "signature": [ "(id: string, uid: string, nodeName: string) => ", - "Container" + "K8sContainer" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/index.ts", "deprecated": false, @@ -2942,34 +2973,34 @@ "children": [ { "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.infra.container.$1", + "id": "def-common.infra.k8sContainer.$1", "type": "string", "tags": [], "label": "id", "description": [], - "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts", + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.infra.container.$2", + "id": "def-common.infra.k8sContainer.$2", "type": "string", "tags": [], "label": "uid", "description": [], - "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts", + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/apm-synthtrace-client", - "id": "def-common.infra.container.$3", + "id": "def-common.infra.k8sContainer.$3", "type": "string", "tags": [], "label": "nodeName", "description": [], - "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/container.ts", + "path": "packages/kbn-apm-synthtrace-client/src/lib/infra/k8s_container.ts", "deprecated": false, "trackAdoption": false } diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index f7b8e921503ca..924b71a3c3b58 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 191 | 0 | 191 | 28 | +| 193 | 0 | 193 | 30 | ## Common diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 26a1b65232fb1..34610cc4c7edc 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: 2024-05-16 +date: 2024-05-17 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 313225279f097..50e6cc3ef7c2f 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index dd189e1f404ab..5cd5ec708a014 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 4f34e2d02b964..7afaf74f92c67 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index 78998c799a362..7851e09d50cd7 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 90d5c01339d3c..83778f073195a 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: 2024-05-16 +date: 2024-05-17 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 27c57d4fcc10e..87e310c222a71 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: 2024-05-16 +date: 2024-05-17 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 50714ad160bd6..e7d049233b283 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: 2024-05-16 +date: 2024-05-17 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 c51b6f749cd2c..2c0ebb2d261c8 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: 2024-05-16 +date: 2024-05-17 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 86d11ebad8aad..52da0031d1edd 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: 2024-05-16 +date: 2024-05-17 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 6dc2a213ae99b..f79e1e4960232 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: 2024-05-16 +date: 2024-05-17 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 b57dbea34a842..d1ca6d47206ab 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: 2024-05-16 +date: 2024-05-17 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 e582564a36996..832c857a3c70a 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: 2024-05-16 +date: 2024-05-17 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 4297a71a9ec2e..e8c7440314002 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 44fcdb779f497..23f3964423438 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index ac9e07ebee19e..864375cf36638 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 6148173599693..075b3a5ace7a1 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: 2024-05-16 +date: 2024-05-17 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 dc387b234aea5..53a6425bbae11 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: 2024-05-16 +date: 2024-05-17 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 fe91e549c307a..ec1939de9dd08 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: 2024-05-16 +date: 2024-05-17 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 3dce17613c957..f52378ce30825 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: 2024-05-16 +date: 2024-05-17 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 93755213ba306..78a5af84bc23d 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: 2024-05-16 +date: 2024-05-17 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_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index 4402b754c1272..eb5ad8fe281c2 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index d9d45d6991243..39f5af2044f3d 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index e9857b3581be8..5645e2aed47af 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 8217caf36ae13..102af47f1424f 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index c7b8b882c2215..debaedfd82209 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index f0fe7b8c52948..e20b8d790ce59 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: 2024-05-16 +date: 2024-05-17 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 e71219c579a22..b0ce2b675bdc7 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: 2024-05-16 +date: 2024-05-17 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 eadf7948210a1..58eee0729b485 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: 2024-05-16 +date: 2024-05-17 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 bf2762d21b99e..287e16f66f98c 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: 2024-05-16 +date: 2024-05-17 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 75b6528cb9b2f..1d41d6166b9c9 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: 2024-05-16 +date: 2024-05-17 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 690edc92ea703..e2fb6d368d8a9 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: 2024-05-16 +date: 2024-05-17 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 a689e6fb469dc..2d973e83c6272 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: 2024-05-16 +date: 2024-05-17 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 e9ed2cae9ffca..a12850d472dfa 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: 2024-05-16 +date: 2024-05-17 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 78384126a9f3c..fae52641dd835 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: 2024-05-16 +date: 2024-05-17 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 0071968858c41..145c7dc0bfadd 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: 2024-05-16 +date: 2024-05-17 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 b888233b0643d..2801523677cc4 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: 2024-05-16 +date: 2024-05-17 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 92697fa71bb4c..bc570e7bf164e 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: 2024-05-16 +date: 2024-05-17 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 d705949c429a4..9b3cbf61aa6ca 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: 2024-05-16 +date: 2024-05-17 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 f549850c9770a..7627419367883 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: 2024-05-16 +date: 2024-05-17 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 cd6f3e99ffd46..f08d68804083b 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: 2024-05-16 +date: 2024-05-17 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 a6508dd639ae3..57ce3a2178cb1 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: 2024-05-16 +date: 2024-05-17 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 9e6d1e512012a..d1cf83c623e6e 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: 2024-05-16 +date: 2024-05-17 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 3725941fd9159..7a41e807c96ab 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: 2024-05-16 +date: 2024-05-17 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 069c27e6956ad..a62930001d10f 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: 2024-05-16 +date: 2024-05-17 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 69557c67d6398..952dc0d6c883d 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: 2024-05-16 +date: 2024-05-17 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 1b05c031a3c2a..54891af50baa5 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: 2024-05-16 +date: 2024-05-17 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 2938229af5cf6..6953f7d7d0f4f 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: 2024-05-16 +date: 2024-05-17 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 55af2a2b04052..b32e5193792ab 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: 2024-05-16 +date: 2024-05-17 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 20a905c16d1f7..890949aff2242 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: 2024-05-16 +date: 2024-05-17 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 7c5fed8bab505..33d93db3c8073 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: 2024-05-16 +date: 2024-05-17 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 daef39e0961f0..53209feb48ce2 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: 2024-05-16 +date: 2024-05-17 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 c5118b4045ccb..165ad578c62fc 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: 2024-05-16 +date: 2024-05-17 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 16075136e3749..32b06c26014a2 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: 2024-05-16 +date: 2024-05-17 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 f1e01b6677e3e..f4d974f9de9c6 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: 2024-05-16 +date: 2024-05-17 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 af6e37d34a5c7..be2bf300e3925 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: 2024-05-16 +date: 2024-05-17 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 2f77ee18b1d37..aa172000d1049 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: 2024-05-16 +date: 2024-05-17 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 0868460e0e143..aa5f0790d8f5f 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: 2024-05-16 +date: 2024-05-17 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 510e1180c8b09..eb4c341edb4ff 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: 2024-05-16 +date: 2024-05-17 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 c378305c3f22d..f1090608dd2ae 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: 2024-05-16 +date: 2024-05-17 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 a0ca24961ac62..baa21e365d46b 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: 2024-05-16 +date: 2024-05-17 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 781ef5417f39f..e24d0ef503027 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: 2024-05-16 +date: 2024-05-17 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 997c63f4a5ca4..2154818b01d8a 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: 2024-05-16 +date: 2024-05-17 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 cdd061bfc78b2..dad4f89e0cccd 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: 2024-05-16 +date: 2024-05-17 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 7499cc93a3cc8..2cd5268a964ef 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: 2024-05-16 +date: 2024-05-17 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 745a9e38124e1..b29ce0544b5e3 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: 2024-05-16 +date: 2024-05-17 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 6d91445f12e7c..86c9175ef6d95 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: 2024-05-16 +date: 2024-05-17 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 ccecbcb9385b0..3613ecccc8385 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: 2024-05-16 +date: 2024-05-17 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 8d7fb99524dc8..c40f3fc9c7b2d 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: 2024-05-16 +date: 2024-05-17 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 f08650bad383a..7dd24162f9d2e 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: 2024-05-16 +date: 2024-05-17 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 31d7dca79e9c5..ebc3b396b431f 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: 2024-05-16 +date: 2024-05-17 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 3bb354475ff7c..1caf9502145c0 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: 2024-05-16 +date: 2024-05-17 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 0906d107519e0..e164374cc04cf 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: 2024-05-16 +date: 2024-05-17 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 c55c038bc5a74..eec2b260e2a6e 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: 2024-05-16 +date: 2024-05-17 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 84bcfe562c0d8..c3bda240aeb4e 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: 2024-05-16 +date: 2024-05-17 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 03350ba3d06a4..fe4fab1c4dabb 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: 2024-05-16 +date: 2024-05-17 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 8950e76ac4739..dd7325027af17 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: 2024-05-16 +date: 2024-05-17 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 32aaeea7ed062..197e043669fa3 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: 2024-05-16 +date: 2024-05-17 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 508e18c979d2b..ec0e3fddb3eaa 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: 2024-05-16 +date: 2024-05-17 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 bd6a6c7b30ce1..d1abda6c5e842 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: 2024-05-16 +date: 2024-05-17 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 824ceb81a22d3..466c3f1a1756a 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: 2024-05-16 +date: 2024-05-17 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 fe462fd32358d..22b77ef36f28b 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: 2024-05-16 +date: 2024-05-17 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 bd3e38f873af2..676cacaade35c 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: 2024-05-16 +date: 2024-05-17 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 71a814a919c61..9ea657253981e 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: 2024-05-16 +date: 2024-05-17 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 74d54f95b306f..c51d6a3cf4edf 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: 2024-05-16 +date: 2024-05-17 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 d4e471d6c15c1..9865b20124ab3 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: 2024-05-16 +date: 2024-05-17 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 13fc9e5b743ad..3be1dda990d74 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: 2024-05-16 +date: 2024-05-17 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 9cb9cc0201c09..4844bcfeefb43 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: 2024-05-16 +date: 2024-05-17 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 08190d1483573..f8aaf5607e0a8 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: 2024-05-16 +date: 2024-05-17 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 b2e5c49847b70..5d6cee9bc751a 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: 2024-05-16 +date: 2024-05-17 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 143757c9a5c52..1d09c57a356cb 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: 2024-05-16 +date: 2024-05-17 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 e800eb4157bc3..186c39943cc25 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: 2024-05-16 +date: 2024-05-17 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 c663c3c117c52..432112834ff9b 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: 2024-05-16 +date: 2024-05-17 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 d748890aaa800..0e75476165972 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: 2024-05-16 +date: 2024-05-17 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 a6931d53482ab..48eb2177866fa 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: 2024-05-16 +date: 2024-05-17 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.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index 35e4843e88499..67868d64c110c 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -3632,11 +3632,11 @@ }, { "plugin": "security", - "path": "x-pack/plugins/security/server/routes/indices/get_fields.ts" + "path": "x-pack/plugins/security/server/routes/feature_check/feature_check.ts" }, { "plugin": "security", - "path": "x-pack/plugins/security/server/routes/role_mapping/feature_check.ts" + "path": "x-pack/plugins/security/server/routes/indices/get_fields.ts" }, { "plugin": "security", @@ -4574,6 +4574,10 @@ "plugin": "serverlessSearch", "path": "x-pack/plugins/serverless_search/server/routes/mapping_routes.ts" }, + { + "plugin": "serverlessSearch", + "path": "x-pack/plugins/serverless_search/server/routes/ingest_pipeline_routes.ts" + }, { "plugin": "snapshotRestore", "path": "x-pack/plugins/snapshot_restore/server/routes/api/app.ts" @@ -5824,11 +5828,11 @@ }, { "plugin": "security", - "path": "x-pack/plugins/security/server/routes/indices/get_fields.test.ts" + "path": "x-pack/plugins/security/server/routes/feature_check/feature_check.test.ts" }, { "plugin": "security", - "path": "x-pack/plugins/security/server/routes/role_mapping/feature_check.test.ts" + "path": "x-pack/plugins/security/server/routes/indices/get_fields.test.ts" }, { "plugin": "security", diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 2f0741b73882e..4348a86af7122 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: 2024-05-16 +date: 2024-05-17 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 74d82823865ac..a47a274e52fd3 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: 2024-05-16 +date: 2024-05-17 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 71d2d9056a41a..afd0796644c50 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: 2024-05-16 +date: 2024-05-17 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 01a0c79507592..a9ff67fdf2bce 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: 2024-05-16 +date: 2024-05-17 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 7d0404dc61a8e..63157c9ff0bc8 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: 2024-05-16 +date: 2024-05-17 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 bb2353baf29cb..9754f2c9aa01e 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: 2024-05-16 +date: 2024-05-17 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 c8b381e62b708..56aca483c6266 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: 2024-05-16 +date: 2024-05-17 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 e7e2f8282fa68..f6e17011b4cdf 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: 2024-05-16 +date: 2024-05-17 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 cbac2e832e608..cd0ae16dbba28 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: 2024-05-16 +date: 2024-05-17 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 c45db8af105ae..6b9b52bfebf3b 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: 2024-05-16 +date: 2024-05-17 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 dbf54f2de2589..047cf3fc3c66e 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: 2024-05-16 +date: 2024-05-17 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 6b24171594fd6..3b26e82d44729 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: 2024-05-16 +date: 2024-05-17 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 a6f3e1349b4e6..b04bc6a4a798d 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: 2024-05-16 +date: 2024-05-17 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 d28c80ddfbd2e..4f0893326e926 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: 2024-05-16 +date: 2024-05-17 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 0598d9c2b8db2..1904590978be8 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: 2024-05-16 +date: 2024-05-17 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 754d285784bf9..c72c3bf34c9ca 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: 2024-05-16 +date: 2024-05-17 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 590893a90cb57..1861471055c85 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: 2024-05-16 +date: 2024-05-17 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 c8a2b600eaa04..d6a816663a6fc 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: 2024-05-16 +date: 2024-05-17 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 6b249c791d4ec..f51c8fff6d086 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: 2024-05-16 +date: 2024-05-17 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 2989480912a13..07e970fc6205d 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: 2024-05-16 +date: 2024-05-17 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 e836d03adce60..d9d0308e46ac9 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: 2024-05-16 +date: 2024-05-17 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 65344413b0abb..36cd196ca79d1 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: 2024-05-16 +date: 2024-05-17 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 c8bf769ee1c0a..1cb761eec2aad 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: 2024-05-16 +date: 2024-05-17 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 88cd5aad5e94b..f998c39a8aa50 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: 2024-05-16 +date: 2024-05-17 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 0ba5087c39f72..97e4a3d7b4451 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: 2024-05-16 +date: 2024-05-17 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 3269c9a08ec78..fe689c4044816 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: 2024-05-16 +date: 2024-05-17 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 ba40766b91324..c96722cb19796 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: 2024-05-16 +date: 2024-05-17 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 efcc8448e2f08..cf6b57c5a728d 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: 2024-05-16 +date: 2024-05-17 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 163ccd3de0a5c..bf53167682f82 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: 2024-05-16 +date: 2024-05-17 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 724f3304476e3..4e86f9e5dd2d5 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: 2024-05-16 +date: 2024-05-17 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 2a5904ad34273..36da3408c86d0 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: 2024-05-16 +date: 2024-05-17 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 f91de95813ed9..b312b6e0b56ed 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: 2024-05-16 +date: 2024-05-17 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 32205b46dd043..87b524dafa5f2 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: 2024-05-16 +date: 2024-05-17 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 792172a4c18e8..f4767a032fe97 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: 2024-05-16 +date: 2024-05-17 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 8c14d60c0fbc6..01140f554987c 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: 2024-05-16 +date: 2024-05-17 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 0b8b77c7af6a0..5518ab165afb8 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: 2024-05-16 +date: 2024-05-17 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 bf6c4692d7885..cb728794046be 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: 2024-05-16 +date: 2024-05-17 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_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index ec79b1c0d06ec..c5c5e1dc81d37 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index 12d9a451b5251..9b7f1ce9a3408 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 3dbccd7d57226..4d7a5ad49c4f9 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: 2024-05-16 +date: 2024-05-17 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 983a333cdfd89..04dcec58add15 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: 2024-05-16 +date: 2024-05-17 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 28651dda40c51..d3b7c99aa427a 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: 2024-05-16 +date: 2024-05-17 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 942f49342587b..5a065851508cc 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: 2024-05-16 +date: 2024-05-17 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 614849ea3a7c3..7148a5d0e7aa7 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: 2024-05-16 +date: 2024-05-17 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 1f0294bedf94e..f3438dcafabec 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: 2024-05-16 +date: 2024-05-17 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 7bae2adb00007..c5018fd07c5a3 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: 2024-05-16 +date: 2024-05-17 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 beee3376da0a7..0b5e4f399f08d 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: 2024-05-16 +date: 2024-05-17 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 173e104c2bada..ee3bbc755c0ba 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: 2024-05-16 +date: 2024-05-17 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 7501f41dac5f6..de15923802341 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: 2024-05-16 +date: 2024-05-17 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_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index d89c3daddb95c..4346109d0559b 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: 2024-05-16 +date: 2024-05-17 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 8d4b1bada6a17..e365514b2bab8 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: 2024-05-16 +date: 2024-05-17 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 ad76fb69c6b3e..93c5f03ec53b7 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: 2024-05-16 +date: 2024-05-17 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 a41ef025bc393..e53010fbd4dca 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: 2024-05-16 +date: 2024-05-17 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 eea26e1d6f2a8..786c6f12754f0 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: 2024-05-16 +date: 2024-05-17 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 ee912df1d5ade..c32e1807716e4 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: 2024-05-16 +date: 2024-05-17 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 d675cdeb9ee42..82c0f60dccfd2 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: 2024-05-16 +date: 2024-05-17 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 3e50096fc8a58..85edc07df625d 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: 2024-05-16 +date: 2024-05-17 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 949b094522ef1..446f50d59ce73 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: 2024-05-16 +date: 2024-05-17 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 2177b1b8373e5..c2b8752a9447e 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: 2024-05-16 +date: 2024-05-17 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 0a061496ea2dc..56bb789213994 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: 2024-05-16 +date: 2024-05-17 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 9f977ef8a647e..69527cbfeb374 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: 2024-05-16 +date: 2024-05-17 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 bb4d385dd23d1..75cefa3e46691 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: 2024-05-16 +date: 2024-05-17 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 3c4ca5fb11135..434d2739c0042 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: 2024-05-16 +date: 2024-05-17 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 5985c2bb53d43..05b59b51d235c 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: 2024-05-16 +date: 2024-05-17 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_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 4ae30030a7bcc..df94f80adfa8d 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index 464dbeeb78c55..7e2098d979b22 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index 1bfe8202f819d..1b7ba91fc4441 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index 550c7ee2d174c..880bde21b83c8 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index a47d088d95071..6b41a3fb8f7a5 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index 976685c74fe2f..7b70ea6f08071 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index 8f89a2db2c49d..eb16fe4be2a99 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 213ad98bd2e3c..bdc9fbd11e79d 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: 2024-05-16 +date: 2024-05-17 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 37ff44574b2dd..df8dda07c1269 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: 2024-05-16 +date: 2024-05-17 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 91f9295cbc5be..469c0e0159e26 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: 2024-05-16 +date: 2024-05-17 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 e54304728bb19..30b1260c0babe 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: 2024-05-16 +date: 2024-05-17 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 7c5cab677b63d..5262b060c6dfa 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: 2024-05-16 +date: 2024-05-17 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 2f4139687a853..6d2fea0e23589 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: 2024-05-16 +date: 2024-05-17 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 ebac7a0d19a17..6ce6b03042ffe 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: 2024-05-16 +date: 2024-05-17 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 3cb0613ddcc29..1f09345bf8401 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: 2024-05-16 +date: 2024-05-17 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_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index c621428bc5c16..c521630f019be 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.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 41717d02ebda0..05065243419f9 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: 2024-05-16 +date: 2024-05-17 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 5dbd0fe47d419..ae3b7e33a1fff 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: 2024-05-16 +date: 2024-05-17 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 69a121f9d8727..d6ad27b0af131 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: 2024-05-16 +date: 2024-05-17 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_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index bb7e1cb54c324..0187203d4b3e7 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: 2024-05-16 +date: 2024-05-17 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 47c96e90f1a56..282060792ed60 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: 2024-05-16 +date: 2024-05-17 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 916abc90e50aa..224063b850e52 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: 2024-05-16 +date: 2024-05-17 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 babdcecccca13..6b1cfbceb9a10 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: 2024-05-16 +date: 2024-05-17 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 9a91bc8af2832..0afaa87646663 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: 2024-05-16 +date: 2024-05-17 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 33ac9cd874775..87fa68e2cb3bf 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: 2024-05-16 +date: 2024-05-17 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 14c86f96ad64e..794d8871b6f0a 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: 2024-05-16 +date: 2024-05-17 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 509bf5ffbd485..d84d6557acc5d 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: 2024-05-16 +date: 2024-05-17 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 c64a7e09fb80d..8dfa3c5db3148 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: 2024-05-16 +date: 2024-05-17 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 26122235e86af..7b9a58a3db57d 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: 2024-05-16 +date: 2024-05-17 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 9d09b1b4c8d11..452f67b1e8759 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: 2024-05-16 +date: 2024-05-17 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_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index 3a423d1392e2e..b9f3503096a8b 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index 2cacf48a952b6..329f6a2930266 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index efcab025912a0..63d96666211dc 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index 704eb2eca7cad..6062054a802a0 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index 1051ea1056ab4..d2429e6d7f155 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 06aef9bfae569..981663e1a108d 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 35a8a1dad00ec..2f2e2e4613b71 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 41c5300faf2f3..7c345921fa97f 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index ee8688883f053..cac1a8d6199fa 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 5949bd5960c1d..4bdd403edf949 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: 2024-05-16 +date: 2024-05-17 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 a4ecdcad0cc3d..3beb47205a28f 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index f7fb94ce846fa..c324f27bff2f9 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index 20780b9f3e6eb..0b0e8f68e8d65 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index cfec3b57d7313..e7333d9b4fc7a 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index c907243a61786..ff0526fa8d49b 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index b82222aaa03a6..038fbc2c29c30 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index 8b5df9f42372e..3a65054db04e9 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index 50cfefb4d0524..21943540887ec 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 7f6779a772c18..211ebb9d30c6d 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 13c43496b9e5d..6cd32e3a8216a 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index b600b5be8ad2e..0ebf82aed75c3 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index 0b4123bbc1a7d..6ecd0720cd545 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 8e16d449af436..338146ed02a70 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index 55f8ba8822713..b7a7df7ad0036 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index e91c7b8196936..8b9c5b52fc088 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index fcbb77e852a40..caca52bcbef5c 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index af8be0fa8f5a1..372ff59dc5a75 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index c47b419b735be..f24762384ecea 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 4f7f43dd2a8da..df42da34ee3e1 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index 75d4725ef357b..de048835ad503 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index 363b55c0d16a2..b41a1ec578d6f 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index a3fdf8e052d2f..9b2c0e7578cb9 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 9e625d94b1890..3ba178a55e200 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: 2024-05-16 +date: 2024-05-17 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 e71eef2a1bc8c..81fec4bb7d11c 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: 2024-05-16 +date: 2024-05-17 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 d3b9373be0271..bc391889d45af 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: 2024-05-16 +date: 2024-05-17 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 db6b87a2dd9bc..15cf096506cd4 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index baef7cbcccd26..46bafcf5db541 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index bf85dc5df4955..5a3baf451f37b 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: 2024-05-16 +date: 2024-05-17 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 415b2a02b41b8..c8f002965c840 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: 2024-05-16 +date: 2024-05-17 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 402bfbb3f90c4..1f1140a615318 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: 2024-05-16 +date: 2024-05-17 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 dab4c21a23621..805b0e68d7a19 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index dbf8449d62373..83fd390d77147 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: 2024-05-16 +date: 2024-05-17 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_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 06898632b335c..e8920b963d44e 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index 502e690f46bb0..0660e62550b6a 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index 285fd33c75287..c9b782fcb5f77 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_es.devdocs.json b/api_docs/kbn_es.devdocs.json index 434582f0c7a68..64d416e95d6c5 100644 --- a/api_docs/kbn_es.devdocs.json +++ b/api_docs/kbn_es.devdocs.json @@ -81,7 +81,7 @@ "signature": [ "(options: ", "InstallSourceOptions", - ") => Promise<{ installPath: string; }>" + ") => Promise<{ installPath: string; disableEsTmpDir: boolean; }>" ], "path": "packages/kbn-es/src/cluster.ts", "deprecated": false, @@ -153,7 +153,7 @@ "signature": [ "(options: ", "InstallSnapshotOptions", - ") => Promise<{ installPath: string; }>" + ") => Promise<{ installPath: string; disableEsTmpDir: boolean; }>" ], "path": "packages/kbn-es/src/cluster.ts", "deprecated": false, @@ -189,7 +189,7 @@ "signature": [ "(archivePath: string, options?: ", "InstallArchiveOptions", - " | undefined) => Promise<{ installPath: string; }>" + " | undefined) => Promise<{ installPath: string; disableEsTmpDir: boolean; }>" ], "path": "packages/kbn-es/src/cluster.ts", "deprecated": false, diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index bbe24a2dccc9b..3a8a97fb48c7e 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: 2024-05-16 +date: 2024-05-17 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 9b179f7ab7e87..1117b475060d2 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: 2024-05-16 +date: 2024-05-17 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 560f2b4d0efe0..b8dd72a7dcd94 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index a87e0d01ae416..31f59062ca8e3 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: 2024-05-16 +date: 2024-05-17 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 f2560bdbe8d85..c9903f08be08d 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: 2024-05-16 +date: 2024-05-17 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 c6c6c0611a875..f0a2568b31c24 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index 0d937df6592c8..3a73aeeff3b42 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 814e18f14cf5e..0e707e004376d 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index 91e3ead1350fd..6caf29b4a5d35 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 7d831c380ee2c..c29fbb021af9c 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index ddcba759bd1bb..ecf09252a4b36 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 3d971abf8407c..99a0041525744 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 04ad5a32c856b..9550f3795d76a 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index 633fb83ee7041..6d974f2ca9e35 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index ae3ee63ee0574..e10d72e994550 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: 2024-05-16 +date: 2024-05-17 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_formatters.mdx b/api_docs/kbn_formatters.mdx index 8a48773d6a634..3b04a25258387 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 01e0892d148d9..af92263e8cfed 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: 2024-05-16 +date: 2024-05-17 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_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index e8528064fd964..61075c6eb6579 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 2fcf761909de9..1ff636eeb0a21 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index 6cc4a893d35c9..50d686491379d 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index b7e80fcd2dd84..653ee460a8fe5 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 15657f39b8393..6c33c24016be9 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 1a246c914a016..d19dc9e916402 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: 2024-05-16 +date: 2024-05-17 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 24b6ad03908c1..1b4cff78a60c8 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: 2024-05-16 +date: 2024-05-17 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 06d2ac4f3792a..db3623534368c 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: 2024-05-16 +date: 2024-05-17 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 09ac4afcf3651..a37230987a541 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: 2024-05-16 +date: 2024-05-17 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 9e8a77a0bde68..8b34f16ff4a37 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: 2024-05-16 +date: 2024-05-17 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 b80c8b49f39a8..b814e45a8d7ae 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: 2024-05-16 +date: 2024-05-17 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 b2e6437117528..4425c183688ab 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: 2024-05-16 +date: 2024-05-17 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 fc4e4023935bf..0da7050bb2168 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management.mdx b/api_docs/kbn_index_management.mdx index 522045b7c9681..01225b0abd28c 100644 --- a/api_docs/kbn_index_management.mdx +++ b/api_docs/kbn_index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management title: "@kbn/index-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management'] --- import kbnIndexManagementObj from './kbn_index_management.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index a03b8fb0dfb9e..4e758ffe534ee 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 5eb8b76dfc8db..af996b7c3b0a7 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index c90abbce90fa1..231b37fa243d3 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: 2024-05-16 +date: 2024-05-17 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 9cf87176d5f1b..251f6f1c286cc 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index b203fc2516fdb..edfc6c4d6a4f8 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index fe5622055d71b..19f988496b073 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: 2024-05-16 +date: 2024-05-17 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 aa9f296afcfb2..b7962b651cba6 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: 2024-05-16 +date: 2024-05-17 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 fe5c456b61fc0..4330063fe00c8 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: 2024-05-16 +date: 2024-05-17 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 1f3a9b93b94f5..61ec8ae5ce8b1 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: 2024-05-16 +date: 2024-05-17 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 1fcb554a8919b..25b833684cbf1 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index d041f048782be..2d2f057707b7c 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index 6e8990daf040c..6bb8189984439 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index af1f09d93641b..edc37ca458a2e 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: 2024-05-16 +date: 2024-05-17 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 f85178aa2f7c0..58e1d323c3109 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index 45c75bd11ea9c..240057a534fbb 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 49419565ee1da..9f84eff04585e 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index e56190abe6db0..e1a85b4a34cf6 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index 6e2a342d7864f..78ddec1b326fb 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index 1f565f99ba23d..a66e7c7f87ece 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index 8aadf9f1d589c..b16d9b73e7713 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index fefcefacd32c6..0a057879d7f2d 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index fd44b889d251a..ba8762a3fc108 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index 961e7b7e26409..f094c882827df 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.devdocs.json b/api_docs/kbn_management_settings_ids.devdocs.json index 4495e0bc68ae0..b4b24972b6989 100644 --- a/api_docs/kbn_management_settings_ids.devdocs.json +++ b/api_docs/kbn_management_settings_ids.devdocs.json @@ -1342,6 +1342,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/management-settings-ids", + "id": "def-common.OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID", + "description": [], + "signature": [ + "\"observability:enableContainerAssetView\"" + ], + "path": "packages/kbn-management/settings/setting_ids/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/management-settings-ids", "id": "def-common.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 297d57dcf33c2..c02ad2f6362ee 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 138 | 0 | 135 | 0 | +| 139 | 0 | 136 | 0 | ## Common diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 0399496f900ea..8e2abaa157e8a 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index 49180bbb02e57..36f4ad1664324 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index da99f5e9f1271..573e59b8eebec 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index de5c1110c2404..97bf635ef7cb9 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index abb534d9be1a2..c22f7ba48d96e 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index e952df482cf47..d84fd70e0101f 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index ac43b164b2534..b0777f30c7b36 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: 2024-05-16 +date: 2024-05-17 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_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index d5ea92226e837..3470163a6c67f 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index 8aaca7383fda2..6e43c3815a031 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index cfff61a6d388c..a935ca38b9907 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index 066dd0af7acd2..706082b11619b 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index 1dc691ed14ac1..61c4bd53b7bf8 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index 68beb5c99a0b1..9ad707baf9de7 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 2ec314b6d2f2f..0dc7fd4d8ce46 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: 2024-05-16 +date: 2024-05-17 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_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index bc36c983a8e58..fdb87e6ead0c8 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index 54904f9dcf01a..34e962709318d 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index ace9ba9a7a16c..1bd4b9f1987be 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 6cc9a25f58261..5f7cd4e2ff0a9 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: 2024-05-16 +date: 2024-05-17 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 be7e72f1acd6e..31f4017eac823 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: 2024-05-16 +date: 2024-05-17 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_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index e42d2355c629e..62b780240d0e9 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index df050e23b8ea3..98320a592e34d 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: 2024-05-16 +date: 2024-05-17 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 bcddcebaee35b..002d87f9f8b7d 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: 2024-05-16 +date: 2024-05-17 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 cd110f90c86da..b31da35bf65b2 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: 2024-05-16 +date: 2024-05-17 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 e690148f10f0e..4394bfcba4ed0 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: 2024-05-16 +date: 2024-05-17 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 8fa568ecb2db4..40a3bd21d910e 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: 2024-05-16 +date: 2024-05-17 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 3f488dc0d0f82..d41647a8f96de 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: 2024-05-16 +date: 2024-05-17 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_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index f39a80437742a..d07b6ab436856 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 7660ee4fbce76..71dad33e042a0 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: 2024-05-16 +date: 2024-05-17 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_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index 0b10962df3c63..225c805a33255 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 446045a85c68d..bc8fe63031abb 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: 2024-05-16 +date: 2024-05-17 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_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 00fb6b3e1c9a4..c4bb62c548c4a 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 0623299e03f7e..4a16938f93301 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index 9292911bd5c30..e02d381b3e051 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index f16ab074bb6cc..15a38628e7285 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 9c4da7bb006fb..3158f504dfa0b 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 2a259d02cbe57..e1cf9fe43736e 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 46db2baac7725..ccf41b9aeca24 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index 6a499920eb57b..34124658ca632 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index 893394d5f2309..971a09d2961bb 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index 17a3a3363b47f..fc637a17aaf88 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 30062861db8aa..5aaecbeb28bbe 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: 2024-05-16 +date: 2024-05-17 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 19e8f1ef457c7..4eaba13918985 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: 2024-05-16 +date: 2024-05-17 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 5988d319eec88..75097b46b245c 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: 2024-05-16 +date: 2024-05-17 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_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index ef342a31fa9e9..13c370601d475 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 3115d92cca918..a44e491c8d3a3 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: 2024-05-16 +date: 2024-05-17 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_check.mdx b/api_docs/kbn_plugin_check.mdx index 70c0cd1635403..b8d5a7807394b 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index b7272bf02596d..65e7c98c7b039 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: 2024-05-16 +date: 2024-05-17 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 2dc3d4b1f5564..3c4eeaf583bd7 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index 5c5db58273a13..840eec03bb9cb 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index 1d19fc6153c52..f7a3ee02052bc 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index b3473decf8180..4c5d579d8e0ea 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index d6dff54af4bfc..75eaac5c950b5 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index b95dab9985f24..076f18c6b1c04 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index d2ff18342228f..08f8a5afa177b 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 3d38276562ee3..2246b0c439f93 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 1a02d530677ff..7979c997747f9 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index f211ee67186c1..f38620267378c 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 939b04a827589..0b091edb14b97 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index f16b1d5406a9b..07257e9fbc75d 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 8e79c9fa63789..bb8079ef191ec 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 99213d80d4b0c..dcfacf132e61e 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: 2024-05-16 +date: 2024-05-17 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 19a4ffd6a4797..68f966745a9ec 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: 2024-05-16 +date: 2024-05-17 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 f8081532d763c..0417a37f7539e 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: 2024-05-16 +date: 2024-05-17 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 01da4c545fd1b..1774ed88835c7 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: 2024-05-16 +date: 2024-05-17 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 cb3e30a4c8bdc..6b6e08cd517ac 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index 5f62dc6463774..9a6ee5f3e5f3d 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index 834913754d3e9..ee4ec1dde186f 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index f1993c9dc6974..41d11899a42af 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index 0baedd862b84b..d1d6055b499da 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index 55d777ede8542..645b55ac68d01 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index 72cbe897839b5..58623b4f50029 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index 917278e0379be..382a175012340 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index f4c24fbe6e150..6febd41dd4a3f 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index 4271e03b4adbd..4aea2a32ee902 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index 5b1f1cbb75d4b..f41f3aefbdc71 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index 697ee4e73a921..0d6cc96a8303c 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index f9b9e50825e7f..3a25e6066e2e5 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index 1b70f82179fcf..b6bc3d26a2e6d 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 3ac83c9da4088..7fea2af26040a 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index 65435c955102a..91d869e6d8495 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index fc45e0583c934..bf43222cd3057 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: 2024-05-16 +date: 2024-05-17 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 3a04b271ffa77..a06c844bb8e3e 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.devdocs.json b/api_docs/kbn_search_api_panels.devdocs.json index 6b64604da0fec..3e17ad34f9985 100644 --- a/api_docs/kbn_search_api_panels.devdocs.json +++ b/api_docs/kbn_search_api_panels.devdocs.json @@ -326,7 +326,7 @@ "label": "IngestData", "description": [], "signature": [ - "({ codeSnippet, selectedLanguage, setSelectedLanguage, docLinks, assetBasePath, application, consolePlugin, sharePlugin, languages, consoleRequest, additionalIngestionPanel, }: React.PropsWithChildren) => JSX.Element" + "({ codeSnippet, selectedLanguage, selectedPipeline, setSelectedLanguage, docLinks, assetBasePath, application, consolePlugin, sharePlugin, languages, consoleRequest, additionalIngestionPanel, ingestPipelineData, setSelectedPipeline, defaultIngestPipeline, }: React.PropsWithChildren) => JSX.Element" ], "path": "packages/kbn-search-api-panels/components/ingest_data.tsx", "deprecated": false, @@ -337,7 +337,7 @@ "id": "def-common.IngestData.$1", "type": "CompoundType", "tags": [], - "label": "{\n codeSnippet,\n selectedLanguage,\n setSelectedLanguage,\n docLinks,\n assetBasePath,\n application,\n consolePlugin,\n sharePlugin,\n languages,\n consoleRequest,\n additionalIngestionPanel,\n}", + "label": "{\n codeSnippet,\n selectedLanguage,\n selectedPipeline,\n setSelectedLanguage,\n docLinks,\n assetBasePath,\n application,\n consolePlugin,\n sharePlugin,\n languages,\n consoleRequest,\n additionalIngestionPanel,\n ingestPipelineData,\n setSelectedPipeline,\n defaultIngestPipeline,\n}", "description": [], "signature": [ "React.PropsWithChildren" @@ -384,6 +384,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.IngestPipelinePanel", + "type": "Function", + "tags": [], + "label": "IngestPipelinePanel", + "description": [], + "signature": [ + "({ selectedPipeline, setSelectedPipeline, ingestPipelinesData, defaultIngestPipeline, }: React.PropsWithChildren) => JSX.Element" + ], + "path": "packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.IngestPipelinePanel.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n selectedPipeline,\n setSelectedPipeline,\n ingestPipelinesData,\n defaultIngestPipeline,\n}", + "description": [], + "signature": [ + "React.PropsWithChildren" + ], + "path": "packages/kbn-search-api-panels/components/ingest_pipelines/ingest_pipeline_panel.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/search-api-panels", "id": "def-common.InstallClientPanel", diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 0761b37a04863..61f63e012b7a4 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 74 | 0 | 74 | 0 | +| 76 | 0 | 76 | 0 | ## Common diff --git a/api_docs/kbn_search_connectors.devdocs.json b/api_docs/kbn_search_connectors.devdocs.json index 1536cc0f405c5..703be6afbdcd4 100644 --- a/api_docs/kbn_search_connectors.devdocs.json +++ b/api_docs/kbn_search_connectors.devdocs.json @@ -2643,15 +2643,7 @@ "section": "def-common.ElasticsearchClient", "text": "ElasticsearchClient" }, - ", connectorId: string, { advancedSnippet, filteringRules, }: { advancedSnippet: string; filteringRules: ", - { - "pluginId": "@kbn/search-connectors", - "scope": "common", - "docId": "kibKbnSearchConnectorsPluginApi", - "section": "def-common.FilteringRule", - "text": "FilteringRule" - }, - "[]; }) => Promise<", + ", connectorId: string) => Promise<", { "pluginId": "@kbn/search-connectors", "scope": "common", @@ -2700,51 +2692,6 @@ "deprecated": false, "trackAdoption": false, "isRequired": true - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.updateFiltering.$3", - "type": "Object", - "tags": [], - "label": "{\n advancedSnippet,\n filteringRules,\n }", - "description": [], - "path": "packages/kbn-search-connectors/lib/update_filtering.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.updateFiltering.$3.advancedSnippet", - "type": "string", - "tags": [], - "label": "advancedSnippet", - "description": [], - "path": "packages/kbn-search-connectors/lib/update_filtering.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.updateFiltering.$3.filteringRules", - "type": "Array", - "tags": [], - "label": "filteringRules", - "description": [], - "signature": [ - { - "pluginId": "@kbn/search-connectors", - "scope": "common", - "docId": "kibKbnSearchConnectorsPluginApi", - "section": "def-common.FilteringRule", - "text": "FilteringRule" - }, - "[]" - ], - "path": "packages/kbn-search-connectors/lib/update_filtering.ts", - "deprecated": false, - "trackAdoption": false - } - ] } ], "returnComment": [], @@ -45942,6 +45889,206 @@ } ] }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security", + "type": "Object", + "tags": [], + "label": "use_document_level_security", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TOGGLE" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".BOOLEAN" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_document_level_security.value", + "type": "boolean", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "@kbn/search-connectors", "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service", diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index 9e5db9a384896..5ed11e321eb61 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3661 | 0 | 3661 | 0 | +| 3672 | 0 | 3672 | 0 | ## Common diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index bfef1bd8b3aea..984025b7d06e7 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index 984eabeb4f418..a9b26f8c602ec 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index 490d1081a6e8a..d58b65eb56578 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 387775730d6cf..bcb46438310cc 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index 1211f3deb9f7f..94deed4ec4598 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index 1fae3b2181779..89d6e4fc0fa13 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index f7e6285fa9233..70ba64c9ab624 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index c3098606794d2..c8834e98b44f3 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index d119d9d2c72d5..f3442ad45f12b 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 21a0574be4cf1..cb475dad3ad2f 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 63fdbc46a818b..7b7a9bfa7808c 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: 2024-05-16 +date: 2024-05-17 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 10fedf5ce7e76..96e2ebca3d22e 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: 2024-05-16 +date: 2024-05-17 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 4702520018bdd..30e170f3cf2cf 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: 2024-05-16 +date: 2024-05-17 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 ebc9a026ea74f..5f658ed3e12df 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: 2024-05-16 +date: 2024-05-17 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 0f486cabed185..60aa38c8a7a96 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: 2024-05-16 +date: 2024-05-17 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 4506b13a9fa35..150892b1a83b9 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: 2024-05-16 +date: 2024-05-17 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 dd3be276a74ce..023bbf4aeee19 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: 2024-05-16 +date: 2024-05-17 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 83a4fa2286df8..8a1874fe8b814 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: 2024-05-16 +date: 2024-05-17 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 f21facb7dacf1..8e604d51e2470 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: 2024-05-16 +date: 2024-05-17 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.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index baae394990f71..a62bdb48a0f09 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 4142401c57e43..faa08f94d3118 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 9440ca4903a74..12815e0c764dd 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: 2024-05-16 +date: 2024-05-17 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 56497662d301e..e38be73a94349 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: 2024-05-16 +date: 2024-05-17 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.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 5f7eff5937ee5..a0457c67c4803 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index fd2bbf80c087b..e6a41b28a8dd5 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: 2024-05-16 +date: 2024-05-17 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.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index f1004ec7b35de..d5fa741e96602 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index bdfa6b9b967ab..94923dc377f94 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: 2024-05-16 +date: 2024-05-17 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 62ac97e20720e..4f131138cc71a 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: 2024-05-16 +date: 2024-05-17 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 a019393fea119..5f11ec521639a 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: 2024-05-16 +date: 2024-05-17 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 1735a03457eb9..4e4ebe2b8e153 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: 2024-05-16 +date: 2024-05-17 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 262e529fa13aa..86c4d69b3efd5 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: 2024-05-16 +date: 2024-05-17 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 1db39cadbf035..2e31210d23d62 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 22a9f74217185..380c44789901c 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index e977a2820cdb7..58bf802d7d92f 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index efaaa4e4114f6..6e7e1d3a1f40c 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index 9a854220887bf..42496b3489c2d 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index 107e89971e6f6..d3298b2ac803b 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index dca21675ec13b..44f0dba92b480 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 98702aa2c1804..fa60e6707fbf9 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: 2024-05-16 +date: 2024-05-17 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 3b36ea5d1e9c0..073d802aa91de 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: 2024-05-16 +date: 2024-05-17 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_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index a518de989cb57..f50075ed2d474 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: 2024-05-16 +date: 2024-05-17 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_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 1bc4f54b8fa0e..c2ebdf62a81d8 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: 2024-05-16 +date: 2024-05-17 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 8361a23eb2903..7835d3cdf4d22 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: 2024-05-16 +date: 2024-05-17 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 35deb9618afa1..956722380965a 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: 2024-05-16 +date: 2024-05-17 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_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index f907c556f1c87..5d44372103f97 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 4d2f5f1823e80..0f6e6712887e8 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index f645cd4d00c9e..3c852594630f1 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: 2024-05-16 +date: 2024-05-17 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 1254ca2154b94..bb3ee8f113b7d 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: 2024-05-16 +date: 2024-05-17 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 a338ea52665e2..b5bd9309f8b18 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: 2024-05-16 +date: 2024-05-17 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 8ef215892bacc..be42cc4157ca2 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: 2024-05-16 +date: 2024-05-17 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 45c20f1b23428..0046fb6826ea2 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: 2024-05-16 +date: 2024-05-17 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 d0965a9c5c3a0..1b5cb7ae74613 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: 2024-05-16 +date: 2024-05-17 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 e7521ef97563c..155ddc0c11d8f 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: 2024-05-16 +date: 2024-05-17 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 2d5cea3cf4b74..537e30cbb2d65 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: 2024-05-16 +date: 2024-05-17 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 ebce9336e18f5..b86f71d81beed 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: 2024-05-16 +date: 2024-05-17 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 540063b47b078..09bb430a3aa85 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: 2024-05-16 +date: 2024-05-17 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 0094b649ab73c..5ff6f52d7fe54 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: 2024-05-16 +date: 2024-05-17 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 549075b984ec6..76e22a9401f24 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: 2024-05-16 +date: 2024-05-17 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 b2259a25a7042..e5bc883bddde8 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: 2024-05-16 +date: 2024-05-17 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 233e5b4be552c..f5604aecb0dd6 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: 2024-05-16 +date: 2024-05-17 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 b9b28030ef322..a94994d2f2b61 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: 2024-05-16 +date: 2024-05-17 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 f7c4adf7e8081..b537dd0dfc291 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: 2024-05-16 +date: 2024-05-17 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 26e2bd19edceb..c49d6066a79d5 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: 2024-05-16 +date: 2024-05-17 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 1c336dd3a7fe5..de9299337b544 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: 2024-05-16 +date: 2024-05-17 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 08ef89efcbd47..a91d801ec4c5b 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: 2024-05-16 +date: 2024-05-17 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 09c1fc506e3c6..23e909c407999 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: 2024-05-16 +date: 2024-05-17 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 8f07624c61f94..b83265c35d7b8 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: 2024-05-16 +date: 2024-05-17 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 ef5ce2e346ee5..ac29444142cdf 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: 2024-05-16 +date: 2024-05-17 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 e11424c8b9a73..e710289fd40b5 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: 2024-05-16 +date: 2024-05-17 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 d6bcbca90ae50..1069afb70486b 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: 2024-05-16 +date: 2024-05-17 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 b01e77817da3c..bec8d90d7c0a8 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: 2024-05-16 +date: 2024-05-17 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 42477fc361f44..2fee7168aabaa 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: 2024-05-16 +date: 2024-05-17 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 600f2c8ef0234..78417172d1319 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: 2024-05-16 +date: 2024-05-17 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 579d9be1c6ffe..293f834f05d33 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: 2024-05-16 +date: 2024-05-17 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 54a7bd5123d50..b5b069a29e0ab 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: 2024-05-16 +date: 2024-05-17 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 c1b20a9cc7c2a..8ac82dd68f8cf 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: 2024-05-16 +date: 2024-05-17 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_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index 6eb0d20daa312..3633185c4a091 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 3fc67c924f5ab..e32ff9e949652 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: 2024-05-16 +date: 2024-05-17 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.devdocs.json b/api_docs/kbn_slo_schema.devdocs.json index 2322c73758327..fb749318c15e2 100644 --- a/api_docs/kbn_slo_schema.devdocs.json +++ b/api_docs/kbn_slo_schema.devdocs.json @@ -745,7 +745,7 @@ "section": "def-common.Duration", "text": "Duration" }, - " | undefined; }; groupBy: string | string[]; revision: number; } & { remoteName?: string | undefined; range?: { from: string; to: string; } | undefined; })[]; }" + " | undefined; }; groupBy: string | string[]; revision: number; } & { remoteName?: string | undefined; range?: { from: Date; to: Date; } | undefined; })[]; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts", "deprecated": false, @@ -910,7 +910,7 @@ "label": "GetPreviewDataParams", "description": [], "signature": [ - "{ indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; }; range: { start: number; end: number; }; } & { objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: ", + "{ indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; }; range: { from: Date; to: Date; }; } & { objective?: ({ target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: ", { "pluginId": "@kbn/slo-schema", "scope": "common", @@ -3241,18 +3241,10 @@ "signature": [ "TypeC", "<{ from: ", - "UnionC", - "<[", "Type", - ", ", - "StringC", - "]>; to: ", - "UnionC", - "<[", + "; to: ", "Type", - ", ", - "StringC", - "]>; }>" + "; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/schema/common.ts", "deprecated": false, @@ -3475,10 +3467,10 @@ "; range: ", "TypeC", "<{ from: ", - "StringC", - "; to: ", - "StringC", - "; }>; }>]>>; }>; }>" + "Type", + "; to: ", + "Type", + "; }>; }>]>>; }>; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/routes/fetch_historical_summary.ts", "deprecated": false, @@ -8054,11 +8046,11 @@ "AnyC", ">; }>>; }>]>; }>]>; }>]>; range: ", "TypeC", - "<{ start: ", - "NumberC", - "; end: ", - "NumberC", - "; }>; }>, ", + "<{ from: ", + "Type", + "; to: ", + "Type", + "; }>; }>, ", "PartialC", "<{ objective: ", "IntersectionC", diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 57f02527ae298..9b1dcf79f7ed0 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_es.mdx b/api_docs/kbn_solution_nav_es.mdx index 166bc52255a21..be1ef596adfa5 100644 --- a/api_docs/kbn_solution_nav_es.mdx +++ b/api_docs/kbn_solution_nav_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-solution-nav-es title: "@kbn/solution-nav-es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/solution-nav-es plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/solution-nav-es'] --- import kbnSolutionNavEsObj from './kbn_solution_nav_es.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_oblt.mdx b/api_docs/kbn_solution_nav_oblt.mdx index 8146c538764e6..1f69c6c2dbd24 100644 --- a/api_docs/kbn_solution_nav_oblt.mdx +++ b/api_docs/kbn_solution_nav_oblt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-solution-nav-oblt title: "@kbn/solution-nav-oblt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/solution-nav-oblt plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/solution-nav-oblt'] --- import kbnSolutionNavObltObj from './kbn_solution_nav_oblt.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 027ec53e5f5cd..f71f3086091d0 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index fba0c0bc86551..df1e5cb309937 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 91e2dc1fa14d7..dbc6b71f15cf1 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: 2024-05-16 +date: 2024-05-17 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 d2c5020047e94..e5dbedf0ae93f 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: 2024-05-16 +date: 2024-05-17 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 36e8a7f76b79f..90df907eeb784 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: 2024-05-16 +date: 2024-05-17 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 42facc713b24c..db5fa66f10db7 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: 2024-05-16 +date: 2024-05-17 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 84737a2c9f9b4..1fa524b0efb8d 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index b184c22be1544..4289e00f1e5e7 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 2236e323df45f..9bdb32296af21 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: 2024-05-16 +date: 2024-05-17 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 a95d2c09c4db2..97d1302cdea1c 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index bfddc274650d6..be45708d02c2a 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index 314d215e990ce..78612333dcd1b 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index cfec45e65cc76..0c988ebd69f35 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index 1edc8f9da5712..c228926342b71 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index c4d3e26e9546d..ecc4518ba8e81 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 176d30b579f26..a96fa3df263b9 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: 2024-05-16 +date: 2024-05-17 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 dc5a0a8484a76..c5c4e4a4a132b 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: 2024-05-16 +date: 2024-05-17 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 ae43f0539af1a..0acf68f5d9d20 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: 2024-05-16 +date: 2024-05-17 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 bb0a0f8c06991..ce32374b41013 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: 2024-05-16 +date: 2024-05-17 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 ef827c0a57ccd..9031fda6e4043 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index 49959eb9f4656..2ee487cd0d663 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index 3791de89a21cf..0c51079c29228 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 931cda1107e6b..3d052cb5f9202 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_badge.mdx b/api_docs/kbn_unsaved_changes_badge.mdx index 9352a38315dd7..18eb27ecd9cc4 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index 32e0ef1be4cb4..627774c485e46 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 1ee17f2da1447..2c67f344d71aa 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: 2024-05-16 +date: 2024-05-17 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 724cd472c9551..80abfba5a79ea 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: 2024-05-16 +date: 2024-05-17 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 d41184c744562..58eb8d7c15930 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: 2024-05-16 +date: 2024-05-17 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 31f07dfbc118d..a61141d65731b 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index b44bb62993580..914287fb5f99c 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index 9d4d5b45e0d5e..dfe5fc14f95f5 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 321f42394aa4e..a909a06d1bae8 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index cf1968ea2b1c5..88715e0a5a96b 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 93dc63ea17678..ea0a35c33ad68 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 357169b6bc610..3556053ea6943 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: 2024-05-16 +date: 2024-05-17 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 7a5aef74a8c4a..5fe49dd46adf0 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: 2024-05-16 +date: 2024-05-17 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 011e618aa23aa..b0f4c61c8a5a8 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: 2024-05-16 +date: 2024-05-17 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 919f4a3b6cb8d..7a9e99ddbfaa8 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: 2024-05-16 +date: 2024-05-17 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 75b891aa6e677..f5c5fadbdedac 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: 2024-05-16 +date: 2024-05-17 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 0e482e6ea7225..8031bc32939b9 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: 2024-05-16 +date: 2024-05-17 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 26e85a2bdd468..5b1dde74c7d56 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: 2024-05-16 +date: 2024-05-17 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 92d78bfb15e3a..18a581ce893ac 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index 96d81e43d0780..a9a7e2a5cd7fa 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index abd0d0369f6ef..c614d4f6dff63 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index 00a94ada300b4..2115a9ac683c5 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index 7a709b5e0fc4e..b3676d8731bd7 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 6ad6f3f106dc5..7c20eddf443c2 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index b8b6c19554faa..9cb264a9150ae 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: 2024-05-16 +date: 2024-05-17 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 3f913dd6a1d76..59378c35aa8e4 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: 2024-05-16 +date: 2024-05-17 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 e84d3422051a2..9d9ecca4dcbfd 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.devdocs.json b/api_docs/metrics_data_access.devdocs.json index da97d001411cf..a744b95998f51 100644 --- a/api_docs/metrics_data_access.devdocs.json +++ b/api_docs/metrics_data_access.devdocs.json @@ -1301,7 +1301,107 @@ "InventoryModel", "<", "InventoryMetrics", - ">)[]" + "> | ", + "InventoryModel", + "<", + "InventoryMetricsWithCharts", + "<{ readonly dockerContainerCpuUsage: ", + "LensBaseLayer", + "; readonly dockerContainerMemoryUsage: ", + "LensBaseLayer", + "; readonly k8sContainerCpuUsage: ", + "LensBaseLayer", + "; readonly k8sContainerMemoryUsage: ", + "LensBaseLayer", + "; }, { readonly cpu: { readonly xy: { readonly dockerContainerCpuUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; chartType: \"xy\"; layers: ({ dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"series\"; breakdown?: ", + "LensBreakdownConfig", + " | undefined; xAxis: ", + "LensBreakdownConfig", + "; seriesType: \"area\" | \"line\" | \"bar\"; } | { dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", + "LensReferenceLineLayer", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"left\" | \"top\" | \"bottom\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + "LensYBoundsConfig", + " | undefined; } & { id: string; }; readonly k8sContainerCpuUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; chartType: \"xy\"; layers: ({ dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"series\"; breakdown?: ", + "LensBreakdownConfig", + " | undefined; xAxis: ", + "LensBreakdownConfig", + "; seriesType: \"area\" | \"line\" | \"bar\"; } | { dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", + "LensReferenceLineLayer", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"left\" | \"top\" | \"bottom\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + "LensYBoundsConfig", + " | undefined; } & { id: string; }; }; readonly metric: { readonly dockerContainerCpuUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; label?: string | undefined; filter?: string | undefined; format?: \"string\" | \"number\" | \"duration\" | \"percent\" | \"currency\" | \"bytes\" | \"bits\" | undefined; decimals?: number | undefined; normalizeByUnit?: \"m\" | \"d\" | \"h\" | \"s\" | undefined; compactValues?: boolean | undefined; randomSampling?: number | undefined; useGlobalFilter?: boolean | undefined; seriesColor?: string | undefined; value: string; chartType: \"metric\"; querySecondaryMetric?: string | undefined; queryMaxValue?: string | undefined; breakdown?: ", + "LensBreakdownConfig", + " | undefined; trendLine?: boolean | undefined; subtitle?: string | undefined; } & { id: string; }; readonly k8sContainerCpuUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; label?: string | undefined; filter?: string | undefined; format?: \"string\" | \"number\" | \"duration\" | \"percent\" | \"currency\" | \"bytes\" | \"bits\" | undefined; decimals?: number | undefined; normalizeByUnit?: \"m\" | \"d\" | \"h\" | \"s\" | undefined; compactValues?: boolean | undefined; randomSampling?: number | undefined; useGlobalFilter?: boolean | undefined; seriesColor?: string | undefined; value: string; chartType: \"metric\"; querySecondaryMetric?: string | undefined; queryMaxValue?: string | undefined; breakdown?: ", + "LensBreakdownConfig", + " | undefined; trendLine?: boolean | undefined; subtitle?: string | undefined; } & { id: string; }; }; }; readonly memory: { xy: { dockerContainerMemoryUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; chartType: \"xy\"; layers: ({ dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"series\"; breakdown?: ", + "LensBreakdownConfig", + " | undefined; xAxis: ", + "LensBreakdownConfig", + "; seriesType: \"area\" | \"line\" | \"bar\"; } | { dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", + "LensReferenceLineLayer", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"left\" | \"top\" | \"bottom\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + "LensYBoundsConfig", + " | undefined; } & { id: string; }; k8sContainerMemoryUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; chartType: \"xy\"; layers: ({ dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"series\"; breakdown?: ", + "LensBreakdownConfig", + " | undefined; xAxis: ", + "LensBreakdownConfig", + "; seriesType: \"area\" | \"line\" | \"bar\"; } | { dataset?: ", + "LensDataset", + " | undefined; yAxis: ", + "LensBaseLayer", + "[]; type: \"annotation\"; events: ({ name: string; color?: string | undefined; icon?: string | undefined; datetime: string; } | { name: string; color?: string | undefined; icon?: string | undefined; field: string; filter: string; })[]; } | ", + "LensReferenceLineLayer", + ")[]; legend?: { show?: boolean | undefined; position?: \"right\" | \"left\" | \"top\" | \"bottom\" | undefined; } | undefined; axisTitleVisibility?: { showXAxisTitle?: boolean | undefined; showYAxisTitle?: boolean | undefined; } | undefined; emphasizeFitting?: boolean | undefined; fittingFunction?: \"None\" | \"Average\" | \"Zero\" | \"Nearest\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined; yBounds?: ", + "LensYBoundsConfig", + " | undefined; } & { id: string; }; }; metric: { dockerContainerMemoryUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; label?: string | undefined; filter?: string | undefined; format?: \"string\" | \"number\" | \"duration\" | \"percent\" | \"currency\" | \"bytes\" | \"bits\" | undefined; decimals?: number | undefined; normalizeByUnit?: \"m\" | \"d\" | \"h\" | \"s\" | undefined; compactValues?: boolean | undefined; randomSampling?: number | undefined; useGlobalFilter?: boolean | undefined; seriesColor?: string | undefined; value: string; chartType: \"metric\"; querySecondaryMetric?: string | undefined; queryMaxValue?: string | undefined; breakdown?: ", + "LensBreakdownConfig", + " | undefined; trendLine?: boolean | undefined; subtitle?: string | undefined; } & { id: string; }; k8sContainerMemoryUsage: { title: string; dataset?: ", + "LensDataset", + " | undefined; label?: string | undefined; filter?: string | undefined; format?: \"string\" | \"number\" | \"duration\" | \"percent\" | \"currency\" | \"bytes\" | \"bits\" | undefined; decimals?: number | undefined; normalizeByUnit?: \"m\" | \"d\" | \"h\" | \"s\" | undefined; compactValues?: boolean | undefined; randomSampling?: number | undefined; useGlobalFilter?: boolean | undefined; seriesColor?: string | undefined; value: string; chartType: \"metric\"; querySecondaryMetric?: string | undefined; queryMaxValue?: string | undefined; breakdown?: ", + "LensBreakdownConfig", + " | undefined; trendLine?: boolean | undefined; subtitle?: string | undefined; } & { id: string; }; }; }; }>>)[]" ], "path": "x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/index.ts", "deprecated": false, diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index ccd39f2162bbf..198a88d6d3b22 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 4f8c6b6fe013a..cc3a12fe8a590 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx index 73a8c8b5c7d31..52f90e201be8f 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] --- import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 40cec41f85dda..4b58490652ace 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: 2024-05-16 +date: 2024-05-17 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 c80f1935b2f6e..feda619f2dde3 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: 2024-05-16 +date: 2024-05-17 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 b81fed6e69a37..1e7b8ef379f8f 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: 2024-05-16 +date: 2024-05-17 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 397df74040477..b85d674a84232 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index 5a3533019838b..2c274f8a5b07a 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 0dd53fb82f210..3f6937d1fe7fa 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index 3cffa85091ec5..977e46df309f5 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -4521,6 +4521,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-public.enableInfrastructureContainerAssetView", + "type": "string", + "tags": [], + "label": "enableInfrastructureContainerAssetView", + "description": [], + "signature": [ + "\"observability:enableInfrastructureContainerAssetView\"" + ], + "path": "x-pack/plugins/observability_solution/observability/common/ui_settings_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-public.enableInfrastructureHostsView", @@ -10105,6 +10120,90 @@ } ] }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView", + "type": "Object", + "tags": [], + "label": "[enableInfrastructureContainerAssetView]", + "description": [], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.category", + "type": "Array", + "tags": [], + "label": "category", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.value", + "type": "boolean", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-server.uiSettings.enableInfrastructureContainerAssetView.schema", + "type": "Object", + "tags": [], + "label": "schema", + "description": [], + "signature": [ + { + "pluginId": "@kbn/config-schema", + "scope": "common", + "docId": "kibKbnConfigSchemaPluginApi", + "section": "def-common.Type", + "text": "Type" + }, + "" + ], + "path": "x-pack/plugins/observability_solution/observability/server/ui_settings.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "observability", "id": "def-server.uiSettings.enableInfrastructureProfilingIntegration", @@ -14051,6 +14150,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "observability", + "id": "def-common.enableInfrastructureContainerAssetView", + "type": "string", + "tags": [], + "label": "enableInfrastructureContainerAssetView", + "description": [], + "signature": [ + "\"observability:enableInfrastructureContainerAssetView\"" + ], + "path": "x-pack/plugins/observability_solution/observability/common/ui_settings_keys.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "observability", "id": "def-common.enableInfrastructureHostsView", diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 4262ed9eec0b0..f561cc2b30f38 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 687 | 2 | 678 | 15 | +| 695 | 2 | 686 | 15 | ## Client diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index 00c154ef7b9bd..f914b84526451 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 94079a6a623ab..af86bb282745b 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index dff0be9912090..01fb2eb61183a 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index de588320fe4d1..0fc72712b45b0 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index b6c68453a523a..c5e4c62abe8e8 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index a9e2a3bac29c6..26f7ba67398be 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: 2024-05-16 +date: 2024-05-17 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 8a9550166fc8d..d65e6fd1cb6b7 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index 2bb9abcc168ee..de7c5bd7b1e8f 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 3e64d151f37b2..0a85ec1f71028 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 48169 | 241 | 36749 | 1858 | +| 48193 | 241 | 36773 | 1860 | ## Plugin Directory @@ -142,7 +142,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | -| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 687 | 2 | 678 | 15 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 695 | 2 | 686 | 15 | | | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 253 | 1 | 251 | 26 | | | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 2 | 0 | 2 | 0 | | | [@elastic/obs-ai-assistant](https://github.com/orgs/elastic/teams/obs-ai-assistant) | - | 2 | 0 | 2 | 0 | @@ -249,7 +249,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 18 | 0 | 18 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 4 | 0 | 4 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 49 | 0 | 49 | 8 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 191 | 0 | 191 | 28 | +| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 193 | 0 | 193 | 30 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-qa](https://github.com/orgs/elastic/teams/kibana-qa) | - | 12 | 0 | 12 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 4 | 0 | 1 | 0 | @@ -538,7 +538,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 23 | 0 | 7 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 8 | 0 | 2 | 3 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 45 | 0 | 0 | 0 | -| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 138 | 0 | 135 | 0 | +| | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 139 | 0 | 136 | 0 | | | [@elastic/appex-sharedux @elastic/kibana-management](https://github.com/orgs/elastic/teams/appex-sharedux ) | - | 20 | 0 | 11 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 88 | 0 | 10 | 0 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 56 | 0 | 6 | 0 | @@ -621,8 +621,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 16 | 0 | 16 | 1 | | | [@elastic/security-detections-response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 125 | 0 | 122 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 74 | 0 | 74 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 3661 | 0 | 3661 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 76 | 0 | 76 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 3672 | 0 | 3672 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 18 | 1 | 17 | 1 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 25 | 0 | 25 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 18 | 1 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index 2145e99247362..17ae841e4cf7d 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index da95f22c5d43b..e6d914b993e9a 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: 2024-05-16 +date: 2024-05-17 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 59bcde1e25540..d9b6d1e49dc00 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 7e50e73193555..450b95b62aee9 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 3c2a53eb016ad..627f617141db3 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: 2024-05-16 +date: 2024-05-17 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 2a05fe65cb673..cfbfe38c627e7 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: 2024-05-16 +date: 2024-05-17 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 23bf7e53d0667..bab44d5f808c0 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: 2024-05-16 +date: 2024-05-17 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 e36801de65fe5..3aae98fbefbb0 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: 2024-05-16 +date: 2024-05-17 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 aacbce38894b8..1be7f3ab83872 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: 2024-05-16 +date: 2024-05-17 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 2bc1a39cc90c6..789683d0e9f0b 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: 2024-05-16 +date: 2024-05-17 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 46ae5fbff6659..8767f1d956706 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: 2024-05-16 +date: 2024-05-17 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 e20f831efa6aa..24881313fef48 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: 2024-05-16 +date: 2024-05-17 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 f28487633f2da..3bdf5ca9305c6 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: 2024-05-16 +date: 2024-05-17 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 3bc20357ed071..ac922dcccf9a6 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: 2024-05-16 +date: 2024-05-17 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 b8692decda8e9..41bb46f258268 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: 2024-05-16 +date: 2024-05-17 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 72f430068b701..b0b21f7ef1698 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: 2024-05-16 +date: 2024-05-17 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 712304b5e4224..675cee452135c 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index 1ac19f6cbeeb9..4f26ccc544955 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index e10f9cbb278ac..8d42178f20e00 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index ad30d44332bc3..5066649de862d 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 9cc1a66ad2cad..d35d950f51d48 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: 2024-05-16 +date: 2024-05-17 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 85c8a7e004df7..75e3bfcc21f7e 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index fea3359dc12e8..8d1e4b618f3cf 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index d6ddfb15b8aa3..f1ddf26d0a3b7 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 30cbb8e5ac8a7..49fce71c23171 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index cf0a98ae88b87..7f7135ff16625 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index 1cb1741ffda15..f15d76343d1da 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index c1f4d9050b1ef..1d8b1d434f1bb 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: 2024-05-16 +date: 2024-05-17 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 8c720971ed0a1..8bc9855effad1 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.devdocs.json b/api_docs/slo.devdocs.json index 7641ca4b43fd5..02cfed22cb843 100644 --- a/api_docs/slo.devdocs.json +++ b/api_docs/slo.devdocs.json @@ -1256,7 +1256,7 @@ }, "<", "CreateSLOForm", - "<{ type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; }>> | undefined; }) => Promise; }" + "<{ type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.synthetics.availability\"; params: { monitorIds: { value: string; label: string; }[]; index: string; } & { tags?: { value: string; label: string; }[] | undefined; projects?: { value: string; label: string; }[] | undefined; filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; }; total: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"last_value\" | \"cardinality\" | \"std_deviation\"; field: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }); } & { filter?: string | { kqlQuery: 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; }; query: { [x: string]: any; }; }[]; } | undefined; }; }>> | undefined; }) => JSX.Element; }" ], "path": "x-pack/plugins/observability_solution/slo/public/types.ts", "deprecated": false, diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 8b5389648d7be..edb940a1a20ae 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index f6aaf051b3b9a..bc3e5500f7e51 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: 2024-05-16 +date: 2024-05-17 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 830a9e5aca980..ecccab49e53ad 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: 2024-05-16 +date: 2024-05-17 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 812a693b535ed..de2e177b8d43d 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: 2024-05-16 +date: 2024-05-17 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 02eb6962b84dd..9aa5b7dbc4989 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: 2024-05-16 +date: 2024-05-17 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 516c11d380aa9..d315f52b1e028 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: 2024-05-16 +date: 2024-05-17 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 74631a8919b1e..bd9a2bba7bbf1 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: 2024-05-16 +date: 2024-05-17 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 053edf9c9126d..1661a32a6d3c7 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: 2024-05-16 +date: 2024-05-17 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 b90952aa40d1d..2d9f16cf8dbf4 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: 2024-05-16 +date: 2024-05-17 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 857fc60f97275..dab9fb8c7c0b2 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index 8ecaf47a13738..0602d9f0463bf 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 378d0497d9775..cd2e9d3f30b2e 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: 2024-05-16 +date: 2024-05-17 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 e1022f8e64e82..15785d5e9c77d 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: 2024-05-16 +date: 2024-05-17 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 9bda799d01487..5c6e086cdbe9c 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 4ff64d38543aa..948a680134bf6 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 709beb47f717d..d29f81c4b8e70 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: 2024-05-16 +date: 2024-05-17 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 3a0eae1a653d8..24e68e14aff39 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index 73a64d8061abd..ace5e5c862af6 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 69324711e559b..a01553de13ec7 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 4f96aa3a99e84..da0d4980397d0 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: 2024-05-16 +date: 2024-05-17 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 22caefb2c4aa7..bb8e9a67fe88f 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 75c3728f3990a..d1f9c494f3a48 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 89fb7f8808360..85b4d97d1e026 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: 2024-05-16 +date: 2024-05-17 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 45c0fe42cf6ce..d1bb77ed006ef 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: 2024-05-16 +date: 2024-05-17 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 e53fe6a4b37c8..db6a1ca512588 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: 2024-05-16 +date: 2024-05-17 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 17e298249be94..55570b4ff6a5b 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: 2024-05-16 +date: 2024-05-17 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 f3adce8e310ca..897c86f288392 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: 2024-05-16 +date: 2024-05-17 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 5d6141e96a9cf..da0ff135945fd 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: 2024-05-16 +date: 2024-05-17 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 2696af2b3e9b5..5c8e4075daeec 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: 2024-05-16 +date: 2024-05-17 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 349085d407f44..36ee250be6b2f 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: 2024-05-16 +date: 2024-05-17 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 b97fa189cac8b..b7c940f5b3377 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: 2024-05-16 +date: 2024-05-17 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 ad4855319ba27..b1b6dac3edd80 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: 2024-05-16 +date: 2024-05-17 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 62ca2ea357824..184b4ec0d7070 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: 2024-05-16 +date: 2024-05-17 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 9be604bdcc0da..207b8f90b309d 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: 2024-05-16 +date: 2024-05-17 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 31bdcc7b53bb3..028b77aa7c213 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: 2024-05-16 +date: 2024-05-17 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 a33529abce609..a10ede7ef660a 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: 2024-05-16 +date: 2024-05-17 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From a74d12cd7c4fccdad65df82ca0083234bfd7346a Mon Sep 17 00:00:00 2001 From: elena-shostak <165678770+elena-shostak@users.noreply.github.com> Date: Fri, 17 May 2024 09:55:42 +0200 Subject: [PATCH 29/71] [FTR] Session Concurrency Test (#183409) ## Summary Set `security.session.cleanupInterval` to 5h for session concurrency test. ### **Prerequisites** - Task for session cleanup with [default schedule set to 1h](https://github.com/elastic/kibana/blob/main/x-pack/plugins/security/server/config.ts#L222). - Task polling interval is set to [3000ms](https://github.com/elastic/kibana/blob/main/x-pack/plugins/task_manager/server/config.ts#L13). - We override `scheduledAt` once we make a request in [runCleanupTaskSoon](https://github.com/elastic/kibana/blob/main/x-pack/test/security_api_integration/tests/session_concurrent_limit/cleanup.ts#L145). ### **Hypothesis** Taking into consideration that: - `session_cleanup` task is not the only one scheduled during test run. - There is sort of an exponential backoff implemented for task polling if there are too many retries. - Clock jitter. I had a hypothesis that if our whole test run exceeds 1h or polling interval gets adjusted because of retries we might end up executing the scheduled cleanup before we trigger `runCleanupTaskSoon` (this is there we drop 1 session already). ### **FTR runs (x55 each)** - `cleanupInterval` set to 5h: [#1](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5986) :green_circle:, [#2](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5987) :green_circle: - `cleanupInterval` set to default 1h: [#1](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5983) :green_circle:, [#2](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5982) :red_circle: (has 2 failures out of 55) ### Checklist - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) __Fixes: https://github.com/elastic/kibana/issues/149091__ --- .../security_api_integration/session_concurrent_limit.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/test/security_api_integration/session_concurrent_limit.config.ts b/x-pack/test/security_api_integration/session_concurrent_limit.config.ts index 748ff56fb4d9d..63da7220f3959 100644 --- a/x-pack/test/security_api_integration/session_concurrent_limit.config.ts +++ b/x-pack/test/security_api_integration/session_concurrent_limit.config.ts @@ -45,6 +45,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { ...xPackAPITestsConfig.get('kbnTestServer.serverArgs'), `--plugin-path=${testEndpointsPlugin}`, '--xpack.security.session.concurrentSessions.maxSessions=2', + '--xpack.security.session.cleanupInterval=5h', `--xpack.security.authc.providers=${JSON.stringify({ basic: { basic1: { order: 0 } }, saml: { saml1: { order: 1, realm: 'saml1' } }, From ae4eec9604a9732bd75c8997e1573d9a6358c699 Mon Sep 17 00:00:00 2001 From: Andrew Wilkins Date: Fri, 17 May 2024 16:02:44 +0800 Subject: [PATCH 30/71] [ObservabilitySolution] use the field API in APM aggregations (#183250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR updates various aggregation scripts in the APM UI to use the Elasticsearch [field API](https://www.elastic.co/guide/en/elasticsearch/reference/current/script-fields-api.html). From the docs: > Use the field API to access document fields: > > `field('my_field').get()` > > This API fundamentally changes how you access documents in Painless. Previously, you had to access the doc map with the field name that you wanted to access: > > `doc['my_field'].value` > > Accessing document fields this way didn’t handle missing values or missing mappings, which meant that to write robust Painless scripts, you needed to include logic to check that both fields and values exist. Doing this will enable us to gracefully switch over to using the Elasticsearch `apm-data` plugin, which we have been working on as a replacement for the APM integration package (https://github.com/elastic/apm-server/issues/11528). One of the significant differences with the integration package is that `apm-data`'s templates rely much more heavily on dynamic mapping. So if for example `service.environment` is missing in a document, accessing it as `doc["service.environment"]` may lead to exceptions like "No field found for [service.environment] in mapping". We address this by using the field API, which will consistently return a given default field value for documents that lack the given field, regardless of whether that field is in the mappings. ### 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 ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../waterfall/waterfall_item.tsx | 4 +- .../collect_data_telemetry/tasks.ts | 11 +- .../__snapshots__/queries.test.ts.snap | 182 ++++++++---------- .../metrics/by_agent/shared/memory/index.ts | 24 ++- .../serverless/get_compute_usage_chart.ts | 2 +- .../fetch_service_paths_from_trace_ids.ts | 12 +- .../traces/get_aggregated_critical_path.ts | 41 ++-- .../server/routes/traces/get_trace_items.ts | 2 +- 8 files changed, 133 insertions(+), 145 deletions(-) diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_item.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_item.tsx index ed21a170c6a90..6260c37ffdc5a 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_item.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_item.tsx @@ -125,8 +125,10 @@ interface IWaterfallItemProps { function PrefixIcon({ item }: { item: IWaterfallSpanOrTransaction }) { switch (item.docType) { case 'span': { + const spanType = item.doc.span.type || ''; + // icon for database spans - const isDbType = item.doc.span.type.startsWith('db'); + const isDbType = spanType.startsWith('db'); if (isDbType) { return ; } diff --git a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts index 570328da84ea0..7bf03245d039e 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts @@ -230,10 +230,9 @@ export const tasks: TelemetryTask[] = [ { terms: { script: ` - if (doc['transaction.type'].value == 'page-load' && doc['user_agent.name'].size() > 0) { - return doc['user_agent.name'].value; + if ($('transaction.type', '') == 'page-load') { + return $('user_agent.name', null); } - return null; `, missing_bucket: true, @@ -242,7 +241,7 @@ export const tasks: TelemetryTask[] = [ // transaction.root { terms: { - script: `return doc['parent.id'].size() == 0`, + script: `return $('parent.id', '') == ''`, missing_bucket: true, }, }, @@ -259,8 +258,8 @@ export const tasks: TelemetryTask[] = [ { terms: { script: ` - if (doc['transaction.type'].value == 'page-load' && doc['client.geo.country_iso_code'].size() > 0) { - return doc['client.geo.country_iso_code'].value; + if ($('transaction.type', '') == 'page-load') { + return $('client.geo.country_iso_code', null); } return null; `, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/metrics/__snapshots__/queries.test.ts.snap b/x-pack/plugins/observability_solution/apm/server/routes/metrics/__snapshots__/queries.test.ts.snap index 44e4c6335ec7c..3fac4d595a274 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/metrics/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/observability_solution/apm/server/routes/metrics/__snapshots__/queries.test.ts.snap @@ -206,15 +206,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; - + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -231,15 +230,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -258,15 +256,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -283,15 +280,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -756,15 +752,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -781,15 +776,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -808,15 +802,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -833,15 +826,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -1302,15 +1294,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -1327,15 +1318,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -1354,15 +1344,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, @@ -1379,15 +1368,14 @@ Object { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = 'system.process.cgroup.memory.mem.limit.bytes'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['system.memory.total'].value; - - double used = doc['system.process.cgroup.memory.mem.usage.bytes'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('system.process.cgroup.memory.mem.limit.bytes', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('system.memory.total', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('system.process.cgroup.memory.mem.usage.bytes', 0); return used / total; ", }, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/observability_solution/apm/server/routes/metrics/by_agent/shared/memory/index.ts index 6fe3475abec84..af3e7301f8e61 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/metrics/by_agent/shared/memory/index.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/metrics/by_agent/shared/memory/index.ts @@ -56,12 +56,11 @@ export const systemMemory = { script: { lang: 'painless', source: ` - if(doc.containsKey('${METRIC_SYSTEM_FREE_MEMORY}') && doc.containsKey('${METRIC_SYSTEM_TOTAL_MEMORY}')){ - double freeMemoryValue = doc['${METRIC_SYSTEM_FREE_MEMORY}'].value; - double totalMemoryValue = doc['${METRIC_SYSTEM_TOTAL_MEMORY}'].value; - return 1 - freeMemoryValue / totalMemoryValue + def freeMemory = (double)$('${METRIC_SYSTEM_FREE_MEMORY}', 0); + def totalMemory = (double)$('${METRIC_SYSTEM_TOTAL_MEMORY}', -1); + if (freeMemory >= 0 && totalMemory > 0) { + return 1 - freeMemory / totalMemory; } - return null; `, }, @@ -87,15 +86,14 @@ export const cgroupMemory = { */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; - String limitKey = '${METRIC_CGROUP_MEMORY_LIMIT_BYTES}'; - - //Should use cgropLimit when value is not empty and not equals to the max limit value. - boolean useCgroupLimit = doc.containsKey(limitKey) && !doc[limitKey].empty && doc[limitKey].value != CGROUP_LIMIT_MAX_VALUE; - - double total = useCgroupLimit ? doc[limitKey].value : doc['${METRIC_SYSTEM_TOTAL_MEMORY}'].value; - - double used = doc['${METRIC_CGROUP_MEMORY_USAGE_BYTES}'].value; + //Should use cgroupLimit when value is not empty and not equals to the max limit value. + double cgroupLimit = $('${METRIC_CGROUP_MEMORY_LIMIT_BYTES}', 0); + double total = (double)((cgroupLimit != 0 && cgroupLimit != CGROUP_LIMIT_MAX_VALUE) ? cgroupLimit : $('${METRIC_SYSTEM_TOTAL_MEMORY}', 0)); + if (total <= 0) { + return null; + } + double used = (double)$('${METRIC_CGROUP_MEMORY_USAGE_BYTES}', 0); return used / total; `, }, diff --git a/x-pack/plugins/observability_solution/apm/server/routes/metrics/serverless/get_compute_usage_chart.ts b/x-pack/plugins/observability_solution/apm/server/routes/metrics/serverless/get_compute_usage_chart.ts index 60ff09131a868..3cdb1469295a9 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/metrics/serverless/get_compute_usage_chart.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/metrics/serverless/get_compute_usage_chart.ts @@ -25,7 +25,7 @@ import { convertComputeUsageToGbSec } from './helper'; export const computeUsageAvgScript = { avg: { - script: `return doc['${METRIC_SYSTEM_TOTAL_MEMORY}'].value * doc['${FAAS_BILLED_DURATION}'].value`, + script: `return $('${METRIC_SYSTEM_TOTAL_MEMORY}', 0) * $('${FAAS_BILLED_DURATION}', 0)`, }, }; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/service_map/fetch_service_paths_from_trace_ids.ts b/x-pack/plugins/observability_solution/apm/server/routes/service_map/fetch_service_paths_from_trace_ids.ts index ad37caa865fb2..5467606954844 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/service_map/fetch_service_paths_from_trace_ids.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/service_map/fetch_service_paths_from_trace_ids.ts @@ -76,18 +76,18 @@ export async function fetchServicePathsFromTraceIds({ map_script: { lang: 'painless', source: `def id; - if (!doc['span.id'].empty) { - id = doc['span.id'].value; - } else { - id = doc['transaction.id'].value; + id = $('span.id', null); + if (id == null) { + id = $('transaction.id', null); } def copy = new HashMap(); copy.id = id; for(key in state.fieldsToCopy) { - if (!doc[key].empty) { - copy[key] = doc[key].value; + def value = $(key, null); + if (value != null) { + copy[key] = value; } } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_aggregated_critical_path.ts b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_aggregated_critical_path.ts index f382085ad8dcc..6a3af27aa838d 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_aggregated_critical_path.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_aggregated_critical_path.ts @@ -123,38 +123,39 @@ export async function getAggregatedCriticalPath({ double duration; def operationMetadata = [ - "service.name": doc['service.name'].value, - "processor.event": doc['processor.event'].value, - "agent.name": doc['agent.name'].value + "service.name": $('service.name', ''), + "processor.event": $('processor.event', ''), + "agent.name": $('agent.name', '') ]; - def isSpan = !doc['span.id'].empty && !doc['span.name'].empty; - - if (isSpan) { - id = doc['span.id'].value; - operationMetadata.put('span.name', doc['span.name'].value); - if (!doc['span.type'].empty) { - operationMetadata.put('span.type', doc['span.type'].value); + def spanName = $('span.name', null); + id = $('span.id', null); + if (id != null && spanName != null) { + operationMetadata.put('span.name', spanName); + def spanType = $('span.type', ''); + if (spanType != '') { + operationMetadata.put('span.type', spanType); } - if (!doc['span.subtype'].empty) { - operationMetadata.put('span.subtype', doc['span.subtype'].value); + def spanSubtype = $('span.subtype', ''); + if (spanSubtype != '') { + operationMetadata.put('span.subtype', spanSubtype); } - duration = doc['span.duration.us'].value; + duration = $('span.duration.us', 0); } else { - id = doc['transaction.id'].value; - operationMetadata.put('transaction.name', doc['transaction.name'].value); - operationMetadata.put('transaction.type', doc['transaction.type'].value); - duration = doc['transaction.duration.us'].value; + id = $('transaction.id', ''); + operationMetadata.put('transaction.name', $('transaction.name', '')); + operationMetadata.put('transaction.type', $('transaction.type', '')); + duration = $('transaction.duration.us', 0); } String operationId = toHash(operationMetadata); def map = [ - "traceId": doc['trace.id'].value, + "traceId": $('trace.id', ''), "id": id, - "parentId": doc['parent.id'].empty ? null : doc['parent.id'].value, + "parentId": $('parent.id', null), "operationId": operationId, - "timestamp": doc['timestamp.us'].value, + "timestamp": $('timestamp.us', 0), "duration": duration ]; diff --git a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts index da84cc208eddf..d38a49745653a 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/traces/get_trace_items.ts @@ -273,7 +273,7 @@ async function getTraceDocsPerPage({ type: 'number', script: { lang: 'painless', - source: `if (doc['${TRANSACTION_DURATION}'].size() > 0) { return doc['${TRANSACTION_DURATION}'].value } else { return doc['${SPAN_DURATION}'].value }`, + source: `$('${TRANSACTION_DURATION}', $('${SPAN_DURATION}', 0))`, }, order: 'desc', }, From 6ea9eb1079cebd136ae1ca1a46fe741941610192 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Fri, 17 May 2024 10:38:45 +0200 Subject: [PATCH 31/71] [Synthetics] Remove isNew flag (#183266) ## Summary Synthetics is not new anymore !! image --- .../journeys/add_monitor.journey.ts | 10 ++++---- .../default_status_alert.journey.ts | 6 +---- .../journeys/alerting_default.journey.ts | 7 +----- .../journeys/data_retention.journey.ts | 4 +-- .../e2e/synthetics/journeys/detail_flyout.ts | 5 +--- .../journeys/getting_started.journey.ts | 5 +--- .../journeys/global_parameters.journey.ts | 5 +--- .../journeys/management_list.journey.ts | 5 +--- .../monitor_summary.journey.ts | 6 +---- .../monitor_form_validation.journey.ts | 6 +---- .../journeys/monitor_selector.journey.ts | 5 +--- .../journeys/overview_scrolling.journey.ts | 5 +--- .../journeys/overview_search.journey.ts | 5 +--- .../journeys/overview_sorting.journey.ts | 5 +--- .../journeys/private_locations.journey.ts | 5 +--- .../project_monitor_read_only.journey.ts | 5 +--- .../journeys/step_details.journey.ts | 6 +---- .../journeys/test_now_mode.journey.ts | 5 +--- .../journeys/test_run_details.journey.ts | 6 +---- .../page_objects/synthetics_app.tsx | 25 ++++++++++++++----- .../synthetics/public/plugin.ts | 1 - 21 files changed, 42 insertions(+), 90 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/add_monitor.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/add_monitor.journey.ts index e18b5cac11e2f..6e317150907ca 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/add_monitor.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/add_monitor.journey.ts @@ -6,7 +6,6 @@ */ import { v4 as uuidv4 } from 'uuid'; import { journey, step, expect, Page } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { FormMonitorType } from '../../../common/runtime_types'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; @@ -147,10 +146,11 @@ const createMonitorJourney = ({ journey( `SyntheticsAddMonitor - ${monitorName}`, async ({ page, params }: { page: Page; params: any }) => { - page.setDefaultTimeout(60 * 1000); - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ + page, + kibanaUrl: params.kibanaUrl, + params, + }); step('Go to monitor management', async () => { await syntheticsApp.navigateToMonitorManagement(true); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alert_rules/default_status_alert.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alert_rules/default_status_alert.journey.ts index 35b2bbbbef5f6..70327eb614ba7 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alert_rules/default_status_alert.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alert_rules/default_status_alert.journey.ts @@ -10,15 +10,11 @@ import { byTestId } from '@kbn/ux-plugin/e2e/journeys/utils'; import { RetryService } from '@kbn/ftr-common-functional-services'; import { v4 as uuidv4 } from 'uuid'; import { getReasonMessage } from '../../../../server/alert_rules/status_rule/message_utils'; -import { recordVideo } from '../../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics_app'; import { SyntheticsServices } from '../services/synthetics_services'; journey(`DefaultStatusAlert`, async ({ page, params }) => { - recordVideo(page); - - page.setDefaultTimeout(60 * 1000); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts index 96e91f81ff9fa..540a8e584cc5e 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/alerting_default.journey.ts @@ -6,17 +6,12 @@ */ import { journey, step, expect, before, after } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { byTestId } from '../../helpers/utils'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { cleanSettings } from './services/settings'; journey('AlertingDefaults', async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); - - page.setDefaultTimeout(60 * 1000); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); before(async () => { await cleanSettings(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/data_retention.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/data_retention.journey.ts index 1e0c00f42a92f..7f7a395b09b36 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/data_retention.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/data_retention.journey.ts @@ -13,9 +13,7 @@ import { byTestId, assertText } from '../../helpers/utils'; let page1: Page; journey(`DataRetentionPage`, async ({ page, params }) => { - page.setDefaultTimeout(60 * 1000); - recordVideo(page); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const getService = params.getService; const retry: RetryService = getService('retry'); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/detail_flyout.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/detail_flyout.ts index 4204e68099042..037ad7bbb651a 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/detail_flyout.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/detail_flyout.ts @@ -6,13 +6,10 @@ */ import { expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey('TestMonitorDetailFlyout', async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const monitorName = 'test-flyout-http-monitor'; step('Go to monitor-management', async () => { diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/getting_started.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/getting_started.journey.ts index abb66db15d601..78261c08e4415 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/getting_started.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/getting_started.journey.ts @@ -6,14 +6,11 @@ */ import { journey, step, expect, before, Page } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { cleanTestMonitors } from './services/add_monitor'; journey(`Getting Started Page`, async ({ page, params }: { page: Page; params: any }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const createBasicMonitor = async () => { await syntheticsApp.fillFirstMonitorDetails({ diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts index d5581e51b1833..3ff9423180686 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/global_parameters.journey.ts @@ -7,14 +7,11 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { byTestId } from '../../helpers/utils'; -import { recordVideo } from '../../helpers/record_video'; import { cleanTestParams } from './services/add_monitor'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey(`GlobalParameters`, async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); before(async () => { await cleanTestParams(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/management_list.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/management_list.journey.ts index b3a4db77680e0..c81fe084194d5 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/management_list.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/management_list.journey.ts @@ -7,7 +7,6 @@ import { journey, step, expect, before, after } from '@elastic/synthetics'; import { byTestId } from '../../helpers/utils'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -16,9 +15,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey(`MonitorManagementList`, async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const testMonitor1 = 'Test monitor 1'; const testMonitor2 = 'Test monitor 2'; const testMonitor3 = 'Test monitor 3'; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_details_page/monitor_summary.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_details_page/monitor_summary.journey.ts index 34b4e0ca985f7..46fa7837ded99 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_details_page/monitor_summary.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_details_page/monitor_summary.journey.ts @@ -9,15 +9,11 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { byTestId } from '@kbn/ux-plugin/e2e/journeys/utils'; import { RetryService } from '@kbn/ftr-common-functional-services'; import moment from 'moment'; -import { recordVideo } from '../../../helpers/record_video'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics_app'; import { SyntheticsServices } from '../services/synthetics_services'; journey(`MonitorSummaryTab`, async ({ page, params }) => { - recordVideo(page); - - page.setDefaultTimeout(60 * 1000); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_form_validation.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_form_validation.journey.ts index c34ea3fa7c8cb..7fdce8d921055 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_form_validation.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_form_validation.journey.ts @@ -6,7 +6,6 @@ */ import { expect, journey, Page, step } from '@elastic/synthetics'; import { FormMonitorType } from '../../../common/runtime_types'; -import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { isEuiFormFieldInValid, @@ -362,10 +361,7 @@ const exitingMonitorConfig = { journey( `SyntheticsAddMonitor - Validation Test`, async ({ page, params }: { page: Page; params: any }) => { - page.setDefaultTimeout(60 * 1000); - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); step('Go to monitor management', async () => { await syntheticsApp.navigateToMonitorManagement(true); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_selector.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_selector.journey.ts index 35e4a8996d5b3..73b7e6e53ca1d 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_selector.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/monitor_selector.journey.ts @@ -7,7 +7,6 @@ import { journey, step, expect, before, after } from '@elastic/synthetics'; import { byTestId } from '../../helpers/utils'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -16,9 +15,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey(`MonitorSelector`, async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const testMonitor1 = 'Test monitor 1'; const testMonitor2 = 'Test monitor 2'; const testMonitor3 = 'Test monitor 3'; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts index 7bec9a7a4b389..30b8a4f456a0d 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_scrolling.journey.ts @@ -7,7 +7,6 @@ import { before, after, expect, journey, step } from '@elastic/synthetics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -16,9 +15,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey('OverviewScrolling', async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const retry: RetryService = params.getService('retry'); const listOfRequests: string[] = []; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_search.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_search.journey.ts index a1729fca0faa3..790cb06f5f5c2 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_search.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_search.journey.ts @@ -7,7 +7,6 @@ import { before, expect, journey, step } from '@elastic/synthetics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -16,11 +15,9 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey('Overview Search', async ({ page, params }) => { - recordVideo(page); - const retry: RetryService = params.getService('retry'); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const elasticJourney = 'Elastic journey'; const cnnJourney = 'CNN journey'; const googleJourney = 'Google journey'; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_sorting.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_sorting.journey.ts index 018df450628ef..cd65a67f1e22a 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_sorting.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/overview_sorting.journey.ts @@ -6,7 +6,6 @@ */ import { before, expect, journey, step } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanTestMonitors, @@ -15,9 +14,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey('OverviewSorting', async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const testMonitor1 = 'acb'; // second alpha, first created const testMonitor2 = 'aCd'; // third alpha, second created const testMonitor3 = 'Abc'; // first alpha, last created diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/private_locations.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/private_locations.journey.ts index b9a9b23d16ca7..9e6bb8352c35f 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/private_locations.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/private_locations.journey.ts @@ -8,7 +8,6 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { waitForLoadingToFinish } from '@kbn/ux-plugin/e2e/journeys/utils'; import { byTestId } from '../../helpers/utils'; -import { recordVideo } from '../../helpers/record_video'; import { addTestMonitor, cleanPrivateLocations, @@ -18,9 +17,7 @@ import { import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; journey(`PrivateLocationsSettings`, async ({ page, params }) => { - recordVideo(page); - - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); page.setDefaultTimeout(2 * 30000); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts index 2bfc97bb9f5f1..4c9d73f3be815 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/project_monitor_read_only.journey.ts @@ -7,18 +7,15 @@ import { after, before, expect, journey, step } from '@elastic/synthetics'; import { SyntheticsServices } from './services/synthetics_services'; -import { recordVideo } from '../../helpers/record_video'; import { cleanTestMonitors, enableMonitorManagedViaApi } from './services/add_monitor'; import { addTestMonitorProject } from './services/add_monitor_project'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { SyntheticsMonitor } from '../../../common/runtime_types'; journey('ProjectMonitorReadOnly', async ({ page, params }) => { - recordVideo(page); - const services = new SyntheticsServices(params); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); let originalMonitorConfiguration: SyntheticsMonitor | null; let monitorId: string; diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/step_details.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/step_details.journey.ts index 5d4c677b21718..07dab69c33ed8 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/step_details.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/step_details.journey.ts @@ -6,15 +6,11 @@ */ import { journey, step, before, after } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; journey(`StepDetailsPage`, async ({ page, params }) => { - recordVideo(page); - - page.setDefaultTimeout(60 * 1000); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts index ac931bbd1e725..2d8b4de8e5b40 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_now_mode.journey.ts @@ -7,15 +7,12 @@ import { journey, step, before, after, expect } from '@elastic/synthetics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -import { recordVideo } from '../../helpers/record_video'; import { byTestId } from '../../helpers/utils'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; journey(`TestNowMode`, async ({ page, params }) => { - page.setDefaultTimeout(60 * 1000); - recordVideo(page); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_run_details.journey.ts b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_run_details.journey.ts index 5c1af51caa18d..8bf7fd0f690d1 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_run_details.journey.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/journeys/test_run_details.journey.ts @@ -6,16 +6,12 @@ */ import { journey, step, before, after, expect } from '@elastic/synthetics'; -import { recordVideo } from '../../helpers/record_video'; import { byTestId } from '../../helpers/utils'; import { syntheticsAppPageProvider } from '../page_objects/synthetics_app'; import { SyntheticsServices } from './services/synthetics_services'; journey(`TestRunDetailsPage`, async ({ page, params }) => { - recordVideo(page); - - page.setDefaultTimeout(60 * 1000); - const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const syntheticsApp = syntheticsAppPageProvider({ page, kibanaUrl: params.kibanaUrl, params }); const services = new SyntheticsServices(params); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/page_objects/synthetics_app.tsx b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/page_objects/synthetics_app.tsx index 4e16479d10434..49aefae295b39 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/page_objects/synthetics_app.tsx +++ b/x-pack/plugins/observability_solution/synthetics/e2e/synthetics/page_objects/synthetics_app.tsx @@ -5,6 +5,8 @@ * 2.0. */ import { expect, Page } from '@elastic/synthetics'; +import { RetryService } from '@kbn/ftr-common-functional-services'; +import { recordVideo } from '../../helpers/record_video'; import { FormMonitorType } from '../../../common/runtime_types/monitor_management'; import { loginPageProvider } from '../../page_objects/login'; import { utilsPageProvider } from '../../page_objects/utils'; @@ -13,7 +15,15 @@ const SIXTY_SEC_TIMEOUT = { timeout: 60 * 1000, }; -export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kibanaUrl: string }) { +export function syntheticsAppPageProvider({ + page, + kibanaUrl, + params, +}: { + page: Page; + kibanaUrl: string; + params: Record; +}) { const remoteKibanaUrl = process.env.SYNTHETICS_REMOTE_KIBANA_URL; const remoteUsername = process.env.SYNTHETICS_REMOTE_KIBANA_USERNAME; const remotePassword = process.env.SYNTHETICS_REMOTE_KIBANA_PASSWORD; @@ -23,6 +33,10 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib const settingsPage = `${basePath}/app/synthetics/settings`; const addMonitor = `${basePath}/app/synthetics/add-monitor`; const overview = `${basePath}/app/synthetics`; + const retry: RetryService = params?.getService('retry'); + + recordVideo(page); + page.setDefaultTimeout(60 * 1000); return { ...loginPageProvider({ @@ -162,8 +176,10 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib async selectLocationsAddEdit({ locations }: { locations: string[] }) { for (let i = 0; i < locations.length; i++) { - await page.click(this.byTestId('syntheticsMonitorConfigLocations')); - await page.click(`text=${locations[i]}`); + await retry.try(async () => { + await page.click(this.byTestId('syntheticsMonitorConfigLocations')); + await page.click(`text=${locations[i]}`); + }); } }, @@ -278,9 +294,6 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib name, inlineScript, recorderScript, - params, - username, - password, apmServiceName, locations, }: { diff --git a/x-pack/plugins/observability_solution/synthetics/public/plugin.ts b/x-pack/plugins/observability_solution/synthetics/public/plugin.ts index 1d7044a79c6bf..7c343ef795776 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/plugin.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/plugin.ts @@ -238,7 +238,6 @@ function registerSyntheticsRoutesWithNavigation( path: OVERVIEW_ROUTE, matchFullPath: true, ignoreTrailingSlash: true, - isNewFeature: true, }, { label: i18n.translate('xpack.synthetics.certificatesPage.heading', { From 8a86be114381b4ee851329b6e5dd76dfc58dda91 Mon Sep 17 00:00:00 2001 From: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com> Date: Fri, 17 May 2024 11:11:11 +0200 Subject: [PATCH 32/71] [Connection Details] Integrate into the Search Solution (#183236) ## Summary Integrates the new Connection Details flyout into the Search solution page. Now, when clicking on the "Endpoints and API keys" button in the header, it will open the new flyout. ![image](https://github.com/elastic/kibana/assets/82822460/8463c752-75e9-4e8c-95a4-43796c9dcb37) ### Checklist Delete any items that are not applicable to this PR. - [ ] 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 - [ ] [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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] 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)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Multiple Spaces—unexpected behavior in non-default Kibana Space. | Low | High | Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces. | | Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. | High | Low | Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure. | | Code should gracefully handle cases when feature X or plugin Y are disabled. | Medium | High | Unit tests will verify that any feature flag or plugin combination still results in our service operational. | | [See more potential risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) | ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Release note Now, when clicking on the "Endpoints and API keys" button in the header, it will open the new flyout. ![image](https://github.com/elastic/kibana/assets/82822460/8463c752-75e9-4e8c-95a4-43796c9dcb37) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../shared/layout/endpoints_header_action.tsx | 240 +++--------------- .../plugins/enterprise_search/tsconfig.json | 1 + .../translations/translations/fr-FR.json | 6 - .../translations/translations/ja-JP.json | 6 - .../translations/translations/zh-CN.json | 6 - 5 files changed, 31 insertions(+), 228 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx index 394da346b2b7c..0085b913cdbb3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx @@ -5,225 +5,45 @@ * 2.0. */ -import React, { FC, PropsWithChildren } from 'react'; -import { useState, useEffect } from 'react'; +import React from 'react'; -import { css } from '@emotion/react'; -import { useValues, useActions } from 'kea'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFlyout, EuiHeaderLinks } from '@elastic/eui'; import { - EuiButtonEmpty, - EuiPopover, - EuiContextMenuPanel, - EuiContextMenuItem, - EuiText, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiCode, - EuiCopy, - EuiButtonIcon, - EuiBadge, - EuiHorizontalRule, - EuiButton, - EuiHeaderLinks, - useEuiTheme, -} from '@elastic/eui'; - + KibanaWiredConnectionDetailsProvider, + ConnectionDetailsFlyoutContent, +} from '@kbn/cloud/connection_details'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n-react'; - -import { FetchApiKeysAPILogic } from '../../enterprise_search_overview/api/fetch_api_keys_logic'; -import { CreateApiKeyFlyout } from '../api_key/create_api_key_flyout'; -import { KibanaLogic } from '../kibana'; import { EndpointIcon } from './endpoint_icon'; -export const EndpointsHeaderAction: FC> = ({ children }) => { - const [isPopoverOpen, setPopoverOpen] = useState(false); - const { cloud, esConfig, navigateToUrl } = useValues(KibanaLogic); - const { makeRequest } = useActions(FetchApiKeysAPILogic); - const { data } = useValues(FetchApiKeysAPILogic); - const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); - const { euiTheme } = useEuiTheme(); - - useEffect(() => makeRequest({}), []); - - const COPIED_LABEL = i18n.translate('xpack.enterpriseSearch.pageTemplate.apiKey.copied', { - defaultMessage: 'Copied', - }); - - const apiKeys = data?.api_keys || []; - const cloudId = cloud?.cloudId; - const elasticsearchEndpoint = esConfig.elasticsearch_host; - - const button = ( - setPopoverOpen(!isPopoverOpen)}> - {i18n.translate('xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel', { - defaultMessage: 'Endpoints & API keys', - })} - - ); +export const EndpointsHeaderAction: React.FC = ({ children }) => { + const [open, setOpen] = React.useState(false); return ( - - - {Boolean(children) && {children}} - - {isFlyoutOpen && setIsFlyoutOpen(false)} />} - setPopoverOpen(false)} - panelPaddingSize="none" - anchorPosition="downLeft" + <> + + + {!!children && {children}} + setOpen((x) => !x)} + data-test-subj="enterpriseSearchEndpointsHeaderActionEndpointsApiKeysButton" > - - - {i18n.translate( - 'xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint', - { - defaultMessage: 'Elasticsearch endpoint:', - } - )} - - - - - - - {elasticsearchEndpoint} - - - - - {(copy) => ( - - )} - - - - , - ...(Boolean(cloudId) - ? [ - - - {i18n.translate('xpack.enterpriseSearch.apiKey.cloudId', { - defaultMessage: 'Cloud ID:', - })} - - - - - - - {cloudId} - - - - - {(copy) => ( - - )} - - - - , - ] - : []), - - - - - 0 ? 'success' : 'warning'} - data-test-subj="api-keys-count-badge" - > - {apiKeys.length} - - ), - }} - /> - - - - - navigateToUrl('/app/management/security/api_keys', { - shouldNotCreateHref: true, - }) - } - /> - - - , - , - - { - setIsFlyoutOpen(true); - setPopoverOpen(false); - }} - data-test-subj="new-api-key-button" - fullWidth - > - - {i18n.translate('xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel', { - defaultMessage: 'New API key', - })} - - - , - ]} - /> - - - - + {i18n.translate('xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel', { + defaultMessage: 'Endpoints & API keys', + })} + + + + {open && ( + setOpen(false)} size={'s'}> + + + + + )} + ); }; diff --git a/x-pack/plugins/enterprise_search/tsconfig.json b/x-pack/plugins/enterprise_search/tsconfig.json index 6161461b48b89..56232cd73f256 100644 --- a/x-pack/plugins/enterprise_search/tsconfig.json +++ b/x-pack/plugins/enterprise_search/tsconfig.json @@ -75,6 +75,7 @@ "@kbn/deeplinks-search", "@kbn/react-kibana-context-theme", "@kbn/search-types", + "@kbn/cloud", "@kbn/try-in-console" ] } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 526bee1ed8169..48ab3c8c34bc2 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -13933,7 +13933,6 @@ "xpack.enterpriseSearch.crawler.deleteDomainModal.description": "Supprimer le domaine {domainUrl} de votre robot d'indexation. Cela supprimera également tous les points d'entrée et toutes les règles d'indexation que vous avez configurés. Tous les documents associés à ce domaine seront supprimés lors de la prochaine indexation. {thisCannotBeUndoneMessage}", "xpack.enterpriseSearch.crawler.entryPointsTable.emptyMessageDescription": "{link} pour spécifier un point d'entrée pour le robot d'indexation", "xpack.enterpriseSearch.deleteConnectorModal.li.myconnectornameRelatedIndexLabel": "{connectorName} (Index associé : {deleteModalIndexName})", - "xpack.enterpriseSearch.endpointsHeader.apiKey.activeKeys": "{number} clés d'API actives", "xpack.enterpriseSearch.errorConnectingState.cloudErrorMessage": "Les nœuds Enterprise Search fonctionnent-ils dans votre déploiement cloud ? {deploymentSettingsLink}", "xpack.enterpriseSearch.errorConnectingState.description1": "Impossible d'établir une connexion à Enterprise Search avec l'URL hôte {enterpriseSearchUrl} en raison de l'erreur suivante :", "xpack.enterpriseSearch.errorConnectingState.description2": "Vérifiez que l'URL hôte est correctement configurée dans {configFile}.", @@ -16150,12 +16149,7 @@ "xpack.enterpriseSearch.overview.gettingStarted.testConnection.description": "Envoyez une requête de test pour confirmer que votre client de langage et votre instance Elasticsearch sont opérationnels.", "xpack.enterpriseSearch.overview.gettingStarted.testConnection.title": "Tester votre connexion", "xpack.enterpriseSearch.overview.navTitle": "Aperçu", - "xpack.enterpriseSearch.overview.pageTemplate.apiKey.copyApiEndpoint": "Copier le point de terminaison Elasticsearch dans le presse-papiers", "xpack.enterpriseSearch.overview.setupCta.description": "Ajoutez des fonctions de recherche à votre application ou à votre organisation interne avec Elastic App Search et Workplace Search. Regardez la vidéo pour savoir ce qu'il est possible de faire lorsque la recherche est facilitée.", - "xpack.enterpriseSearch.pageTemplate.apiKey.copied": "Copié", - "xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint": "Point de terminaison Elasticsearch :", - "xpack.enterpriseSearch.pageTemplate.apiKey.manageLabel": "Gérer", - "xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel": "Nouvelle clé d'API", "xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel": "Points de terminaison et clés d'API", "xpack.enterpriseSearch.passwordLabel": "Mot de passe", "xpack.enterpriseSearch.pipeline.title": "Transformer et enrichir vos données", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e7c5c729dcd78..dac5c30126d51 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13912,7 +13912,6 @@ "xpack.enterpriseSearch.crawler.deleteDomainModal.description": "ドメイン{domainUrl}をクローラーから削除します。これにより、設定したすべてのエントリポイントとクロールルールも削除されます。このドメインに関連するすべてのドキュメントは、次回のクロールで削除されます。{thisCannotBeUndoneMessage}", "xpack.enterpriseSearch.crawler.entryPointsTable.emptyMessageDescription": "クローラーのエントリポイントを指定するには、{link}してください", "xpack.enterpriseSearch.deleteConnectorModal.li.myconnectornameRelatedIndexLabel": "{connectorName}(関連付けられたインデックス:{deleteModalIndexName})", - "xpack.enterpriseSearch.endpointsHeader.apiKey.activeKeys": "{number}個のアクティブなAPIキー", "xpack.enterpriseSearch.errorConnectingState.cloudErrorMessage": "クラウドデプロイのエンタープライズ サーチノードが実行中ですか?{deploymentSettingsLink}", "xpack.enterpriseSearch.errorConnectingState.description1": "次のエラーのため、ホストURL {enterpriseSearchUrl}では、エンタープライズ サーチへの接続を確立できません。", "xpack.enterpriseSearch.errorConnectingState.description2": "ホストURLが{configFile}で正しく構成されていることを確認してください。", @@ -16128,12 +16127,7 @@ "xpack.enterpriseSearch.overview.gettingStarted.testConnection.description": "テストリクエストを送信して、言語クライアントとElasticsearchインスタンスが起動し、実行中であることを確認してください。", "xpack.enterpriseSearch.overview.gettingStarted.testConnection.title": "接続をテスト", "xpack.enterpriseSearch.overview.navTitle": "概要", - "xpack.enterpriseSearch.overview.pageTemplate.apiKey.copyApiEndpoint": "Elasticsearchエンドポイントをクリップボードにコピーします。", "xpack.enterpriseSearch.overview.setupCta.description": "Elastic App Search および Workplace Search を使用して、アプリまたは社内組織に検索を追加できます。検索が簡単になるとどのような利点があるのかについては、動画をご覧ください。", - "xpack.enterpriseSearch.pageTemplate.apiKey.copied": "コピー完了", - "xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint": "Elasticsearchエンドポイント:", - "xpack.enterpriseSearch.pageTemplate.apiKey.manageLabel": "管理", - "xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel": "新しいAPIキー", "xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel": "エンドポイントとAPIキー", "xpack.enterpriseSearch.passwordLabel": "パスワード", "xpack.enterpriseSearch.pipeline.title": "データの変換とエンリッチ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b33c76617db9e..7e2e9e2e28f1e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13938,7 +13938,6 @@ "xpack.enterpriseSearch.crawler.deleteDomainModal.description": "从网络爬虫中移除域 {domainUrl}。这还会删除您已设置的所有入口点和爬网规则。将在下次爬网时移除与此域相关的任何文档。{thisCannotBeUndoneMessage}", "xpack.enterpriseSearch.crawler.entryPointsTable.emptyMessageDescription": "{link}以指定网络爬虫的入口点", "xpack.enterpriseSearch.deleteConnectorModal.li.myconnectornameRelatedIndexLabel": "{connectorName}(相关索引:{deleteModalIndexName})", - "xpack.enterpriseSearch.endpointsHeader.apiKey.activeKeys": "{number} 个活动 API 密钥", "xpack.enterpriseSearch.errorConnectingState.cloudErrorMessage": "您的云部署是否正在运行 Enterprise Search 节点?{deploymentSettingsLink}", "xpack.enterpriseSearch.errorConnectingState.description1": "由于以下错误,我们无法与主机 URL {enterpriseSearchUrl} 的 Enterprise Search 建立连接:", "xpack.enterpriseSearch.errorConnectingState.description2": "确保在 {configFile} 中已正确配置主机 URL。", @@ -16155,12 +16154,7 @@ "xpack.enterpriseSearch.overview.gettingStarted.testConnection.description": "发送测试请求,以确认您的语言客户端和 Elasticsearch 实例已启动并正在运行。", "xpack.enterpriseSearch.overview.gettingStarted.testConnection.title": "测试您的连接", "xpack.enterpriseSearch.overview.navTitle": "概览", - "xpack.enterpriseSearch.overview.pageTemplate.apiKey.copyApiEndpoint": "复制 Elasticsearch 终端到剪贴板。", "xpack.enterpriseSearch.overview.setupCta.description": "通过 Elastic App Search 和 Workplace Search,将搜索添加到您的应用或内部组织中。观看视频,了解方便易用的搜索功能可以帮您做些什么。", - "xpack.enterpriseSearch.pageTemplate.apiKey.copied": "已复制", - "xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint": "Elasticsearch 终端:", - "xpack.enterpriseSearch.pageTemplate.apiKey.manageLabel": "管理", - "xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel": "新 API 密钥", "xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel": "终端和 API 密钥", "xpack.enterpriseSearch.passwordLabel": "密码", "xpack.enterpriseSearch.pipeline.title": "转换和扩充数据", From 4a3b74a167306e322d8f66d0381b1e87c5df6bcc Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Fri, 17 May 2024 11:11:51 +0200 Subject: [PATCH 33/71] `@kbn/std`: fix `pick` method to work for objects without prototypes (#183697) ## Summary Related to https://github.com/elastic/kibana/issues/7104 Extracted from https://github.com/elastic/kibana/pull/123748 `http2` headers are created via `Object.create(null)`, which causes issues with the way our `pick` method was implemented. PR fixes it. --- packages/kbn-std/src/pick.test.ts | 35 +++++++++++++++++++++++++++++++ packages/kbn-std/src/pick.ts | 3 +-- 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 packages/kbn-std/src/pick.test.ts diff --git a/packages/kbn-std/src/pick.test.ts b/packages/kbn-std/src/pick.test.ts new file mode 100644 index 0000000000000..b58c8b1e9a9f8 --- /dev/null +++ b/packages/kbn-std/src/pick.test.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 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 { pick } from './pick'; + +describe('pick', () => { + it('works with object created inline', () => { + const obj = { foo: 'bar', hello: 'dolly' }; + + const result = pick(obj, ['foo']); + expect(result).toEqual({ foo: 'bar' }); + }); + + it('works with objects created via Object.create(null)', () => { + const obj = Object.create(null); + Object.assign(obj, { foo: 'bar', hello: 'dolly' }); + + const result = pick(obj, ['foo']); + expect(result).toEqual({ foo: 'bar' }); + }); + + it('does not pick properties from the prototype', () => { + const proto = { prot: 'o' }; + const obj = Object.create(proto); + Object.assign(obj, { foo: 'bar', hello: 'dolly' }); + + const result = pick(obj, ['foo', 'prot']); + expect(result).toEqual({ foo: 'bar' }); + }); +}); diff --git a/packages/kbn-std/src/pick.ts b/packages/kbn-std/src/pick.ts index c8347aadb219a..8d01c80caee6d 100644 --- a/packages/kbn-std/src/pick.ts +++ b/packages/kbn-std/src/pick.ts @@ -8,10 +8,9 @@ export function pick(obj: T, keys: readonly K[]): Pick { return keys.reduce((acc, key) => { - if (obj.hasOwnProperty(key)) { + if (Object.hasOwn(obj, key)) { acc[key] = obj[key]; } - return acc; }, {} as Pick); } From b0f8ee7f5e5a755e03723f79e09badbcae374dc0 Mon Sep 17 00:00:00 2001 From: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com> Date: Fri, 17 May 2024 11:14:49 +0200 Subject: [PATCH 34/71] [Connection Details] Show "Manage api keys" link when no permissions (#183599) ## Summary This change show the "Manage API keys" link on the Connection Details flyout "API key" tab when user does not have permissions to edit their API keys: ![image](https://github.com/elastic/kibana/assets/82822460/89f5e1a4-737e-401c-ba9e-314ded050bd2) ### 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] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../views/missing_permissions_panel.tsx | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/cloud/connection_details/tabs/api_keys_tab/views/missing_permissions_panel.tsx b/packages/cloud/connection_details/tabs/api_keys_tab/views/missing_permissions_panel.tsx index 5e2d5dd2da830..fcc58d14443c7 100644 --- a/packages/cloud/connection_details/tabs/api_keys_tab/views/missing_permissions_panel.tsx +++ b/packages/cloud/connection_details/tabs/api_keys_tab/views/missing_permissions_panel.tsx @@ -7,25 +7,32 @@ */ import * as React from 'react'; -import { EuiCallOut } from '@elastic/eui'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { ManageKeysLink } from '../components/manage_keys_link'; export const MissingPermissionsPanel: React.FC = () => { return ( - -

- {i18n.translate('cloud.connectionDetails.tabs.apiKeys.missingPermPanel.description', { - defaultMessage: - 'Your assigned role does not have the necessary permissions to create an API key. ' + - 'Please contact your administrator.', + <> + - + > +

+ {i18n.translate('cloud.connectionDetails.tabs.apiKeys.missingPermPanel.description', { + defaultMessage: + 'Your assigned role does not have the necessary permissions to create an API key. ' + + 'Please contact your administrator.', + })} +

+
+ + + + + ); }; From 297b5bd5573169be2e49b647a4334838a1091247 Mon Sep 17 00:00:00 2001 From: Mykola Harmash Date: Fri, 17 May 2024 12:15:36 +0200 Subject: [PATCH 35/71] [Onboarding] Fix bash script URL in the quickstart snippet (#183596) Fixes https://github.com/elastic/kibana/issues/183594 Add logic to take into account absolute CDN URLs when generating bash script download link. --- .../server/routes/logs/route.ts | 10 +++++----- .../configs/index.ts | 4 ++-- .../tests/logs/environment.spec.ts | 6 ++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts index c7a411441658e..b46b1508ed21b 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts +++ b/x-pack/plugins/observability_solution/observability_onboarding/server/routes/logs/route.ts @@ -58,12 +58,12 @@ const installShipperSetupRoute = createObservabilityOnboardingServerRoute({ core.setup.http.basePath.publicBaseUrl ?? // priority given to server.publicBaseUrl plugins.cloud?.setup?.kibanaUrl ?? // then cloud id getFallbackKibanaUrl(coreStart); // falls back to local network binding - const installScriptPath = coreStart.http.staticAssets.getPluginAssetHref( - 'standalone_agent_setup.sh' - ); + const scriptDownloadUrl = new URL( + coreStart.http.staticAssets.getPluginAssetHref('standalone_agent_setup.sh'), + kibanaUrl + ).toString(); - const scriptDownloadUrl = `${kibanaUrl}${installScriptPath}`; - const apiEndpoint = `${kibanaUrl}/internal/observability_onboarding`; + const apiEndpoint = new URL(`${kibanaUrl}/internal/observability_onboarding`).toString(); return { apiEndpoint, diff --git a/x-pack/test/observability_onboarding_api_integration/configs/index.ts b/x-pack/test/observability_onboarding_api_integration/configs/index.ts index 8708ef8235d55..57e56a9b022a7 100644 --- a/x-pack/test/observability_onboarding_api_integration/configs/index.ts +++ b/x-pack/test/observability_onboarding_api_integration/configs/index.ts @@ -8,11 +8,11 @@ import { mapValues } from 'lodash'; import { createTestConfig, CreateTestConfig } from '../common/config'; -export const MOCKED_PUBLIC_BASE_URL = 'http://mockedPublicBaseUrl'; +export const MOCKED_PUBLIC_BASE_URL = 'http://mockedpublicbaseurl'; // my.mocked.domain$myMockedEsUr$myKibanaMockedUrl export const MOCKED_ENCODED_CLOUD_ID = 'bXkubW9ja2VkLmRvbWFpbiRteU1vY2tlZEVzVXJsJG15TW9ja2VkS2liYW5hVXJs'; -export const MOCKED_KIBANA_URL = 'https://myMockedKibanaUrl.my.mocked.domain:443'; +export const MOCKED_KIBANA_URL = 'https://mymockedkibanaurl.my.mocked.domain'; export const observabilityOnboardingDebugLogger = { name: 'plugins.observabilityOnboarding', diff --git a/x-pack/test/observability_onboarding_api_integration/tests/logs/environment.spec.ts b/x-pack/test/observability_onboarding_api_integration/tests/logs/environment.spec.ts index 41a14061b4059..2f7b514d54a57 100644 --- a/x-pack/test/observability_onboarding_api_integration/tests/logs/environment.spec.ts +++ b/x-pack/test/observability_onboarding_api_integration/tests/logs/environment.spec.ts @@ -29,7 +29,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(request.body.scriptDownloadUrl).to.match( new RegExp( - `${MOCKED_PUBLIC_BASE_URL}/.+?/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh` + `${MOCKED_PUBLIC_BASE_URL}/.+?/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh`, + 'i' ) ); }); @@ -45,7 +46,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); expect(request.body.scriptDownloadUrl).to.match( new RegExp( - `${MOCKED_KIBANA_URL}/.+?/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh` + `${MOCKED_KIBANA_URL}/.+?/plugins/observabilityOnboarding/assets/standalone_agent_setup.sh`, + 'i' ) ); }); From 012d62fe09d70b7c5c8f5a0f0c3141d6059bff4c Mon Sep 17 00:00:00 2001 From: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com> Date: Fri, 17 May 2024 12:33:06 +0200 Subject: [PATCH 36/71] [Connection Details] Adds telemetry for user actions (#183265) ## Summary Closes https://github.com/elastic/kibana/issues/180869 Add tracking for the following user actions: - [x] "Learn more" click - [x] Tab switches - [x] Copy endpoint - [x] Show cloud ID - [x] Copy cloud ID - [x] Create API key - [x] "Manage API keys" click - [x] Key encoding change - [x] API key copy The emitted telemetry events are as follows: - `connection_details_learn_more_clicked` - `connection_details_tab_switched` - Has `tab` paramter - `connection_details_copy_endpoint_url_clicked` - `connection_details_show_cloud_id_toggled` - `connection_details_copy_cloud_id_clicked` - `connection_details_new_api_key_created` - `connection_details_manage_api_keys_clicked` - `connection_details_key_encoding_changed` - Has `format` paramter - `connection_details_copy_api_key_clicked` - Has `format` paramter ### Checklist Delete any items that are not applicable to this PR. - [ ] 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 - [ ] [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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] 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)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Multiple Spaces—unexpected behavior in non-default Kibana Space. | Low | High | Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces. | | Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. | High | Low | Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure. | | Code should gracefully handle cases when feature X or plugin Y are disabled. | Medium | High | Unit tests will verify that any feature flag or plugin combination still results in our service operational. | | [See more potential risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) | ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/copy_input/copy_input.tsx | 8 ++- ...nection_details_flyout_content.stories.tsx | 3 +- .../connection_details_flyout_content.tsx | 13 +++- .../cloud/connection_details/kibana/global.ts | 1 + .../kibana_connection_details_provider.tsx | 48 +++++++++++++- packages/cloud/connection_details/service.ts | 14 +++- packages/cloud/connection_details/stories.tsx | 11 +++- .../components/manage_keys_link.tsx | 5 +- .../views/success_form/success_form.tsx | 1 + .../success_form/success_form_controlled.tsx | 4 +- .../tabs/endpoints_tab/endpoints_tab.tsx | 22 ++++++- .../rows/cloud_id_row/cloud_id_row.tsx | 23 ++++--- .../endpoints_tab/rows/endpoints_url_row.tsx | 5 +- packages/cloud/connection_details/types.ts | 17 +++++ .../cloud_links/public/plugin.test.ts | 2 +- .../cloud_links/public/plugin.tsx | 65 ++++++++++++++++++- 16 files changed, 214 insertions(+), 28 deletions(-) diff --git a/packages/cloud/connection_details/components/copy_input/copy_input.tsx b/packages/cloud/connection_details/components/copy_input/copy_input.tsx index 6111208b06148..ac82407fe1588 100644 --- a/packages/cloud/connection_details/components/copy_input/copy_input.tsx +++ b/packages/cloud/connection_details/components/copy_input/copy_input.tsx @@ -12,9 +12,10 @@ import { i18n } from '@kbn/i18n'; export interface CopyInputProps { value: string; + onCopyClick?: React.MouseEventHandler; } -export const CopyInput: React.FC = ({ value }) => { +export const CopyInput: React.FC = ({ value, onCopyClick }) => { const textRef = React.useRef(null); return ( @@ -52,7 +53,10 @@ export const CopyInput: React.FC = ({ value }) => { {(copy) => ( ) => { + onCopyClick?.(event); + copy(); + }} iconType="copyClipboard" size="m" color={'text'} diff --git a/packages/cloud/connection_details/connection_details_flyout_content.stories.tsx b/packages/cloud/connection_details/connection_details_flyout_content.stories.tsx index 6893acfd8b8b3..542a9f343691b 100644 --- a/packages/cloud/connection_details/connection_details_flyout_content.stories.tsx +++ b/packages/cloud/connection_details/connection_details_flyout_content.stories.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiFlyout } from '@elastic/eui'; +import { action } from '@storybook/addon-actions'; import { StoriesProvider, StoriesProviderKeyCreationError, @@ -22,7 +23,7 @@ export default { export const Default = () => { return ( {}}> - + diff --git a/packages/cloud/connection_details/connection_details_flyout_content.tsx b/packages/cloud/connection_details/connection_details_flyout_content.tsx index 9cbe8042caa42..31dce6dc4f848 100644 --- a/packages/cloud/connection_details/connection_details_flyout_content.tsx +++ b/packages/cloud/connection_details/connection_details_flyout_content.tsx @@ -17,11 +17,12 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ConnectionDetails } from './connection_details'; -import { useConnectionDetailsOpts } from './context'; +import { useConnectionDetailsOpts, useConnectionDetailsService } from './context'; import { Tabs } from './tabs'; export const ConnectionDetailsFlyoutContent: React.FC = () => { const ctx = useConnectionDetailsOpts(); + const service = useConnectionDetailsService(); const header = ( @@ -39,7 +40,15 @@ export const ConnectionDetailsFlyoutContent: React.FC = () => { defaultMessage: 'Connect to the Elasticsearch API by using the following details.', })}{' '} {!!ctx.links?.learnMore && ( - + // Below onClick is used only for telemetry, but `href` is the real + // semantic action. + // eslint-disable-next-line @elastic/eui/href-or-on-click + service.emitTelemetryEvent(['learn_more_clicked'])} + > {i18n.translate('cloud.connectionDetails.learnMoreButtonLabel', { defaultMessage: 'Learn more', })} diff --git a/packages/cloud/connection_details/kibana/global.ts b/packages/cloud/connection_details/kibana/global.ts index af3767d12ad77..0a2ede8e5157d 100644 --- a/packages/cloud/connection_details/kibana/global.ts +++ b/packages/cloud/connection_details/kibana/global.ts @@ -19,6 +19,7 @@ export interface ConnectionDetailsGlobalDependencies { http: CoreStart['http']; application: CoreStart['application']; overlays: CoreStart['overlays']; + analytics?: CoreStart['analytics']; }; plugins: { cloud?: CloudStart; diff --git a/packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx b/packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx index 38bc354e9c912..a8cc79dfcd9d2 100644 --- a/packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx +++ b/packages/cloud/connection_details/kibana/kibana_connection_details_provider.tsx @@ -17,7 +17,7 @@ import { useAsyncMemo } from '../hooks/use_async_memo'; const createOpts = async (props: KibanaConnectionDetailsProviderProps) => { const { options, start } = props; - const { http, docLinks } = start.core; + const { http, docLinks, analytics } = start.core; const locator = start.plugins?.share?.url?.locators.get('MANAGEMENT_APP_LOCATOR'); const manageKeysLink = await locator?.getUrl({ sectionId: 'security', appId: 'api_keys' }); const result: ConnectionDetailsOpts = { @@ -68,6 +68,51 @@ const createOpts = async (props: KibanaConnectionDetailsProviderProps) => { hasPermission: async () => true, ...options?.apiKeys, }, + onTelemetryEvent: (event) => { + if (!analytics) return; + switch (event[0]) { + case 'learn_more_clicked': { + analytics.reportEvent('connection_details_learn_more_clicked', {}); + break; + } + case 'tab_switched': { + analytics.reportEvent('connection_details_tab_switched', { tab: event[1]!.tab }); + break; + } + case 'copy_endpoint_url_clicked': { + analytics.reportEvent('connection_details_copy_endpoint_url_clicked', {}); + break; + } + case 'show_cloud_id_toggled': { + analytics.reportEvent('connection_details_show_cloud_id_toggled', {}); + break; + } + case 'copy_cloud_id_clicked': { + analytics.reportEvent('connection_details_copy_cloud_id_clicked', {}); + break; + } + case 'new_api_key_created': { + analytics.reportEvent('connection_details_new_api_key_created', {}); + break; + } + case 'manage_api_keys_clicked': { + analytics.reportEvent('connection_details_manage_api_keys_clicked', {}); + break; + } + case 'key_encoding_changed': { + analytics.reportEvent('connection_details_key_encoding_changed', { + format: event[1]!.format, + }); + break; + } + case 'copy_api_key_clicked': { + analytics.reportEvent('connection_details_copy_api_key_clicked', { + format: event[1]!.format, + }); + break; + } + } + }, }; return result; @@ -83,6 +128,7 @@ export interface KibanaConnectionDetailsProviderProps { theme: CoreStart['theme']; http?: CoreStart['http']; application?: CoreStart['application']; + analytics?: CoreStart['analytics']; }; plugins?: { cloud?: CloudStart; diff --git a/packages/cloud/connection_details/service.ts b/packages/cloud/connection_details/service.ts index 1e59206337baa..f07a3edeae201 100644 --- a/packages/cloud/connection_details/service.ts +++ b/packages/cloud/connection_details/service.ts @@ -10,7 +10,7 @@ import { BehaviorSubject } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { ApiKey } from './tabs/api_keys_tab/views/success_form/types'; import type { Format } from './tabs/api_keys_tab/views/success_form/format_select'; -import type { ConnectionDetailsOpts, TabID } from './types'; +import type { ConnectionDetailsOpts, TabID, ConnectionDetailsTelemetryEvents } from './types'; export class ConnectionDetailsService { public readonly tabId$ = new BehaviorSubject('endpoints'); @@ -39,6 +39,7 @@ export class ConnectionDetailsService { }; public readonly toggleShowCloudId = () => { + this.emitTelemetryEvent(['show_cloud_id_toggled']); this.showCloudId$.next(!this.showCloudId$.getValue()); }; @@ -48,6 +49,7 @@ export class ConnectionDetailsService { }; public readonly setApiKeyFormat = (format: Format) => { + this.emitTelemetryEvent(['key_encoding_changed', { format }]); this.apiKeyFormat$.next(format); }; @@ -76,6 +78,7 @@ export class ConnectionDetailsService { name: this.apiKeyName$.getValue(), }); this.apiKey$.next(apiKey); + this.emitTelemetryEvent(['new_api_key_created']); } catch (error) { this.apiKeyError$.next(error); } finally { @@ -88,4 +91,13 @@ export class ConnectionDetailsService { this.apiKeyError$.next(error); }); }; + + public readonly emitTelemetryEvent = (event: ConnectionDetailsTelemetryEvents) => { + try { + this.opts.onTelemetryEvent?.(event); + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error emitting telemetry event', error); + } + }; } diff --git a/packages/cloud/connection_details/stories.tsx b/packages/cloud/connection_details/stories.tsx index f6d15f181835c..41fb267f797e4 100644 --- a/packages/cloud/connection_details/stories.tsx +++ b/packages/cloud/connection_details/stories.tsx @@ -41,8 +41,15 @@ const defaultOpts: ConnectionDetailsOpts = { }, }; -export const StoriesProvider: React.FC = ({ children }) => { - return {children}; +export const StoriesProvider: React.FC> = ({ + children, + ...rest +}) => { + return ( + + {children} + + ); }; export const StoriesProviderKeyCreationError: React.FC = ({ children }) => { diff --git a/packages/cloud/connection_details/tabs/api_keys_tab/components/manage_keys_link.tsx b/packages/cloud/connection_details/tabs/api_keys_tab/components/manage_keys_link.tsx index a6ead0f520bb4..44ec35154874b 100644 --- a/packages/cloud/connection_details/tabs/api_keys_tab/components/manage_keys_link.tsx +++ b/packages/cloud/connection_details/tabs/api_keys_tab/components/manage_keys_link.tsx @@ -21,7 +21,10 @@ export const ManageKeysLink: React.FC = () => { return ( { + service.emitTelemetryEvent(['manage_api_keys_clicked']); + service.opts?.navigateToUrl?.(link); + }} data-test-subj={'connectionDetailsManageApiKeysLink'} > {i18n.translate('cloud.connectionDetails.apiKeys.managerLinkLabel', { diff --git a/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form.tsx b/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form.tsx index 154c3c8d2b4bb..f08ab3aec8844 100644 --- a/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form.tsx +++ b/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form.tsx @@ -23,6 +23,7 @@ export const SuccessForm: React.FC = () => { apiKey={apiKey} format={format} onFormatChange={service.setApiKeyFormat} + onCopyClick={() => service.emitTelemetryEvent(['copy_api_key_clicked', { format }])} /> ); }; diff --git a/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form_controlled.tsx b/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form_controlled.tsx index d5717bee8e244..e4dca6c5fe4f4 100644 --- a/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form_controlled.tsx +++ b/packages/cloud/connection_details/tabs/api_keys_tab/views/success_form/success_form_controlled.tsx @@ -18,12 +18,14 @@ export interface SuccessFormControlledProps { apiKey: ApiKey; format: Format; onFormatChange: (format: Format) => void; + onCopyClick?: () => void; } export const SuccessFormControlled: React.FC = ({ apiKey, format, onFormatChange, + onCopyClick, }) => { const keyValue = format === 'encoded' ? apiKey.encoded : `${apiKey.id}:${apiKey.key}`; @@ -62,7 +64,7 @@ export const SuccessFormControlled: React.FC = ({ fullWidth data-test-subj={'connectionDetailsApiKeyValueRow'} > - + diff --git a/packages/cloud/connection_details/tabs/endpoints_tab/endpoints_tab.tsx b/packages/cloud/connection_details/tabs/endpoints_tab/endpoints_tab.tsx index 40f442348acf7..ffdea170096ac 100644 --- a/packages/cloud/connection_details/tabs/endpoints_tab/endpoints_tab.tsx +++ b/packages/cloud/connection_details/tabs/endpoints_tab/endpoints_tab.tsx @@ -8,19 +8,35 @@ import { EuiForm } from '@elastic/eui'; import * as React from 'react'; -import { useConnectionDetailsOpts } from '../../context'; +import { useConnectionDetailsOpts, useConnectionDetailsService } from '../../context'; +import { useBehaviorSubject } from '../../hooks/use_behavior_subject'; import { CloudIdRow } from './rows/cloud_id_row'; import { EndpointUrlRow } from './rows/endpoints_url_row'; export const EndpointsTab: React.FC = () => { const { endpoints } = useConnectionDetailsOpts(); + const service = useConnectionDetailsService(); + const showCloudId = useBehaviorSubject(service.showCloudId$); if (!endpoints) return null; return ( - {!!endpoints?.url && } - {!!endpoints?.id && } + {!!endpoints?.url && ( + service.emitTelemetryEvent(['copy_endpoint_url_clicked'])} + /> + )} + {!!endpoints?.id && ( + service.emitTelemetryEvent(['copy_cloud_id_clicked'])} + /> + )} ); }; diff --git a/packages/cloud/connection_details/tabs/endpoints_tab/rows/cloud_id_row/cloud_id_row.tsx b/packages/cloud/connection_details/tabs/endpoints_tab/rows/cloud_id_row/cloud_id_row.tsx index ddaddb8d6ce5d..53d31baaef8f1 100644 --- a/packages/cloud/connection_details/tabs/endpoints_tab/rows/cloud_id_row/cloud_id_row.tsx +++ b/packages/cloud/connection_details/tabs/endpoints_tab/rows/cloud_id_row/cloud_id_row.tsx @@ -10,18 +10,23 @@ import * as React from 'react'; import { EuiFormRow, EuiSpacer, EuiSwitch } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { CopyInput } from '../../../../components/copy_input'; -import { useConnectionDetailsService } from '../../../../context'; -import { useBehaviorSubject } from '../../../../hooks/use_behavior_subject'; import { Label } from './label'; export interface CloudIdRowProps { value: string; + showCloudId: boolean; + learnMoreUrl?: string; + onShowCloudIdToggle: () => void; + onCopyClick?: () => void; } -export const CloudIdRow: React.FC = ({ value }) => { - const service = useConnectionDetailsService(); - const showCloudId = useBehaviorSubject(service.showCloudId$); - +export const CloudIdRow: React.FC = ({ + value, + showCloudId, + learnMoreUrl, + onShowCloudIdToggle, + onCopyClick, +}) => { return ( <> @@ -31,7 +36,7 @@ export const CloudIdRow: React.FC = ({ value }) => { defaultMessage: 'Show Cloud ID', })} checked={showCloudId} - onChange={service.toggleShowCloudId} + onChange={() => onShowCloudIdToggle()} data-test-subj="connectionDetailsCloudIdSwitch" /> @@ -39,7 +44,7 @@ export const CloudIdRow: React.FC = ({ value }) => { {showCloudId && ( } + label={ )} diff --git a/packages/cloud/connection_details/tabs/endpoints_tab/rows/endpoints_url_row.tsx b/packages/cloud/connection_details/tabs/endpoints_tab/rows/endpoints_url_row.tsx index 29f40dcb0c5e2..7eb9683b11ed6 100644 --- a/packages/cloud/connection_details/tabs/endpoints_tab/rows/endpoints_url_row.tsx +++ b/packages/cloud/connection_details/tabs/endpoints_tab/rows/endpoints_url_row.tsx @@ -13,9 +13,10 @@ import { CopyInput } from '../../../components/copy_input'; export interface EndpointUrlProps { url: string; + onCopyClick?: () => void; } -export const EndpointUrlRow: React.FC = ({ url }) => { +export const EndpointUrlRow: React.FC = ({ url, onCopyClick }) => { return ( = ({ url }) => { fullWidth data-test-subj="connectionDetailsEsUrl" > - + onCopyClick?.()} /> ); }; diff --git a/packages/cloud/connection_details/types.ts b/packages/cloud/connection_details/types.ts index 81b51a444ed78..593f2b57ee3ac 100644 --- a/packages/cloud/connection_details/types.ts +++ b/packages/cloud/connection_details/types.ts @@ -13,6 +13,7 @@ export interface ConnectionDetailsOpts { endpoints?: ConnectionDetailsOptsEndpoints; apiKeys?: ConnectionDetailsOptsApiKeys; navigateToUrl?: (url: string) => void; + onTelemetryEvent?: (event: ConnectionDetailsTelemetryEvents) => void; } export interface ConnectionDetailsOptsLinks { @@ -33,4 +34,20 @@ export interface ConnectionDetailsOptsApiKeys { hasPermission: () => Promise; } +export type ConnectionDetailsTelemetryEvent = [ + id: EventId, + payload?: EventPayload +]; + +export type ConnectionDetailsTelemetryEvents = + | ConnectionDetailsTelemetryEvent<'learn_more_clicked'> + | ConnectionDetailsTelemetryEvent<'tab_switched', { tab: string }> + | ConnectionDetailsTelemetryEvent<'copy_endpoint_url_clicked'> + | ConnectionDetailsTelemetryEvent<'show_cloud_id_toggled'> + | ConnectionDetailsTelemetryEvent<'copy_cloud_id_clicked'> + | ConnectionDetailsTelemetryEvent<'new_api_key_created'> + | ConnectionDetailsTelemetryEvent<'manage_api_keys_clicked'> + | ConnectionDetailsTelemetryEvent<'key_encoding_changed', { format: string }> + | ConnectionDetailsTelemetryEvent<'copy_api_key_clicked', { format: string }>; + export type TabID = 'endpoints' | 'apiKeys'; diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.test.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.test.ts index d2f987337a440..d930d024d2484 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.test.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.test.ts @@ -26,7 +26,7 @@ describe('Cloud Links Plugin - public', () => { describe('start', () => { beforeEach(() => { - plugin.setup(); + plugin.setup(coreMock.createSetup()); }); afterEach(() => { diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx index 0e8b5eec26f65..9f385500b13e8 100755 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/plugin.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { CoreStart, Plugin } from '@kbn/core/public'; +import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import type { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public'; import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; @@ -30,7 +30,68 @@ interface CloudLinksDepsStart { export class CloudLinksPlugin implements Plugin { - public setup() {} + public setup({ analytics }: CoreSetup) { + analytics.registerEventType({ + eventType: 'connection_details_learn_more_clicked', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_tab_switched', + schema: { + tab: { + type: 'keyword', + _meta: { + description: 'Connection details tab that was switched to.', + optional: false, + }, + }, + }, + }); + analytics.registerEventType({ + eventType: 'connection_details_copy_endpoint_url_clicked', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_show_cloud_id_toggled', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_copy_cloud_id_clicked', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_new_api_key_created', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_manage_api_keys_clicked', + schema: {}, + }); + analytics.registerEventType({ + eventType: 'connection_details_key_encoding_changed', + schema: { + format: { + type: 'keyword', + _meta: { + description: 'The format of the API key that was changed to.', + optional: false, + }, + }, + }, + }); + analytics.registerEventType({ + eventType: 'connection_details_copy_api_key_clicked', + schema: { + format: { + type: 'keyword', + _meta: { + description: 'The format of the API key that was copied.', + optional: false, + }, + }, + }, + }); + } public start(core: CoreStart, plugins: CloudLinksDepsStart) { const { cloud, security, guidedOnboarding, share } = plugins; From d87d04c611dd494b5e511bb296cf12d6bbe1f90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Fri, 17 May 2024 12:38:46 +0200 Subject: [PATCH 37/71] Improve retry logic in Cypress CI (#183488) ## Summary Previously when Fleet server setup step has failed the whole Cypress run was aborted. Added try catch on all the steps, so in case any of them fails we retry whole spec again --- .../fleet_server/fleet_server_services.ts | 8 +- .../scripts/run_cypress/parallel.ts | 192 +++++++++--------- 2 files changed, 106 insertions(+), 94 deletions(-) diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts index ae518420531fd..73cf96b5b6864 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/fleet_server/fleet_server_services.ts @@ -10,6 +10,7 @@ import type { KbnClient } from '@kbn/test'; import execa from 'execa'; import chalk from 'chalk'; import assert from 'assert'; +import pRetry from 'p-retry'; import type { AgentPolicy, CreateAgentPolicyResponse, Output } from '@kbn/fleet-plugin/common'; import { AGENT_POLICY_API_ROUTES, @@ -137,7 +138,12 @@ export const startFleetServer = async ({ const isServerless = await isServerlessKibanaFlavor(kbnClient); const policyId = policy || !isServerless ? await getOrCreateFleetServerAgentPolicyId(kbnClient, logger) : ''; - const serviceToken = isServerless ? '' : await generateFleetServiceToken(kbnClient, logger); + const serviceToken = isServerless + ? '' + : await pRetry(async () => generateFleetServiceToken(kbnClient, logger), { + retries: 2, + forever: false, + }); const startedFleetServer = await startFleetServerWithDocker({ kbnClient, logger, diff --git a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts index e12e70cb0aa9c..08c0846d2ce8e 100644 --- a/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts +++ b/x-pack/plugins/security_solution/scripts/run_cypress/parallel.ts @@ -316,100 +316,107 @@ ${JSON.stringify( ci: process.env.CI, }; - const shutdownEs = await pRetry( - async () => - runElasticsearch({ - config, - log, - name: `ftr-${esPort}`, - esFrom: config.get('esTestCluster')?.from || 'snapshot', - onEarlyExit, - }), - { retries: 2, forever: false } - ); - - await runKibanaServer({ - procs, - config, - installDir: options?.installDir, - extraKbnOpts: - options?.installDir || options?.ci || !isOpen - ? [] - : ['--dev', '--no-dev-config', '--no-dev-credentials'], - onEarlyExit, - inspect: argv.inspect, - }); - // Setup fleet if Cypress config requires it let fleetServer: void | StartedFleetServer; - if (cypressConfigFile.env?.WITH_FLEET_SERVER) { - log.info(`Setting up fleet-server for this Cypress config`); - - const kbnClient = createKbnClient({ - url: baseUrl, - username: config.get('servers.kibana.username'), - password: config.get('servers.kibana.password'), - log, + let shutdownEs; + + try { + shutdownEs = await pRetry( + async () => + runElasticsearch({ + config, + log, + name: `ftr-${esPort}`, + esFrom: config.get('esTestCluster')?.from || 'snapshot', + onEarlyExit, + }), + { retries: 2, forever: false } + ); + + await runKibanaServer({ + procs, + config, + installDir: options?.installDir, + extraKbnOpts: + options?.installDir || options?.ci || !isOpen + ? [] + : ['--dev', '--no-dev-config', '--no-dev-credentials'], + onEarlyExit, + inspect: argv.inspect, }); - fleetServer = await startFleetServer({ - kbnClient, - logger: log, - port: - fleetServerPort ?? config.has('servers.fleetserver.port') - ? (config.get('servers.fleetserver.port') as number) - : undefined, - // `force` is needed to ensure that any currently running fleet server (perhaps left - // over from an interrupted run) is killed and a new one restarted - force: true, - }); - } + if (cypressConfigFile.env?.WITH_FLEET_SERVER) { + log.info(`Setting up fleet-server for this Cypress config`); - await providers.loadAll(); + const kbnClient = createKbnClient({ + url: baseUrl, + username: config.get('servers.kibana.username'), + password: config.get('servers.kibana.password'), + log, + }); - const functionalTestRunner = new FunctionalTestRunner( - log, - config, - EsVersion.getDefault() - ); + fleetServer = await pRetry( + async () => + startFleetServer({ + kbnClient, + logger: log, + port: + fleetServerPort ?? config.has('servers.fleetserver.port') + ? (config.get('servers.fleetserver.port') as number) + : undefined, + // `force` is needed to ensure that any currently running fleet server (perhaps left + // over from an interrupted run) is killed and a new one restarted + force: true, + }), + { retries: 2, forever: false } + ); + } - const ftrEnv = await pRetry(() => functionalTestRunner.run(abortCtrl.signal), { - retries: 1, - }); + await providers.loadAll(); - log.debug( - `Env. variables returned by [functionalTestRunner.run()]:\n`, - JSON.stringify(ftrEnv, null, 2) - ); + const functionalTestRunner = new FunctionalTestRunner( + log, + config, + EsVersion.getDefault() + ); - // Normalized the set of available env vars in cypress - const cyCustomEnv = { - ...ftrEnv, + const ftrEnv = await pRetry(() => functionalTestRunner.run(abortCtrl.signal), { + retries: 1, + }); - // NOTE: - // ELASTICSEARCH_URL needs to be created here with auth because SIEM cypress setup depends on it. At some - // points we should probably try to refactor that code to use `ELASTICSEARCH_URL_WITH_AUTH` instead - ELASTICSEARCH_URL: - ftrEnv.ELASTICSEARCH_URL ?? createUrlFromFtrConfig('elasticsearch', true), - ELASTICSEARCH_URL_WITH_AUTH: createUrlFromFtrConfig('elasticsearch', true), - ELASTICSEARCH_USERNAME: - ftrEnv.ELASTICSEARCH_USERNAME ?? config.get('servers.elasticsearch.username'), - ELASTICSEARCH_PASSWORD: - ftrEnv.ELASTICSEARCH_PASSWORD ?? config.get('servers.elasticsearch.password'), + log.debug( + `Env. variables returned by [functionalTestRunner.run()]:\n`, + JSON.stringify(ftrEnv, null, 2) + ); - FLEET_SERVER_URL: createUrlFromFtrConfig('fleetserver'), + // Normalized the set of available env vars in cypress + const cyCustomEnv = { + ...ftrEnv, - KIBANA_URL: baseUrl, - KIBANA_URL_WITH_AUTH: createUrlFromFtrConfig('kibana', true), - KIBANA_USERNAME: config.get('servers.kibana.username'), - KIBANA_PASSWORD: config.get('servers.kibana.password'), + // NOTE: + // ELASTICSEARCH_URL needs to be created here with auth because SIEM cypress setup depends on it. At some + // points we should probably try to refactor that code to use `ELASTICSEARCH_URL_WITH_AUTH` instead + ELASTICSEARCH_URL: + ftrEnv.ELASTICSEARCH_URL ?? createUrlFromFtrConfig('elasticsearch', true), + ELASTICSEARCH_URL_WITH_AUTH: createUrlFromFtrConfig('elasticsearch', true), + ELASTICSEARCH_USERNAME: + ftrEnv.ELASTICSEARCH_USERNAME ?? config.get('servers.elasticsearch.username'), + ELASTICSEARCH_PASSWORD: + ftrEnv.ELASTICSEARCH_PASSWORD ?? config.get('servers.elasticsearch.password'), - IS_SERVERLESS: config.get('serverless'), + FLEET_SERVER_URL: createUrlFromFtrConfig('fleetserver'), - ...argv.env, - }; + KIBANA_URL: baseUrl, + KIBANA_URL_WITH_AUTH: createUrlFromFtrConfig('kibana', true), + KIBANA_USERNAME: config.get('servers.kibana.username'), + KIBANA_PASSWORD: config.get('servers.kibana.password'), - log.info(` + IS_SERVERLESS: config.get('serverless'), + + ...argv.env, + }; + + log.info(` ---------------------------------------------- Cypress run ENV for file: ${filePath}: ---------------------------------------------- @@ -419,18 +426,17 @@ ${JSON.stringify(cyCustomEnv, null, 2)} ---------------------------------------------- `); - if (isOpen) { - await cypress.open({ - configFile: cypressConfigFilePath, - config: { - e2e: { - baseUrl, + if (isOpen) { + await cypress.open({ + configFile: cypressConfigFilePath, + config: { + e2e: { + baseUrl, + }, + env: cyCustomEnv, }, - env: cyCustomEnv, - }, - }); - } else { - try { + }); + } else { result = await cypress.run({ browser: 'chrome', spec: filePath, @@ -450,9 +456,9 @@ ${JSON.stringify(cyCustomEnv, null, 2)} if (!(result as CypressCommandLine.CypressRunResult)?.totalFailed) { _.pull(failedSpecFilePaths, filePath); } - } catch (error) { - result = error; } + } catch (error) { + log.error(error); } if (fleetServer) { @@ -460,7 +466,7 @@ ${JSON.stringify(cyCustomEnv, null, 2)} } await procs.stop('kibana'); - await shutdownEs(); + await shutdownEs?.(); cleanupServerPorts({ esPort, kibanaPort, fleetServerPort }); return result; From 621c5bc2834001b3b5590b63820d649babdfb6bc Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Fri, 17 May 2024 13:03:10 +0200 Subject: [PATCH 38/71] skip inference_endpoints api integration test on MKI (#183705) ## Summary This test leaves trained model that fails follow-up UI test on [MKI](https://buildkite.com/elastic/appex-qa-serverless-kibana-ftr-tests/builds/1719#018f8523-c241-46ad-b664-441e12947a7f) for both Security/Search projects. Before this test on MKI ![image](https://github.com/elastic/kibana/assets/10977896/a6f2fcfa-fae5-42a5-ab60-3e516fb1baff) After this test run image The best fix is to make sure that system is reset to the state before the test (delete added trained model, saved objects if any) It might cause random failures on Kibana CI as well, but skipping for MKI only now. --- .../test_suites/common/index_management/inference_endpoints.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts index ecfcff804d69a..cacf294b4c81a 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/inference_endpoints.ts @@ -19,6 +19,8 @@ export default function ({ getService }: FtrProviderContext) { const service = 'elser'; describe('Inference endpoints', function () { + // test adds new trained model '.elser_model_2_linux-x86_64', but does not clean it. Follow up tests are affected + this.tags(['failsOnMKI']); before(async () => { log.debug(`Creating inference endpoint`); try { From 4b7d0145827925a297d1e1728e4447ecc4673554 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 17 May 2024 14:18:01 +0300 Subject: [PATCH 39/71] Change all connectors to use the basic auth header instead of the `auth` property of `axios` (#183162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes: https://github.com/elastic/kibana/issues/182391 ## Framework changes - Utils to construct basic header from username and password: [`fad6bde` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/fad6bde6afb1302c8629da47173abcdf41a1a602), [`b10d103` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/b10d103bd92fd17c77b97a6e014832ac1c6a9bdc) - Automatically convert `auth` to basic auth header in the sub-actions framework: [`ee27353` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/ee2735305142cbb84a930e3dc36e7a6d9116bab6) - Automatically convert `auth` to basic auth header in axios utils: [`94753a7` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/94753a7342595c0e2ee33c8f6620661d73526990) ## Jira Commit: [`c366163` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/c366163486a516f1d2d62b63d3bde171b6643e19) ## All ServiceNow connectors Commit: [`4324d93` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/4324d931f7bcacfd8b2d7b4eaa9c40562dc22c52) ## IBM Resilient IBM Resilient already uses the basic auth headers. PR https://github.com/elastic/kibana/pull/180561 added this functionality. The connector was manually tested when reviewing the PR. In [`7d9edab` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/7d9edabd6e8454ebc2190bfc7f565c8c345f47f5) I updated the connector to use the new util function. ## Webhook Commit: [`1a62c77` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/1a62c77d46dd40e9529ae102a6db3fc1513c494e) ## Cases webhook Commit: [`104f881` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/104f8812515f5944bc103fa0142e55a0b0350e84) ## xMatters Commit: [`ea7be2b` (#183162)](https://github.com/elastic/kibana/pull/183162/commits/ea7be2bbee89b71c3855769fc480a013e4020732) ## Connectors that do not use the `axios` `auth` property - D3Security - Email - Microsoft Teams - OpenAI - Opsgenie - PagerDuty - Sentinel One - Slack - Slack API - Swimlane - Tines - Torq ### 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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Connectors not working correctly | Low | High | Unit test and manual testing of all connectors affected | ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: “jeramysoucy” Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 4 +- x-pack/plugins/actions/server/index.ts | 1 + .../actions/server/lib/axios_utils.test.ts | 49 +++++++++++++ .../plugins/actions/server/lib/axios_utils.ts | 13 +++- .../server/lib/get_basic_auth_header.test.ts | 69 +++++++++++++++++++ .../server/lib/get_basic_auth_header.ts | 33 +++++++++ x-pack/plugins/actions/server/lib/index.ts | 1 + .../server/sub_action_framework/mocks.ts | 12 ++++ .../sub_action_connector.test.ts | 38 ++++++++++ .../sub_action_connector.ts | 21 ++++-- .../cases_webhook/service.test.ts | 38 ++++++++++ .../connector_types/cases_webhook/service.ts | 14 ++-- .../connector_types/jira/service.test.ts | 16 +++++ .../server/connector_types/jira/service.ts | 3 +- .../lib/servicenow/utils.test.ts | 3 +- .../connector_types/lib/servicenow/utils.ts | 3 +- .../connector_types/resilient/resilient.ts | 12 ++-- .../connector_types/webhook/index.test.ts | 5 +- .../server/connector_types/webhook/index.ts | 11 ++- .../connector_types/xmatters/index.test.ts | 49 ++++++++----- .../connector_types/xmatters/post_xmatters.ts | 6 +- yarn.lock | 18 ++--- 22 files changed, 361 insertions(+), 58 deletions(-) create mode 100644 x-pack/plugins/actions/server/lib/get_basic_auth_header.test.ts create mode 100644 x-pack/plugins/actions/server/lib/get_basic_auth_header.ts diff --git a/package.json b/package.json index dd5fbd1c06325..a4ad1dd7e5e81 100644 --- a/package.json +++ b/package.json @@ -83,9 +83,7 @@ "**/@langchain/core": "0.1.53", "**/@types/node": "20.10.5", "**/@typescript-eslint/utils": "5.62.0", - "**/axios": "1.6.3", "**/chokidar": "^3.5.3", - "**/follow-redirects": "1.15.2", "**/globule/minimatch": "^3.1.2", "**/hoist-non-react-statics": "^3.3.2", "**/isomorphic-fetch/node-fetch": "^2.6.7", @@ -959,7 +957,7 @@ "archiver": "^5.3.1", "async": "^3.2.3", "aws4": "^1.12.0", - "axios": "1.6.3", + "axios": "^1.6.8", "base64-js": "^1.3.1", "bitmap-sdf": "^1.0.3", "blurhash": "^2.0.1", diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 39b94204520b7..ef1c277ff049b 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -33,6 +33,7 @@ export { asSavedObjectExecutionSource, asHttpRequestExecutionSource, asNotificationExecutionSource, + getBasicAuthHeader, } from './lib'; export { ACTION_SAVED_OBJECT_TYPE } from './constants/saved_objects'; diff --git a/x-pack/plugins/actions/server/lib/axios_utils.test.ts b/x-pack/plugins/actions/server/lib/axios_utils.test.ts index 43f16b4863e9a..c5e23f6cd3db3 100644 --- a/x-pack/plugins/actions/server/lib/axios_utils.test.ts +++ b/x-pack/plugins/actions/server/lib/axios_utils.test.ts @@ -339,6 +339,55 @@ describe('request', () => { `"Do not use \\"baseURL\\" in the creation of your axios instance because you will mostly break proxy"` ); }); + + test('it converts the auth property to basic auth header', async () => { + await request({ + axios, + url: '/test', + logger, + configurationUtilities, + auth: { username: 'username', password: 'password' }, + }); + + expect(axiosMock).toHaveBeenCalledWith('/test', { + method: 'get', + httpAgent: undefined, + httpsAgent: expect.any(HttpsAgent), + proxy: false, + maxContentLength: 1000000, + timeout: 360000, + headers: { Authorization: `Basic ${Buffer.from('username:password').toString('base64')}` }, + }); + }); + + test('it does not override an authorization header if provided', async () => { + await request({ + axios, + url: '/test', + logger, + configurationUtilities, + auth: { username: 'username', password: 'password' }, + headers: { + 'Content-Type': 'application/json', + 'X-Test-Header': 'test', + Authorization: 'Bearer my_token', + }, + }); + + expect(axiosMock).toHaveBeenCalledWith('/test', { + method: 'get', + httpAgent: undefined, + httpsAgent: expect.any(HttpsAgent), + proxy: false, + maxContentLength: 1000000, + timeout: 360000, + headers: { + 'Content-Type': 'application/json', + 'X-Test-Header': 'test', + Authorization: 'Bearer my_token', + }, + }); + }); }); describe('patch', () => { diff --git a/x-pack/plugins/actions/server/lib/axios_utils.ts b/x-pack/plugins/actions/server/lib/axios_utils.ts index b623f427be681..3852f2a33755b 100644 --- a/x-pack/plugins/actions/server/lib/axios_utils.ts +++ b/x-pack/plugins/actions/server/lib/axios_utils.ts @@ -18,6 +18,7 @@ import { Logger } from '@kbn/core/server'; import { getCustomAgents } from './get_custom_agents'; import { ActionsConfigurationUtilities } from '../actions_config'; import { SSLSettings } from '../types'; +import { combineHeadersWithBasicAuthHeader } from './get_basic_auth_header'; export const request = async ({ axios, @@ -55,10 +56,18 @@ export const request = async ({ const { maxContentLength, timeout: settingsTimeout } = configurationUtilities.getResponseSettings(); + const { auth, ...restConfig } = config; + + const headersWithBasicAuth = combineHeadersWithBasicAuthHeader({ + username: auth?.username, + password: auth?.password, + headers, + }); + return await axios(url, { - ...config, + ...restConfig, method, - headers, + headers: headersWithBasicAuth, ...(data ? { data } : {}), // use httpAgent and httpsAgent and set axios proxy: false, to be able to handle fail on invalid certs httpAgent, diff --git a/x-pack/plugins/actions/server/lib/get_basic_auth_header.test.ts b/x-pack/plugins/actions/server/lib/get_basic_auth_header.test.ts new file mode 100644 index 0000000000000..b5064797de38a --- /dev/null +++ b/x-pack/plugins/actions/server/lib/get_basic_auth_header.test.ts @@ -0,0 +1,69 @@ +/* + * 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 { combineHeadersWithBasicAuthHeader, getBasicAuthHeader } from './get_basic_auth_header'; + +describe('get_basic_auth_header', () => { + describe('getBasicAuthHeader', () => { + it('constructs the basic auth header correctly', () => { + expect(getBasicAuthHeader({ username: 'test', password: 'foo' })).toEqual({ + Authorization: `Basic ${Buffer.from('test:foo').toString('base64')}`, + }); + }); + }); + + describe('combineHeadersWithBasicAuthHeader', () => { + it('constructs the basic auth header correctly', () => { + expect(combineHeadersWithBasicAuthHeader({ username: 'test', password: 'foo' })).toEqual({ + Authorization: `Basic ${Buffer.from('test:foo').toString('base64')}`, + }); + }); + + it('adds extra headers correctly', () => { + expect( + combineHeadersWithBasicAuthHeader({ + username: 'test', + password: 'foo', + headers: { 'X-token': 'foo' }, + }) + ).toEqual({ + Authorization: `Basic ${Buffer.from('test:foo').toString('base64')}`, + 'X-token': 'foo', + }); + }); + + it('does not overrides the auth header if provided', () => { + expect( + combineHeadersWithBasicAuthHeader({ + username: 'test', + password: 'foo', + headers: { Authorization: 'Bearer my_token' }, + }) + ).toEqual({ + Authorization: 'Bearer my_token', + }); + }); + + it('returns only the headers if auth is undefined', () => { + expect( + combineHeadersWithBasicAuthHeader({ + headers: { 'X-token': 'foo' }, + }) + ).toEqual({ + 'X-token': 'foo', + }); + }); + + it('returns undefined with no arguments', () => { + expect(combineHeadersWithBasicAuthHeader()).toEqual(undefined); + }); + + it('returns undefined when headers are null', () => { + expect(combineHeadersWithBasicAuthHeader({ headers: null })).toEqual(undefined); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/lib/get_basic_auth_header.ts b/x-pack/plugins/actions/server/lib/get_basic_auth_header.ts new file mode 100644 index 0000000000000..8408f6be76f2b --- /dev/null +++ b/x-pack/plugins/actions/server/lib/get_basic_auth_header.ts @@ -0,0 +1,33 @@ +/* + * 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 { AxiosHeaderValue } from 'axios'; + +interface GetBasicAuthHeaderArgs { + username: string; + password: string; +} + +type CombineHeadersWithBasicAuthHeader = Partial & { + headers?: Record | null; +}; + +export const getBasicAuthHeader = ({ username, password }: GetBasicAuthHeaderArgs) => { + const header = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; + + return { Authorization: header }; +}; + +export const combineHeadersWithBasicAuthHeader = ({ + username, + password, + headers, +}: CombineHeadersWithBasicAuthHeader = {}) => { + return username != null && password != null + ? { ...getBasicAuthHeader({ username, password }), ...headers } + : headers ?? undefined; +}; diff --git a/x-pack/plugins/actions/server/lib/index.ts b/x-pack/plugins/actions/server/lib/index.ts index e97198ee7b432..2737d83abfff6 100644 --- a/x-pack/plugins/actions/server/lib/index.ts +++ b/x-pack/plugins/actions/server/lib/index.ts @@ -38,3 +38,4 @@ export { export { validateEmptyStrings } from './validate_empty_strings'; export { parseDate } from './parse_date'; export type { RelatedSavedObjects } from './related_saved_objects'; +export { getBasicAuthHeader, combineHeadersWithBasicAuthHeader } from './get_basic_auth_header'; diff --git a/x-pack/plugins/actions/server/sub_action_framework/mocks.ts b/x-pack/plugins/actions/server/sub_action_framework/mocks.ts index 572aa20422a94..f6c8e86dd5af3 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/mocks.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/mocks.ts @@ -78,6 +78,18 @@ export class TestSubActionConnector extends SubActionConnector } = {}) { + const res = await this.request({ + url: 'https://example.com', + data: {}, + auth: { username: 'username', password: 'password' }, + headers: { 'X-Test-Header': 'test', ...headers }, + responseSchema: schema.object({ status: schema.string() }), + }); + + return res; + } } export class TestNoSubActions extends SubActionConnector { diff --git a/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.test.ts b/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.test.ts index 5980072217dd5..1358684d86093 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.test.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.test.ts @@ -210,5 +210,43 @@ describe('SubActionConnector', () => { 'Request to external service failed. Connector Id: test-id. Connector type: .test. Method: get. URL: https://example.com' ); }); + + it('converts auth axios property to a basic auth header if provided', async () => { + await service.testAuth(); + + expect(requestMock).toHaveBeenCalledTimes(1); + expect(requestMock).toBeCalledWith({ + axios: axiosInstanceMock, + configurationUtilities: mockedActionsConfig, + logger, + method: 'get', + data: {}, + headers: { + 'Content-Type': 'application/json', + 'X-Test-Header': 'test', + Authorization: `Basic ${Buffer.from('username:password').toString('base64')}`, + }, + url: 'https://example.com', + }); + }); + + it('does not override an authorization header if provided', async () => { + await service.testAuth({ headers: { Authorization: 'Bearer my_token' } }); + + expect(requestMock).toHaveBeenCalledTimes(1); + expect(requestMock).toBeCalledWith({ + axios: axiosInstanceMock, + configurationUtilities: mockedActionsConfig, + logger, + method: 'get', + data: {}, + headers: { + 'Content-Type': 'application/json', + 'X-Test-Header': 'test', + Authorization: 'Bearer my_token', + }, + url: 'https://example.com', + }); + }); }); }); diff --git a/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts b/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts index 14e2ded5d1a38..19cc7e90d6254 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/sub_action_connector.ts @@ -15,6 +15,7 @@ import axios, { AxiosRequestHeaders, AxiosHeaders, AxiosHeaderValue, + AxiosBasicCredentials, } from 'axios'; import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; @@ -29,6 +30,7 @@ import { SubAction, SubActionRequestParams } from './types'; import { ServiceParams } from './types'; import * as i18n from './translations'; import { request } from '../lib/axios_utils'; +import { combineHeadersWithBasicAuthHeader } from '../lib/get_basic_auth_header'; const isObject = (value: unknown): value is Record => { return isPlainObject(value); @@ -87,8 +89,17 @@ export abstract class SubActionConnector { } } - private getHeaders(headers?: AxiosRequestHeaders): Record { - return { 'Content-Type': 'application/json', ...headers }; + private getHeaders( + auth?: AxiosBasicCredentials, + headers?: AxiosRequestHeaders + ): Record { + const headersWithBasicAuth = combineHeadersWithBasicAuthHeader({ + username: auth?.username, + password: auth?.password, + headers, + }); + + return { 'Content-Type': 'application/json', ...headersWithBasicAuth }; } private validateResponse(responseSchema: Type, data: unknown) { @@ -137,15 +148,17 @@ export abstract class SubActionConnector { `Request to external service. Connector Id: ${this.connector.id}. Connector type: ${this.connector.type} Method: ${method}. URL: ${normalizedURL}` ); + const { auth, ...restConfig } = config; + const res = await request({ - ...config, + ...restConfig, axios: this.axiosInstance, url: normalizedURL, logger: this.logger, method, data: this.normalizeData(data), configurationUtilities: this.configurationUtilities, - headers: this.getHeaders(headers as AxiosHeaders), + headers: this.getHeaders(auth, headers as AxiosHeaders), timeout, }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.test.ts index 4598e8471a838..1e7b492ada959 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.test.ts @@ -13,6 +13,7 @@ import { CasesWebhookMethods, CasesWebhookPublicConfigurationType, ExternalServi import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server/lib'; const logger = loggingSystemMock.create().get() as jest.Mocked; jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => { @@ -124,6 +125,43 @@ describe('Cases webhook service', () => { ) ).not.toThrow(); }); + + test('uses the basic auth header for authentication', () => { + createExternalService( + actionId, + { + config, + secrets: { user: 'username', password: 'password' }, + }, + logger, + configurationUtilities + ); + + expect(axios.create).toHaveBeenCalledWith({ + headers: { + ...getBasicAuthHeader({ username: 'username', password: 'password' }), + 'content-type': 'application/json', + }, + }); + }); + + test('does not add the basic auth header for authentication if hasAuth=false', () => { + createExternalService( + actionId, + { + config: { ...config, hasAuth: false }, + secrets: { user: 'username', password: 'password' }, + }, + logger, + configurationUtilities + ); + + expect(axios.create).toHaveBeenCalledWith({ + headers: { + 'content-type': 'application/json', + }, + }); + }); }); describe('getIncident', () => { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.ts b/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.ts index dd0b28d321a60..46407c18bd081 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/cases_webhook/service.ts @@ -8,10 +8,10 @@ import axios, { AxiosResponse } from 'axios'; import { Logger } from '@kbn/core/server'; -import { isString } from 'lodash'; import { renderMustacheStringNoEscape } from '@kbn/actions-plugin/server/lib/mustache_renderer'; import { request } from '@kbn/actions-plugin/server/lib/axios_utils'; import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; +import { combineHeadersWithBasicAuthHeader } from '@kbn/actions-plugin/server/lib'; import { validateAndNormalizeUrl, validateJson } from './validators'; import { createServiceError, @@ -69,14 +69,18 @@ export const createExternalService = ( } const createIncidentUrl = removeSlash(createIncidentUrlConfig); + const headersWithBasicAuth = hasAuth + ? combineHeadersWithBasicAuthHeader({ + username: user ?? undefined, + password: password ?? undefined, + headers, + }) + : {}; const axiosInstance = axios.create({ - ...(hasAuth && isString(secrets.user) && isString(secrets.password) - ? { auth: { username: secrets.user, password: secrets.password } } - : {}), headers: { ['content-type']: 'application/json', - ...(headers != null ? headers : {}), + ...headersWithBasicAuth, }, }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/jira/service.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/jira/service.test.ts index f6b4dcf909abe..34e0f1f799ce5 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/jira/service.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/jira/service.test.ts @@ -13,6 +13,7 @@ import { ExternalService } from './types'; import { Logger } from '@kbn/core/server'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server'; const logger = loggingSystemMock.create().get() as jest.Mocked; interface ResponseError extends Error { @@ -204,6 +205,21 @@ describe('Jira service', () => { ) ).toThrow(); }); + + test('uses the basic auth header for authentication', () => { + createExternalService( + { + config: { apiUrl: 'https://coolsite.net/', projectKey: 'CK' }, + secrets: { apiToken: 'token', email: 'elastic@elastic.com' }, + }, + logger, + configurationUtilities + ); + + expect(axios.create).toHaveBeenCalledWith({ + headers: getBasicAuthHeader({ username: 'elastic@elastic.com', password: 'token' }), + }); + }); }); describe('getIncident', () => { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/jira/service.ts b/x-pack/plugins/stack_connectors/server/connector_types/jira/service.ts index f0ad0e3062ccc..3cd5115234da1 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/jira/service.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/jira/service.ts @@ -15,6 +15,7 @@ import { throwIfResponseIsNotValid, } from '@kbn/actions-plugin/server/lib/axios_utils'; import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server'; import { CreateCommentParams, CreateIncidentParams, @@ -66,7 +67,7 @@ export const createExternalService = ( const searchUrl = `${urlWithoutTrailingSlash}/${BASE_URL}/search`; const axiosInstance = axios.create({ - auth: { username: email, password: apiToken }, + headers: getBasicAuthHeader({ username: email, password: apiToken }), }); const getIncidentViewURL = (key: string) => { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts index f151affe8a597..8bc9fa0565d8f 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.test.ts @@ -20,6 +20,7 @@ import type { ResponseError } from './types'; import { connectorTokenClientMock } from '@kbn/actions-plugin/server/lib/connector_token_client.mock'; import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; import { getOAuthJwtAccessToken } from '@kbn/actions-plugin/server/lib/get_oauth_jwt_access_token'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server'; jest.mock('@kbn/actions-plugin/server/lib/get_oauth_jwt_access_token', () => ({ getOAuthJwtAccessToken: jest.fn(), @@ -189,7 +190,7 @@ describe('utils', () => { expect(createAxiosInstanceMock).toHaveBeenCalledTimes(1); expect(createAxiosInstanceMock).toHaveBeenCalledWith({ - auth: { password: 'password', username: 'username' }, + headers: getBasicAuthHeader({ username: 'username', password: 'password' }), }); }); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts index 44171d1a11947..8998fa9f94c63 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/lib/servicenow/utils.ts @@ -11,6 +11,7 @@ import { addTimeZoneToDate, getErrorMessage } from '@kbn/actions-plugin/server/l import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; import { ConnectorTokenClientContract } from '@kbn/actions-plugin/server/types'; import { getOAuthJwtAccessToken } from '@kbn/actions-plugin/server/lib/get_oauth_jwt_access_token'; +import { getBasicAuthHeader } from '@kbn/actions-plugin/server'; import { ExternalServiceCredentials, Incident, @@ -115,7 +116,7 @@ export const getAxiosInstance = ({ if (!isOAuth && username && password) { axiosInstance = axios.create({ - auth: { username, password }, + headers: getBasicAuthHeader({ username, password }), }); } else { axiosInstance = axios.create(); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/resilient/resilient.ts b/x-pack/plugins/stack_connectors/server/connector_types/resilient/resilient.ts index 4c8175e52ab8f..1351488dbf892 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/resilient/resilient.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/resilient/resilient.ts @@ -7,7 +7,7 @@ import { AxiosError } from 'axios'; import { omitBy, isNil } from 'lodash/fp'; -import { CaseConnector, ServiceParams } from '@kbn/actions-plugin/server'; +import { CaseConnector, getBasicAuthHeader, ServiceParams } from '@kbn/actions-plugin/server'; import { schema, Type } from '@kbn/config-schema'; import { getErrorMessage } from '@kbn/actions-plugin/server/lib/axios_utils'; import { @@ -96,12 +96,10 @@ export class ResilientConnector extends CaseConnector< } private getAuthHeaders() { - const token = Buffer.from( - this.secrets.apiKeyId + ':' + this.secrets.apiKeySecret, - 'utf8' - ).toString('base64'); - - return { Authorization: `Basic ${token}` }; + return getBasicAuthHeader({ + username: this.secrets.apiKeyId, + password: this.secrets.apiKeySecret, + }); } private getOrgUrl() { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.test.ts index bd24314fa79b7..9e82b2d2c31e7 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.test.ts @@ -348,13 +348,10 @@ describe('execute()', () => { delete requestMock.mock.calls[0][0].configurationUtilities; expect(requestMock.mock.calls[0][0]).toMatchInlineSnapshot(` Object { - "auth": Object { - "password": "123", - "username": "abc", - }, "axios": undefined, "data": "some data", "headers": Object { + "Authorization": "Basic YWJjOjEyMw==", "aheader": "a value", }, "logger": Object { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.ts index b71c478177178..a3383ee844016 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/webhook/index.ts @@ -25,6 +25,7 @@ import { SecurityConnectorFeatureId, } from '@kbn/actions-plugin/common/types'; import { renderMustacheString } from '@kbn/actions-plugin/server/lib/mustache_renderer'; +import { combineHeadersWithBasicAuthHeader } from '@kbn/actions-plugin/server/lib'; import { SSLCertType, WebhookAuthType } from '../../../common/webhook/constants'; import { getRetryAfterIntervalFromHeaders } from '../lib/http_response_retry_header'; import { nullableType } from '../lib/nullable'; @@ -210,6 +211,7 @@ export async function executor( isString(secrets.password) ? { auth: { username: secrets.user, password: secrets.password } } : {}; + const sslCertificate = authType === WebhookAuthType.SSL && ((isString(secrets.crt) && isString(secrets.key)) || isString(secrets.pfx)) @@ -233,14 +235,19 @@ export async function executor( ...(ca ? { ca: Buffer.from(ca, 'base64') } : {}), }; + const headersWithBasicAuth = combineHeadersWithBasicAuthHeader({ + username: basicAuth.auth?.username, + password: basicAuth.auth?.password, + headers, + }); + const result: Result> = await promiseResult( request({ axios: axiosInstance, method, url, logger, - ...basicAuth, - headers: headers ? headers : {}, + headers: headersWithBasicAuth, data, configurationUtilities, sslOverrides, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/xmatters/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/xmatters/index.test.ts index f39f510282984..23f7a01887126 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/xmatters/index.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/xmatters/index.test.ts @@ -5,11 +5,7 @@ * 2.0. */ -jest.mock('./post_xmatters', () => ({ - postXmatters: jest.fn(), -})); -import { postXmatters } from './post_xmatters'; - +import axios from 'axios'; import { Logger } from '@kbn/core/server'; import { ConnectorTypeConfigType, @@ -28,8 +24,22 @@ import { import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; import { loggerMock } from '@kbn/logging-mocks'; +import * as utils from '@kbn/actions-plugin/server/lib/axios_utils'; + +jest.mock('axios'); +jest.mock('@kbn/actions-plugin/server/lib/axios_utils', () => { + const originalUtils = jest.requireActual('@kbn/actions-plugin/server/lib/axios_utils'); + return { + ...originalUtils, + request: jest.fn(), + patch: jest.fn(), + }; +}); + +axios.create = jest.fn(() => axios); +const requestMock = utils.request as jest.Mock; +axios.create = jest.fn(() => axios); -const postxMattersMock = postXmatters as jest.Mock; const services: Services = actionsMock.createServices(); const mockedLogger: jest.Mocked = loggerMock.create(); @@ -383,8 +393,8 @@ describe('connector validation', () => { describe('execute()', () => { beforeEach(() => { - postxMattersMock.mockReset(); - postxMattersMock.mockResolvedValue({ + requestMock.mockReset(); + requestMock.mockResolvedValue({ status: 200, statusText: '', data: '', @@ -415,14 +425,10 @@ describe('execute()', () => { logger: mockedLogger, }); - expect(postxMattersMock.mock.calls[0][0]).toMatchInlineSnapshot(` + const { method, url, headers, data } = requestMock.mock.calls[0][0]; + + expect({ method, url, headers, data }).toMatchInlineSnapshot(` Object { - "basicAuth": Object { - "auth": Object { - "password": "123", - "username": "abc", - }, - }, "data": Object { "alertActionGroupName": "Small t-shirt", "date": "2022-01-18T19:01:08.818Z", @@ -432,6 +438,10 @@ describe('execute()', () => { "spaceId": "default", "tags": "test1, test2", }, + "headers": Object { + "Authorization": "Basic YWJjOjEyMw==", + }, + "method": "post", "url": "https://abc.def/my-xmatters", } `); @@ -442,7 +452,7 @@ describe('execute()', () => { configUrl: 'https://abc.def/my-xmatters', usesBasic: true, }; - postxMattersMock.mockRejectedValueOnce({ + requestMock.mockRejectedValueOnce({ tag: 'err', message: 'maxContentLength size of 1000000 exceeded', }); @@ -496,9 +506,10 @@ describe('execute()', () => { logger: mockedLogger, }); - expect(postxMattersMock.mock.calls[0][0]).toMatchInlineSnapshot(` + const { method, url, headers, data } = requestMock.mock.calls[0][0]; + + expect({ method, url, headers, data }).toMatchInlineSnapshot(` Object { - "basicAuth": undefined, "data": Object { "alertActionGroupName": "Small t-shirt", "date": "2022-01-18T19:01:08.818Z", @@ -508,6 +519,8 @@ describe('execute()', () => { "spaceId": "default", "tags": "test1, test2", }, + "headers": undefined, + "method": "post", "url": "https://abc.def/my-xmatters?apiKey=someKey", } `); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/xmatters/post_xmatters.ts b/x-pack/plugins/stack_connectors/server/connector_types/xmatters/post_xmatters.ts index 1fcec5a4238f7..2c2a08901cec3 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/xmatters/post_xmatters.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/xmatters/post_xmatters.ts @@ -9,6 +9,7 @@ import axios, { AxiosResponse } from 'axios'; import { Logger } from '@kbn/core/server'; import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; import { request } from '@kbn/actions-plugin/server/lib/axios_utils'; +import { combineHeadersWithBasicAuthHeader } from '@kbn/actions-plugin/server/lib'; interface PostXmattersOptions { url: string; @@ -42,7 +43,10 @@ export async function postXmatters( method: 'post', url, logger, - ...basicAuth, + headers: combineHeadersWithBasicAuthHeader({ + username: basicAuth?.auth.username, + password: basicAuth?.auth.password, + }), data, configurationUtilities, validateStatus: () => true, diff --git a/yarn.lock b/yarn.lock index e35fbacacef73..cf2d9e2fb8052 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12246,12 +12246,12 @@ axe-core@^4.9.0: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.0.tgz#b18971494551ab39d4ff5f7d4c6411bd20cc7c2a" integrity sha512-H5orY+M2Fr56DWmMFpMrq5Ge93qjNdPVqzBv5gWK3aD1OvjBEJlEzxf09z93dGVQeI0LiW+aCMIx1QtShC/zUw== -axios@1.6.3, axios@^1.0.0, axios@^1.3.4, axios@^1.6.0, axios@^1.6.7: - version "1.6.3" - resolved "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz#7f50f23b3aa246eff43c54834272346c396613f4" - integrity sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww== +axios@^1.0.0, axios@^1.3.4, axios@^1.6.0, axios@^1.6.7, axios@^1.6.8: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== dependencies: - follow-redirects "^1.15.0" + follow-redirects "^1.15.6" form-data "^4.0.0" proxy-from-env "^1.1.0" @@ -17589,10 +17589,10 @@ focus-lock@^0.11.6: dependencies: tslib "^2.0.3" -follow-redirects@1.15.2, follow-redirects@^1.0.0, follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.0.0, follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== font-awesome@4.7.0: version "4.7.0" From 148eeec0fe8cea89d1fccc5e6432be95be304a79 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Fri, 17 May 2024 13:23:21 +0200 Subject: [PATCH 40/71] Update `supertest` and `superagent` to latest version (#183587) ## Summary Related to https://github.com/elastic/kibana/issues/7104 Update supertest, superagent, and the corresponding type package, to their latest version. (of course, types had some signature changes and we're massively using supertest in all our FTR suites so the whole Kibana multiverse has to review it) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 6 +- .../src/http_server.test.ts | 2 +- .../base_path_proxy_server.test.ts | 6 +- .../http/cookie_session_storage.test.ts | 14 ++-- .../http/versioned_router.test.ts | 2 +- .../metrics/server_collector.test.ts | 4 +- .../apis/data_views/fields_route/cache.ts | 2 +- .../saved_objects_management/scroll_count.ts | 3 +- test/api_integration/apis/telemetry/opt_in.ts | 2 +- test/api_integration/services/supertest.ts | 6 +- test/common/services/bsearch.ts | 2 +- .../create_apm_event_client/index.test.ts | 2 +- .../common/lib/wait_for_execution_count.ts | 2 +- .../helpers/alerting_api_helper.ts | 6 +- .../helpers/alerting_wait_for_helpers.ts | 2 +- .../observability/helpers/data_view.ts | 6 +- .../group1/tests/alerting/find.ts | 4 +- .../group1/tests/alerting/find_with_post.ts | 4 +- .../group1/tests/alerting/get.ts | 4 +- .../actions/connector_types/crowdstrike.ts | 2 +- .../actions/connector_types/sentinelone.ts | 2 +- .../actions/sub_action_framework/index.ts | 4 +- .../actions/connector_types/stack/webhook.ts | 4 +- .../spaces_only/tests/alerting/group1/find.ts | 6 +- .../spaces_only/tests/alerting/group1/get.ts | 4 +- .../tests/alerting/group3/test_helpers.ts | 20 +++--- .../apis/asset_manager/tests/helpers.ts | 4 +- .../apis/cloud_security_posture/helper.ts | 4 +- .../index_management/lib/templates.api.ts | 4 +- .../apis/metrics_ui/helpers.ts | 3 - .../apis/telemetry/telemetry.ts | 2 +- .../common/apm_api_supertest.ts | 2 +- .../apm_api_integration/common/bettertest.ts | 2 +- .../alerts/helpers/alerting_api_helper.ts | 16 ++--- .../helpers/cleanup_rule_and_alert_state.ts | 4 +- .../alerts/helpers/wait_for_active_rule.ts | 2 +- .../common/lib/alerts.ts | 12 ++-- .../common/lib/api/attachments.ts | 26 +++---- .../common/lib/api/case.ts | 6 +- .../common/lib/api/configuration.ts | 10 +-- .../common/lib/api/connectors.ts | 14 ++-- .../common/lib/api/files.ts | 20 +++--- .../common/lib/api/index.ts | 47 ++++++------- .../common/lib/api/user_actions.ts | 8 +-- .../common/lib/api/user_profiles.ts | 10 +-- .../tests/common/cases/bulk_create_cases.ts | 2 +- .../tests/common/cases/delete_cases.ts | 2 +- .../tests/common/cases/import_export.ts | 2 +- .../trial/connectors/cases/cases_connector.ts | 2 +- x-pack/test/common/services/bsearch_secure.ts | 2 +- .../alerts/create_alerts_index.ts | 2 +- .../alerts/delete_all_alerts.ts | 2 +- .../alerts/get_alerts_by_id.ts | 2 +- .../alerts/get_alerts_by_ids.ts | 2 +- .../alerts/wait_for_alerts_to_be_present.ts | 2 +- .../detections_response/rules/create_rule.ts | 2 +- .../rules/delete_all_rules.ts | 2 +- .../detections_response/rules/delete_rule.ts | 2 +- .../rules/wait_for_rule_status.ts | 2 +- .../common/dataset_quality_api_supertest.ts | 2 +- .../tests/integrations/package_utils.ts | 8 +-- .../apis/epm/install_by_upload.ts | 15 +++-- x-pack/test/fleet_api_integration/helpers.ts | 8 +-- .../test/functional/services/transform/api.ts | 2 +- .../apps/cases/group2/attachment_framework.ts | 4 +- .../triggers_actions_ui/connectors/utils.ts | 14 ++-- .../monitoring_api_integration/packages.ts | 5 +- .../observability_ai_assistant_api_client.ts | 53 +++++++++++++-- .../tests/connectors/connectors.spec.ts | 4 +- .../common/obs_api_supertest.ts | 2 +- .../observability_onboarding_api_supertest.ts | 2 +- .../common/api_supertest.ts | 2 +- .../common/bettertest.ts | 4 +- .../common/lib/create_users_and_roles.ts | 4 +- .../common/suites/bulk_create.ts | 2 +- .../common/suites/bulk_delete.ts | 2 +- .../common/suites/bulk_get.ts | 2 +- .../common/suites/bulk_resolve.ts | 6 +- .../common/suites/bulk_update.ts | 6 +- .../common/suites/create.ts | 6 +- .../common/suites/delete.ts | 6 +- .../common/suites/export.ts | 6 +- .../common/suites/find.ts | 6 +- .../common/suites/get.ts | 6 +- .../common/suites/import.ts | 6 +- .../common/suites/resolve.ts | 6 +- .../common/suites/resolve_import_errors.ts | 6 +- .../common/suites/update.ts | 6 +- .../create_endpoint_exceptions.ts | 2 +- .../execution_logic/utils.ts | 7 +- .../actions/create_new_webhook_action.ts | 4 +- .../utils/alerts/get_alerts.ts | 2 +- .../migrations/finalize_alerts_migration.ts | 2 +- .../migrations/start_alerts_migration.ts | 2 +- .../alerts/wait_for_alert_to_complete.ts | 2 +- .../utils/connectors/create_connector.ts | 2 +- .../utils/connectors/delete_connector.ts | 5 +- .../utils/connectors/get_connector.ts | 2 +- .../item/create_exception_list_item.ts | 2 +- .../create_container_with_endpoint_entries.ts | 2 +- .../list/create_container_with_entries.ts | 2 +- .../list/create_exception_list.ts | 2 +- .../list/delete_exception_list.ts | 2 +- .../detections_response/utils/get_stats.ts | 2 +- .../machine_learning_setup.ts | 4 +- .../utils/rules/create_legacy_rule_action.ts | 2 +- .../utils/rules/create_non_security_rule.ts | 4 +- .../utils/rules/create_rule_saved_object.ts | 2 +- .../utils/rules/create_rule_with_auth.ts | 2 +- .../create_rule_with_exception_entries.ts | 2 +- .../utils/rules/fetch_rule.ts | 2 +- .../utils/rules/find_immutable_rule_by_id.ts | 2 +- .../utils/rules/get_coverage_overview.ts | 2 +- .../utils/rules/get_rule_actions.ts | 15 ++--- .../utils/rules/patch_rule.ts | 2 +- .../delete_prebuilt_rules_fleet_package.ts | 4 +- .../prebuilt_rules/get_installed_rules.ts | 4 +- ...get_prebuilt_rules_and_timelines_status.ts | 2 +- .../get_prebuilt_rules_fleet_package.ts | 2 +- .../get_prebuilt_rules_status.ts | 2 +- .../install_fleet_package_by_url.ts | 4 +- .../install_mock_prebuilt_rules.ts | 2 +- .../prebuilt_rules/install_prebuilt_rules.ts | 2 +- .../install_prebuilt_rules_and_timelines.ts | 2 +- .../install_prebuilt_rules_fleet_package.ts | 2 +- .../review_install_prebuilt_rules.ts | 2 +- .../review_upgrade_prebuilt_rules.ts | 2 +- .../prebuilt_rules/upgrade_prebuilt_rules.ts | 2 +- .../utils/rules/preview_rule.ts | 2 +- .../preview_rule_with_exception_entries.ts | 2 +- .../utils/rules/update_rule.ts | 2 +- .../telemetry/get_security_telemetry_stats.ts | 2 +- .../utils/asset_criticality.ts | 4 +- .../utils/get_risk_engine_stats.ts | 2 +- .../entity_analytics/utils/risk_engine.ts | 17 ++--- .../genai/invoke_ai/utils/create_connector.ts | 2 +- .../utils/post_actions_client_execute.ts | 2 +- .../trial_license_complete_tier/helpers.ts | 7 +- .../lists_and_exception_lists/utils.ts | 22 +++--- .../common/lib/create_users_and_roles.ts | 4 +- .../suites/disable_legacy_url_aliases.ts | 6 +- .../common/suites/get_shareable_references.ts | 6 +- .../common/suites/update_objects_spaces.ts | 6 +- .../api_integration/services/svl_cases/api.ts | 2 +- .../api_integration/services/transform/api.ts | 2 +- .../alerting/helpers/alerting_api_helper.ts | 32 ++++----- .../helpers/alerting_wait_for_helpers.ts | 4 +- .../common/apm_api_supertest.ts | 2 +- .../common/dataset_quality_api_supertest.ts | 2 +- .../synthetics/synthetics_enablement.ts | 4 +- .../shared/lib/object_remover.ts | 8 +-- .../shared/services/bsearch_secure.ts | 2 +- yarn.lock | 67 ++++++++++--------- 153 files changed, 439 insertions(+), 431 deletions(-) diff --git a/package.json b/package.json index a4ad1dd7e5e81..696e5aa63a5d5 100644 --- a/package.json +++ b/package.json @@ -1523,7 +1523,7 @@ "@types/source-map-support": "^0.5.3", "@types/stats-lite": "^2.2.0", "@types/styled-components": "^5.1.0", - "@types/supertest": "^2.0.12", + "@types/supertest": "^6.0.2", "@types/tapable": "^1.0.6", "@types/tar": "^6.1.11", "@types/testing-library__jest-dom": "^5.14.7", @@ -1697,8 +1697,8 @@ "style-loader": "^1.1.3", "stylelint": "^14.9.1", "stylelint-scss": "^4.3.0", - "superagent": "^8.1.2", - "supertest": "^6.3.3", + "superagent": "^9.0.2", + "supertest": "^7.0.0", "svgo": "^2.8.0", "table": "^6.8.1", "tape": "^5.0.1", diff --git a/packages/core/http/core-http-server-internal/src/http_server.test.ts b/packages/core/http/core-http-server-internal/src/http_server.test.ts index 129efea82cc8c..235fab185e12d 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.test.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.test.ts @@ -1622,7 +1622,7 @@ describe('setup contract', () => { .get('/static/some_json.json') .expect(200); - const etag = response.get('etag'); + const etag = response.get('etag')!; expect(etag).not.toBeUndefined(); await supertest(innerServer.listener) diff --git a/packages/kbn-cli-dev-mode/src/integration_tests/base_path_proxy_server.test.ts b/packages/kbn-cli-dev-mode/src/integration_tests/base_path_proxy_server.test.ts index 5e8a7a50a7b32..0f0a69638cfa2 100644 --- a/packages/kbn-cli-dev-mode/src/integration_tests/base_path_proxy_server.test.ts +++ b/packages/kbn-cli-dev-mode/src/integration_tests/base_path_proxy_server.test.ts @@ -28,7 +28,7 @@ describe('BasePathProxyServer', () => { let logger: TestLog; let config: IHttpConfig; let basePath: string; - let proxySupertest: supertest.SuperTest; + let proxySupertest: supertest.Agent; beforeEach(async () => { logger = new TestLog(); @@ -330,7 +330,7 @@ describe('BasePathProxyServer', () => { describe('shouldRedirect', () => { let proxyServerWithoutShouldRedirect: BasePathProxyServer; - let proxyWithoutShouldRedirectSupertest: supertest.SuperTest; + let proxyWithoutShouldRedirectSupertest: supertest.Agent; beforeEach(async () => { // setup and start a proxy server which does not use "shouldRedirectFromOldBasePath" @@ -373,7 +373,7 @@ describe('BasePathProxyServer', () => { describe('constructor option for sending in a custom basePath', () => { let proxyServerWithFooBasePath: BasePathProxyServer; - let proxyWithFooBasePath: supertest.SuperTest; + let proxyWithFooBasePath: supertest.Agent; beforeEach(async () => { // setup and start a proxy server which uses a basePath of "foo" diff --git a/src/core/server/integration_tests/http/cookie_session_storage.test.ts b/src/core/server/integration_tests/http/cookie_session_storage.test.ts index 4a71502db0ccb..192a3daf8fd2d 100644 --- a/src/core/server/integration_tests/http/cookie_session_storage.test.ts +++ b/src/core/server/integration_tests/http/cookie_session_storage.test.ts @@ -134,7 +134,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); expect(cookies).toHaveLength(1); @@ -172,7 +172,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); expect(cookies).toHaveLength(1); @@ -204,7 +204,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200, { value: null }); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).not.toBeDefined(); }); @@ -237,7 +237,7 @@ describe('Cookie based SessionStorage', () => { .get('/') .expect(200, { value: userData }); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); await delay(sessionDurationMs); @@ -283,7 +283,7 @@ describe('Cookie based SessionStorage', () => { .get('/') .expect(200, { value: userData }); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); const sessionCookie = retrieveSessionCookie(cookies[0]); @@ -418,7 +418,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; const sessionCookie = retrieveSessionCookie(cookies[0]); const response2 = await supertest(innerServer.listener) @@ -475,7 +475,7 @@ describe('Cookie based SessionStorage', () => { const response = await supertest(innerServer.listener).get('/').expect(200); - const cookies = response.get('set-cookie'); + const cookies = response.get('set-cookie')!; expect(cookies).toBeDefined(); expect(cookies).toHaveLength(1); diff --git a/src/core/server/integration_tests/http/versioned_router.test.ts b/src/core/server/integration_tests/http/versioned_router.test.ts index 86427a2488e2d..72daedb990dff 100644 --- a/src/core/server/integration_tests/http/versioned_router.test.ts +++ b/src/core/server/integration_tests/http/versioned_router.test.ts @@ -29,7 +29,7 @@ interface AdditionalOptions { describe('Routing versioned requests', () => { let router: IRouter; - let supertest: Supertest.SuperTest; + let supertest: Supertest.Agent; async function setupServer(cliArgs: Partial = {}, options: AdditionalOptions = {}) { logger = loggingSystemMock.create(); diff --git a/src/core/server/integration_tests/metrics/server_collector.test.ts b/src/core/server/integration_tests/metrics/server_collector.test.ts index 6151cd1ebe6bf..481447f599b21 100644 --- a/src/core/server/integration_tests/metrics/server_collector.test.ts +++ b/src/core/server/integration_tests/metrics/server_collector.test.ts @@ -116,7 +116,7 @@ describe('ServerMetricsCollector', () => { // Subscribe to the aborted$ event const waitFor1stAbort = disconnectAborted$.pipe(take(1)).toPromise(); - discoReq1.abort(); + void discoReq1.abort(); // Wait for the aborted$ event await waitFor1stAbort; @@ -132,7 +132,7 @@ describe('ServerMetricsCollector', () => { // Subscribe to the aborted$ event const waitFor2ndAbort = disconnectAborted$.pipe(take(1)).toPromise(); - discoReq2.abort(); + void discoReq2.abort(); // Wait for the aborted$ event await waitFor2ndAbort; diff --git a/test/api_integration/apis/data_views/fields_route/cache.ts b/test/api_integration/apis/data_views/fields_route/cache.ts index c97be4b7411f3..dea14dec6bdcd 100644 --- a/test/api_integration/apis/data_views/fields_route/cache.ts +++ b/test/api_integration/apis/data_views/fields_route/cache.ts @@ -70,7 +70,7 @@ export default function ({ getService }: FtrProviderContext) { await supertest .get(FIELDS_PATH) - .set('If-None-Match', response.get('etag')) + .set('If-None-Match', response.get('etag')!) .query({ pattern: '*', include_unmapped: true, diff --git a/test/api_integration/apis/saved_objects_management/scroll_count.ts b/test/api_integration/apis/saved_objects_management/scroll_count.ts index 94e301a085d70..26f4f4e1a626e 100644 --- a/test/api_integration/apis/saved_objects_management/scroll_count.ts +++ b/test/api_integration/apis/saved_objects_management/scroll_count.ts @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -import { SuperTest, Test } from 'supertest'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -14,7 +13,7 @@ const apiUrl = '/api/kibana/management/saved_objects/scroll/counts'; const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard']; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest') as SuperTest; + const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); const esArchiver = getService('esArchiver'); diff --git a/test/api_integration/apis/telemetry/opt_in.ts b/test/api_integration/apis/telemetry/opt_in.ts index c7d8a42c6e392..a9fc5861368ca 100644 --- a/test/api_integration/apis/telemetry/opt_in.ts +++ b/test/api_integration/apis/telemetry/opt_in.ts @@ -87,7 +87,7 @@ export default function optInTest({ getService }: FtrProviderContext) { } async function postTelemetryV2OptIn( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, value: unknown, statusCode: number ): Promise { diff --git a/test/api_integration/services/supertest.ts b/test/api_integration/services/supertest.ts index baf42703c64a0..d8ce0d918c45a 100644 --- a/test/api_integration/services/supertest.ts +++ b/test/api_integration/services/supertest.ts @@ -13,13 +13,15 @@ import { format as formatUrl } from 'url'; import supertest from 'supertest'; import { FtrProviderContext } from '../../functional/ftr_provider_context'; -export function KibanaSupertestProvider({ getService }: FtrProviderContext) { +export function KibanaSupertestProvider({ getService }: FtrProviderContext): supertest.Agent { const config = getService('config'); const kibanaServerUrl = formatUrl(config.get('servers.kibana')); return supertest(kibanaServerUrl); } -export function ElasticsearchSupertestProvider({ getService }: FtrProviderContext) { +export function ElasticsearchSupertestProvider({ + getService, +}: FtrProviderContext): supertest.Agent { const config = getService('config'); const esServerConfig = config.get('servers.elasticsearch'); diff --git a/test/common/services/bsearch.ts b/test/common/services/bsearch.ts index fd79a9b9a75e8..fd9ef7f703a0b 100644 --- a/test/common/services/bsearch.ts +++ b/test/common/services/bsearch.ts @@ -40,7 +40,7 @@ const getSpaceUrlPrefix = (spaceId?: string): string => { * Options for the send method */ interface SendOptions { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; options: object; strategy: string; space?: string; diff --git a/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts b/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts index 43b31c588f422..9cab31e30af64 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts @@ -74,7 +74,7 @@ describe('APMEventClient', () => { resolve(undefined); }, 100); }); - incomingRequest.abort(); + void incomingRequest.abort(); }, 100); }); diff --git a/x-pack/test/alerting_api_integration/common/lib/wait_for_execution_count.ts b/x-pack/test/alerting_api_integration/common/lib/wait_for_execution_count.ts index 6ad0dab431d81..3cd95484e1d5c 100644 --- a/x-pack/test/alerting_api_integration/common/lib/wait_for_execution_count.ts +++ b/x-pack/test/alerting_api_integration/common/lib/wait_for_execution_count.ts @@ -13,7 +13,7 @@ async function delay(millis: number): Promise { } export function createWaitForExecutionCount( - st: supertest.SuperTest, + st: supertest.Agent, spaceId?: string, delayMs: number = 3000 ) { diff --git a/x-pack/test/alerting_api_integration/observability/helpers/alerting_api_helper.ts b/x-pack/test/alerting_api_integration/observability/helpers/alerting_api_helper.ts index bd6c7761a5fcd..1a66aa2fcd9ed 100644 --- a/x-pack/test/alerting_api_integration/observability/helpers/alerting_api_helper.ts +++ b/x-pack/test/alerting_api_integration/observability/helpers/alerting_api_helper.ts @@ -6,7 +6,7 @@ */ import type { Client } from '@elastic/elasticsearch'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { ToolingLog } from '@kbn/tooling-log'; import { ThresholdParams } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { refreshSavedObjectIndices } from './refresh_index'; @@ -17,7 +17,7 @@ export async function createIndexConnector({ indexName, logger, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name: string; indexName: string; logger: ToolingLog; @@ -51,7 +51,7 @@ export async function createRule({ logger, esClient, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleTypeId: string; name: string; params: Params; diff --git a/x-pack/test/alerting_api_integration/observability/helpers/alerting_wait_for_helpers.ts b/x-pack/test/alerting_api_integration/observability/helpers/alerting_wait_for_helpers.ts index 63960b222cede..032435a03e007 100644 --- a/x-pack/test/alerting_api_integration/observability/helpers/alerting_wait_for_helpers.ts +++ b/x-pack/test/alerting_api_integration/observability/helpers/alerting_wait_for_helpers.ts @@ -29,7 +29,7 @@ export async function waitForRuleStatus({ }: { id: string; expectedStatus: string; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; retryService: RetryService; logger: ToolingLog; }): Promise> { diff --git a/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts b/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts index 1f84b2556ab70..3fdceed845856 100644 --- a/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts +++ b/x-pack/test/alerting_api_integration/observability/helpers/data_view.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { ToolingLog } from '@kbn/tooling-log'; export const createDataView = async ({ @@ -15,7 +15,7 @@ export const createDataView = async ({ title, logger, }: { - supertest: SuperTest; + supertest: SuperTestAgent; id: string; name: string; title: string; @@ -50,7 +50,7 @@ export const deleteDataView = async ({ id, logger, }: { - supertest: SuperTest; + supertest: SuperTestAgent; id: string; logger: ToolingLog; }) => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts index 9816059995142..7abc1b134f01d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { chunk, omit } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; @@ -16,7 +16,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const findTestUtils = ( describeType: 'internal' | 'public', objectRemover: ObjectRemover, - supertest: SuperTest, + supertest: SuperTestAgent, supertestWithoutAuth: any ) => { describe.skip(describeType, () => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts index 7b60595c4d66e..82956536c6d1a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/find_with_post.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { chunk, omit } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; import { UserAtSpaceScenarios } from '../../../scenarios'; @@ -16,7 +16,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const findTestUtils = ( describeType: 'internal' | 'public', objectRemover: ObjectRemover, - supertest: SuperTest, + supertest: SuperTestAgent, supertestWithoutAuth: any ) => { // FLAKY: https://github.com/elastic/kibana/issues/182314 diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts index 6d245ed28cc00..fa362249229e3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/get.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SuperuserAtSpace1, UserAtSpaceScenarios } from '../../../scenarios'; import { getUrlPrefix, @@ -19,7 +19,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const getTestUtils = ( describeType: 'internal' | 'public', objectRemover: ObjectRemover, - supertest: SuperTest, + supertest: SuperTestAgent, supertestWithoutAuth: any ) => { describe(describeType, () => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/crowdstrike.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/crowdstrike.ts index 7b4d064eb6b6e..c9d1ca8814592 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/crowdstrike.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/crowdstrike.ts @@ -131,7 +131,7 @@ export default function createCrowdstrikeTests({ getService }: FtrProviderContex password = 'changeme', errorLogger = logErrorDetails, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; subAction: string; subActionParams: Record; expectedHttpCode?: number; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/sentinelone.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/sentinelone.ts index 4b184ccb7a595..bf6f88be08fdf 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/sentinelone.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/sentinelone.ts @@ -140,7 +140,7 @@ export default function createSentinelOneTests({ getService }: FtrProviderContex password = 'changeme', errorLogger = logErrorDetails, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; subAction: string; subActionParams: Record; expectedHttpCode?: number; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts index 0a363e001535b..85e3b8405257f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts @@ -22,7 +22,7 @@ const createSubActionConnector = async ({ connectorTypeId = 'test.sub-action-connector', expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; config?: Record; secrets?: Record; connectorTypeId?: string; @@ -56,7 +56,7 @@ const executeSubAction = async ({ subActionParams, expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; connectorId: string; subAction: string; subActionParams: Record; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts index f3b3a4e7076ef..ad6a8d12baed3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/connector_types/stack/webhook.ts @@ -8,7 +8,7 @@ import http from 'http'; import https from 'https'; import getPort from 'get-port'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import expect from '@kbn/expect'; import { URL, format as formatUrl } from 'url'; import { @@ -177,7 +177,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { } export async function createWebhookAction( - supertest: SuperTest, + supertest: SuperTestAgent, webhookSimulatorURL: string, config: Record> = {} ): Promise { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts index a83ec1e70a0a1..d38bb2f40a275 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/find.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { fromKueryExpression } from '@kbn/es-query'; import { Spaces } from '../../../scenarios'; import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; @@ -14,7 +14,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; async function createAlert( objectRemover: ObjectRemover, - supertest: SuperTest, + supertest: SuperTestAgent, overwrites = {} ) { const { body: createdAlert } = await supertest @@ -28,7 +28,7 @@ async function createAlert( const findTestUtils = ( describeType: 'internal' | 'public', - supertest: SuperTest, + supertest: SuperTestAgent, objectRemover: ObjectRemover ) => { describe(describeType, () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get.ts index 51f246e2c5609..c22e8233f2d73 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group1/get.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { Spaces } from '../../../scenarios'; import { getUrlPrefix, getTestRuleData, ObjectRemover } from '../../../../common/lib'; import { FtrProviderContext } from '../../../../common/ftr_provider_context'; @@ -14,7 +14,7 @@ import { FtrProviderContext } from '../../../../common/ftr_provider_context'; const getTestUtils = ( describeType: 'internal' | 'public', objectRemover: ObjectRemover, - supertest: SuperTest + supertest: SuperTestAgent ) => { describe(describeType, () => { afterEach(() => objectRemover.removeAll()); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/test_helpers.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/test_helpers.ts index a898cc14b9104..b2196d9ea3724 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/test_helpers.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group3/test_helpers.ts @@ -8,7 +8,7 @@ import moment from 'moment'; import type { RetryService } from '@kbn/ftr-common-functional-services'; import type { IValidatedEvent } from '@kbn/event-log-plugin/server'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import expect from '@kbn/expect'; import type { FtrProviderContext } from '../../../../common/ftr_provider_context'; import { getUrlPrefix, getTestRuleData, ObjectRemover, getEventLog } from '../../../../common/lib'; @@ -23,7 +23,7 @@ export const createRule = async ({ }: { actionId: string; pattern: { instance: boolean[] }; - supertest: SuperTest; + supertest: SuperTestAgent; objectRemover: ObjectRemover; overwrites?: any; }) => { @@ -65,7 +65,7 @@ export const createAction = async ({ supertest, objectRemover, }: { - supertest: SuperTest; + supertest: SuperTestAgent; objectRemover: ObjectRemover; }) => { const { body: createdAction } = await supertest @@ -89,7 +89,7 @@ export const createMaintenanceWindow = async ({ objectRemover, }: { overwrites?: any; - supertest: SuperTest; + supertest: SuperTestAgent; objectRemover: ObjectRemover; }) => { const { body: window } = await supertest @@ -112,11 +112,7 @@ export const createMaintenanceWindow = async ({ return window; }; -export const getActiveMaintenanceWindows = async ({ - supertest, -}: { - supertest: SuperTest; -}) => { +export const getActiveMaintenanceWindows = async ({ supertest }: { supertest: SuperTestAgent }) => { const { body: activeMaintenanceWindows } = await supertest .get(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/maintenance_window/_active`) .set('kbn-xsrf', 'foo') @@ -130,7 +126,7 @@ export const finishMaintenanceWindow = async ({ supertest, }: { id: string; - supertest: SuperTest; + supertest: SuperTestAgent; }) => { return supertest .post( @@ -188,7 +184,7 @@ export const expectNoActionsFired = async ({ retry, }: { id: string; - supertest: SuperTest; + supertest: SuperTestAgent; retry: RetryService; }) => { const events = await retry.try(async () => { @@ -215,7 +211,7 @@ export const runSoon = async ({ retry, }: { id: string; - supertest: SuperTest; + supertest: SuperTestAgent; retry: RetryService; }) => { return retry.try(async () => { diff --git a/x-pack/test/api_integration/apis/asset_manager/tests/helpers.ts b/x-pack/test/api_integration/apis/asset_manager/tests/helpers.ts index 3d1086ca8b8e4..94ab2f5b99ffe 100644 --- a/x-pack/test/api_integration/apis/asset_manager/tests/helpers.ts +++ b/x-pack/test/api_integration/apis/asset_manager/tests/helpers.ts @@ -9,11 +9,11 @@ import type { AssetWithoutTimestamp } from '@kbn/assetManager-plugin/common/type import type { WriteSamplesPostBody } from '@kbn/assetManager-plugin/server'; import { apm, infra, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; const SAMPLE_ASSETS_ENDPOINT = '/api/asset-manager/assets/sample'; -export type KibanaSupertest = SuperTest; +export type KibanaSupertest = SuperTestAgent; // NOTE: In almost every case in tests, you want { refresh: true } // in the options of this function, so it is defaulted to that value. diff --git a/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts b/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts index 03a61807f1b31..5565e7a6e096b 100644 --- a/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts +++ b/x-pack/test/api_integration/apis/cloud_security_posture/helper.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { Client } from '@elastic/elasticsearch'; import expect from '@kbn/expect'; import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; @@ -43,7 +43,7 @@ export const addIndex = async (es: Client, findingsMock: T[], indexName: stri }; export async function createPackagePolicy( - supertest: SuperTest, + supertest: SuperTestAgent, agentPolicyId: string, policyTemplate: string, input: string, diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/templates.api.ts b/x-pack/test/api_integration/apis/management/index_management/lib/templates.api.ts index e929cbff2f188..bae578e6c0490 100644 --- a/x-pack/test/api_integration/apis/management/index_management/lib/templates.api.ts +++ b/x-pack/test/api_integration/apis/management/index_management/lib/templates.api.ts @@ -36,7 +36,9 @@ export function templatesApi(getService: FtrProviderContext['getService']) { .send(payload); // Delete all templates created during tests - const cleanUpTemplates = async (additionalRequestHeaders: object = {}) => { + const cleanUpTemplates = async ( + additionalRequestHeaders: Record = {} + ) => { try { await deleteTemplates(templatesCreated).set(additionalRequestHeaders); templatesCreated = []; diff --git a/x-pack/test/api_integration/apis/metrics_ui/helpers.ts b/x-pack/test/api_integration/apis/metrics_ui/helpers.ts index b5375b8d70cab..427909e10ac94 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/helpers.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/helpers.ts @@ -6,9 +6,6 @@ */ import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import { SuperTest, Test } from 'supertest'; - -export type KibanaSupertest = SuperTest; // generates traces, metrics for services export function generateServicesData({ diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry.ts b/x-pack/test/api_integration/apis/telemetry/telemetry.ts index 3652b32d9a372..203189906f812 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry.ts @@ -50,7 +50,7 @@ function getCacheDetails(body: UnencryptedTelemetryPayload): CacheDetails[] { * @param timestamp The new timestamp to be set */ function updateMonitoringDates( - esSupertest: SuperTest.SuperTest, + esSupertest: SuperTest.Agent, fromTimestamp: string, toTimestamp: string, timestamp: string diff --git a/x-pack/test/apm_api_integration/common/apm_api_supertest.ts b/x-pack/test/apm_api_integration/common/apm_api_supertest.ts index 9e4f25a7c7c7b..330de33dc7ab6 100644 --- a/x-pack/test/apm_api_integration/common/apm_api_supertest.ts +++ b/x-pack/test/apm_api_integration/common/apm_api_supertest.ts @@ -15,7 +15,7 @@ import type { import type { APIEndpoint } from '@kbn/apm-plugin/server'; import { formatRequest } from '@kbn/server-route-repository'; -export function createApmApiClient(st: supertest.SuperTest) { +export function createApmApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test/apm_api_integration/common/bettertest.ts b/x-pack/test/apm_api_integration/common/bettertest.ts index e1132be3f9a77..8665d200fd15f 100644 --- a/x-pack/test/apm_api_integration/common/bettertest.ts +++ b/x-pack/test/apm_api_integration/common/bettertest.ts @@ -30,7 +30,7 @@ export interface BetterTestResponse { * This is useful for tests that expect a 200 response * It also makes it easier to debug tests that fail because of a 500 response. */ -export function getBettertest(st: supertest.SuperTest) { +export function getBettertest(st: supertest.Agent) { return async ({ pathname, method = 'get', diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts index e4cc8d302ba4a..5da6ee4f860d0 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/alerting_api_helper.ts @@ -8,7 +8,7 @@ import { Client, errors } from '@elastic/elasticsearch'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; import pRetry from 'p-retry'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { ApmRuleType } from '@kbn/rule-data-utils'; import { ApmRuleParamsType } from '@kbn/apm-plugin/common/rules/schema'; import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; @@ -26,7 +26,7 @@ export async function createApmRule({ params, actions = [], }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleTypeId: T; name: string; params: ApmRuleParamsType[T]; @@ -111,7 +111,7 @@ export async function runRuleSoon({ supertest, }: { ruleId: string; - supertest: SuperTest; + supertest: SuperTestAgent; }): Promise> { return pRetry( async () => { @@ -143,13 +143,13 @@ export async function deleteRuleById({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { await supertest.delete(`/api/alerting/rule/${ruleId}`).set('kbn-xsrf', 'foo'); } -export async function deleteApmRules(supertest: SuperTest) { +export async function deleteApmRules(supertest: SuperTestAgent) { const res = await supertest.get( `/api/alerting/rules/_find?filter=alert.attributes.consumer:apm&per_page=10000` ); @@ -180,7 +180,7 @@ export async function createIndexConnector({ supertest, name, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name: string; }) { const { body } = await supertest @@ -228,7 +228,7 @@ export async function deleteAllActionConnectors({ supertest, es, }: { - supertest: SuperTest; + supertest: SuperTestAgent; es: Client; }): Promise { const res = await supertest.get(`/api/actions/connectors`); @@ -245,7 +245,7 @@ async function deleteActionConnector({ supertest, actionId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; actionId: string; }) { return supertest.delete(`/api/actions/connector/${actionId}`).set('kbn-xsrf', 'foo'); diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts index ae97266ee084c..2fae6c9643ff7 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/cleanup_rule_and_alert_state.ts @@ -7,7 +7,7 @@ import { Client } from '@elastic/elasticsearch'; import { ToolingLog } from '@kbn/tooling-log'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { clearKibanaApmEventLog, deleteApmRules, @@ -22,7 +22,7 @@ export async function cleanupRuleAndAlertState({ logger, }: { es: Client; - supertest: SuperTest; + supertest: SuperTestAgent; logger: ToolingLog; }) { try { diff --git a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_rule.ts b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_rule.ts index 29dc20840cec5..a2ee7253e9b8c 100644 --- a/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_rule.ts +++ b/x-pack/test/apm_api_integration/tests/alerts/helpers/wait_for_active_rule.ts @@ -17,7 +17,7 @@ export async function waitForActiveRule({ logger, }: { ruleId: string; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; logger?: ToolingLog; }): Promise> { return pRetry( diff --git a/x-pack/test/cases_api_integration/common/lib/alerts.ts b/x-pack/test/cases_api_integration/common/lib/alerts.ts index a74524c448493..8df074b0d3474 100644 --- a/x-pack/test/cases_api_integration/common/lib/alerts.ts +++ b/x-pack/test/cases_api_integration/common/lib/alerts.ts @@ -30,7 +30,7 @@ import { createComment, deleteAllComments } from './api'; import { postCaseReq } from './mock'; export const createSecuritySolutionAlerts = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, numberOfSignals: number = 1 ): Promise> => { @@ -47,7 +47,7 @@ export const createSecuritySolutionAlerts = async ( }; export const getSecuritySolutionAlerts = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, alertIds: string[] ): Promise> => { const { body: updatedAlert } = await supertest @@ -70,7 +70,7 @@ export const getAlertById = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; id: string; index: string; expectedHttpCode?: number; @@ -97,7 +97,7 @@ export const createCaseAttachAlertAndDeleteAlert = async ({ alerts, getAlerts, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; totalCases: number; indexOfCaseToDelete: number; owner: string; @@ -145,7 +145,7 @@ export const createCaseAttachAlertAndDeleteCase = async ({ alerts, getAlerts, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; totalCases: number; indicesOfCaseToDelete: number[]; owner: string; @@ -194,7 +194,7 @@ export const createCaseAndAttachAlert = async ({ alerts, getAlerts, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; totalCases: number; owner: string; alerts: Alerts; diff --git a/x-pack/test/cases_api_integration/common/lib/api/attachments.ts b/x-pack/test/cases_api_integration/common/lib/api/attachments.ts index 1dbee9fbd4616..2f598f5a23b11 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/attachments.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/attachments.ts @@ -33,7 +33,7 @@ export const bulkGetAttachments = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; attachmentIds: string[]; caseId: string; auth?: { user: User; space: string | null }; @@ -57,12 +57,12 @@ export const createComment = async ({ expectedHttpCode = 200, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; params: AttachmentRequest; auth?: { user: User; space: string | null } | null; expectedHttpCode?: number; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.post( `${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/${caseId}/comments` @@ -86,7 +86,7 @@ export const bulkCreateAttachments = async ({ auth = { user: superUser, space: null }, expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; params: BulkCreateAttachmentsRequest; auth?: { user: User; space: string | null }; @@ -110,7 +110,7 @@ export const createCaseAndBulkCreateAttachments = async ({ auth = { user: superUser, space: null }, expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; numberOfAttachments?: number; auth?: { user: User; space: string | null }; expectedHttpCode?: number; @@ -156,7 +156,7 @@ export const deleteComment = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; commentId: string; expectedHttpCode?: number; @@ -178,7 +178,7 @@ export const deleteAllComments = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -199,7 +199,7 @@ export const getAllComments = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; auth?: { user: User; space: string | null }; expectedHttpCode?: number; @@ -219,7 +219,7 @@ export const getComment = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; commentId: string; expectedHttpCode?: number; @@ -241,12 +241,12 @@ export const updateComment = async ({ auth = { user: superUser, space: null }, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; req: AttachmentPatchRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null } | null; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.patch( `${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/${caseId}/comments` @@ -269,7 +269,7 @@ export const bulkDeleteFileAttachments = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; fileIds: string[]; expectedHttpCode?: number; @@ -290,7 +290,7 @@ export const findAttachments = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; query?: Record; expectedHttpCode?: number; diff --git a/x-pack/test/cases_api_integration/common/lib/api/case.ts b/x-pack/test/cases_api_integration/common/lib/api/case.ts index a6605d8e83aab..dd1b668b429cf 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/case.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/case.ts @@ -15,11 +15,11 @@ import { superUser } from '../authentication/users'; import { getSpaceUrlPrefix, setupAuth } from './helpers'; export const createCase = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, params: CasePostRequest, expectedHttpCode: number = 200, auth: { user: User; space: string | null } | null = { user: superUser, space: null }, - headers: Record = {} + headers: Record = {} ): Promise => { const apiCall = supertest.post(`${getSpaceUrlPrefix(auth?.space)}${CASES_URL}`); @@ -44,7 +44,7 @@ export const deleteCases = async ({ expectedHttpCode = 204, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseIDs: string[]; expectedHttpCode?: number; auth?: { user: User; space: string | null }; diff --git a/x-pack/test/cases_api_integration/common/lib/api/configuration.ts b/x-pack/test/cases_api_integration/common/lib/api/configuration.ts index cfaa18b430c11..c74c6be78da82 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/configuration.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/configuration.ts @@ -60,11 +60,11 @@ export const getConfigurationOutput = (update = false, overwrite = {}): Partial< }; export const createConfiguration = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, req: ConfigurationRequest = getConfigurationRequest(), expectedHttpCode: number = 200, auth: { user: User; space: string | null } | null = { user: superUser, space: null }, - headers: Record = {} + headers: Record = {} ): Promise => { const apiCall = supertest.post(`${getSpaceUrlPrefix(auth?.space)}${CASE_CONFIGURE_URL}`); @@ -86,7 +86,7 @@ export const getConfiguration = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -102,12 +102,12 @@ export const getConfiguration = async ({ }; export const updateConfiguration = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, id: string, req: ConfigurationPatchRequest, expectedHttpCode: number = 200, auth: { user: User; space: string | null } | null = { user: superUser, space: null }, - headers: Record = {} + headers: Record = {} ): Promise => { const apiCall = supertest.patch(`${getSpaceUrlPrefix(auth?.space)}${CASE_CONFIGURE_URL}/${id}`); diff --git a/x-pack/test/cases_api_integration/common/lib/api/connectors.ts b/x-pack/test/cases_api_integration/common/lib/api/connectors.ts index 0eb6854cb735d..3bd959b031ae5 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/connectors.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/connectors.ts @@ -194,7 +194,7 @@ export const getCaseConnectors = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; expectedHttpCode?: number; auth?: { user: User; space: string | null }; }): Promise => { @@ -215,13 +215,13 @@ export const createCaseWithConnector = async ({ createCaseReq = getPostCaseRequest(), headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; serviceNowSimulatorURL: string; actionsRemover: ActionsRemover; configureReq?: Record; auth?: { user: User; space: string | null } | null; createCaseReq?: CasePostRequest; - headers?: Record; + headers?: Record; }): Promise<{ postedCase: Case; connector: CreateConnectorResponse; @@ -288,7 +288,7 @@ export const createConnector = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; req: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -309,7 +309,7 @@ export const getConnectors = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -329,7 +329,7 @@ export const executeConnector = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; connectorId: string; req: Record; expectedHttpCode?: number; @@ -352,7 +352,7 @@ export const executeSystemConnector = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; connectorId: string; req: Record; expectedHttpCode?: number; diff --git a/x-pack/test/cases_api_integration/common/lib/api/files.ts b/x-pack/test/cases_api_integration/common/lib/api/files.ts index 71951564c6a28..8e2710603020f 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/files.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/files.ts @@ -23,7 +23,7 @@ export const downloadFile = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; fileId: string; kind: string; mimeType: string; @@ -49,7 +49,7 @@ export const deleteFileForFileKind = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; fileKind: string; id: string; expectedHttpCode?: number; @@ -72,7 +72,7 @@ export const deleteFiles = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; files: string[]; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -91,7 +91,7 @@ export const deleteAllFilesForKind = async ({ auth = { user: superUser, space: null }, expectedHttpCode = 200, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; kind: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -124,7 +124,7 @@ export const deleteAllFiles = async ({ expectedHttpCode = 200, ignoreErrors = true, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; expectedHttpCode?: number; auth?: { user: User; space: string | null }; ignoreErrors?: boolean; @@ -165,7 +165,7 @@ export const getFileById = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; id: string; kind: string; expectedHttpCode?: number; @@ -185,7 +185,7 @@ export const listFiles = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; params: Parameters[0]; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -213,7 +213,7 @@ export const createFile = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; params: CreateFileSchema; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -238,7 +238,7 @@ export const uploadFile = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; data: string | object; kind: string; fileId: string; @@ -264,7 +264,7 @@ export const createAndUploadFile = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; data: string | object; createFileParams: CreateFileSchema; expectedHttpCode?: number; diff --git a/x-pack/test/cases_api_integration/common/lib/api/index.ts b/x-pack/test/cases_api_integration/common/lib/api/index.ts index 0b7f9c542a6d0..6582601f0f1a9 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/index.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/index.ts @@ -129,7 +129,7 @@ export const setStatus = async ({ supertest, cases, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; cases: SetStatusCasesParams[]; }): Promise => { const { body }: { body: Cases } = await supertest @@ -143,7 +143,7 @@ export const setStatus = async ({ /** * Add case as a connector */ -export const createCaseAction = async (supertest: SuperTest.SuperTest) => { +export const createCaseAction = async (supertest: SuperTest.Agent) => { const { body: createdAction } = await supertest .post('/api/actions/connector') .set('kbn-xsrf', 'foo') @@ -159,10 +159,7 @@ export const createCaseAction = async (supertest: SuperTest.SuperTest, - id: string -) => { +export const deleteCaseAction = async (supertest: SuperTest.Agent, id: string) => { await supertest.delete(`/api/actions/connector/${id}`).set('kbn-xsrf', 'foo'); }; @@ -418,11 +415,11 @@ export const updateCase = async ({ auth = { user: superUser, space: null }, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; params: CasesPatchRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null } | null; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.patch(`${getSpaceUrlPrefix(auth?.space)}${CASES_URL}`); @@ -444,7 +441,7 @@ export const getAllCasesStatuses = async ({ auth = { user: superUser, space: null }, query = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; expectedHttpCode?: number; auth?: { user: User; space: string | null }; query?: Record; @@ -465,7 +462,7 @@ export const getCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; includeComments?: boolean; expectedHttpCode?: number; @@ -490,7 +487,7 @@ export const getCaseMetrics = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; features: CaseMetricsFeature[] | CaseMetricsFeature; expectedHttpCode?: number; @@ -512,7 +509,7 @@ export const resolveCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; includeComments?: boolean; expectedHttpCode?: number; @@ -537,7 +534,7 @@ export const findCases = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -560,7 +557,7 @@ export const getCasesByAlert = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; alertID: string; query?: Record; expectedHttpCode?: number; @@ -581,7 +578,7 @@ export const getTags = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -602,7 +599,7 @@ export const getReporters = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -623,7 +620,7 @@ export const getCategories = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; query?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -646,12 +643,12 @@ export const pushCase = async ({ auth = { user: superUser, space: null }, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; connectorId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null } | null; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.post( `${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/${caseId}/connector/${connectorId}/_push` @@ -674,7 +671,7 @@ export const getAlertsAttachedToCase = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -727,7 +724,7 @@ export const getCasesMetrics = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; features: string[] | string; query?: Record; expectedHttpCode?: number; @@ -774,7 +771,7 @@ export const bulkGetCases = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; ids: string[]; fields?: string[]; expectedHttpCode?: number; @@ -796,7 +793,7 @@ export const searchCases = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; body?: Record; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -820,13 +817,13 @@ export const replaceCustomField = async ({ auth = { user: superUser, space: null }, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; customFieldId: string; params: CustomFieldPutRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null } | null; - headers?: Record; + headers?: Record; }): Promise => { const apiCall = supertest.put( `${getSpaceUrlPrefix( diff --git a/x-pack/test/cases_api_integration/common/lib/api/user_actions.ts b/x-pack/test/cases_api_integration/common/lib/api/user_actions.ts index fc4f8382d3508..4a0f38b5dcc90 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/user_actions.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/user_actions.ts @@ -42,7 +42,7 @@ export const getCaseUserActions = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseID: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -61,7 +61,7 @@ export const findCaseUserActions = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseID: string; options?: UserActionFindRequest; expectedHttpCode?: number; @@ -82,7 +82,7 @@ export const getCaseUserActionStats = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseID: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -101,7 +101,7 @@ export const getCaseUsers = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; caseId: string; expectedHttpCode?: number; auth?: { user: User; space: string | null }; diff --git a/x-pack/test/cases_api_integration/common/lib/api/user_profiles.ts b/x-pack/test/cases_api_integration/common/lib/api/user_profiles.ts index bc78feab4070c..f9a66fe0f7962 100644 --- a/x-pack/test/cases_api_integration/common/lib/api/user_profiles.ts +++ b/x-pack/test/cases_api_integration/common/lib/api/user_profiles.ts @@ -38,7 +38,7 @@ export const bulkGetUserProfiles = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; req: BulkGetUserProfilesParams; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -62,7 +62,7 @@ export const suggestUserProfiles = async ({ expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; req: SuggestUserProfilesRequest; expectedHttpCode?: number; auth?: { user: User; space: string | null }; @@ -89,10 +89,10 @@ export const updateUserProfileAvatar = async ({ expectedHttpCode = 200, headers = {}, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; req: UserProfileAvatarData; expectedHttpCode?: number; - headers?: Record; + headers?: Record; }): Promise => { await supertest .post('/internal/security/user_profile/_data') @@ -106,7 +106,7 @@ export const loginUsers = async ({ supertest, users = [superUser], }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; users?: User[]; }) => { const cookies: Cookie[] = []; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/bulk_create_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/bulk_create_cases.ts index 8177649f22ab3..c07d858e4b389 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/bulk_create_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/bulk_create_cases.ts @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext): void => { expectedHttpCode = 200, auth = { user: superUser, space: null }, }: { - superTestService?: SuperTest.SuperTest; + superTestService?: SuperTest.Agent; data: object; expectedHttpCode?: number; auth?: { user: User; space: string | null }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts index 4c3239fe0d126..ef0e09f1a477d 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/delete_cases.ts @@ -700,7 +700,7 @@ const createCaseWithFiles = async ({ owner, auth = { user: superUser, space: null }, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; fileKind: string; owner: string; auth?: { user: User; space: string | null }; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts index 060b6463c0451..3aa11c83947ad 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/common/cases/import_export.ts @@ -161,7 +161,7 @@ export default ({ getService }: FtrProviderContext): void => { }); }; -const expectImportToHaveOneCase = async (supertestService: supertest.SuperTest) => { +const expectImportToHaveOneCase = async (supertestService: supertest.Agent) => { const findResponse = await findCases({ supertest: supertestService, query: {} }); expect(findResponse.total).to.eql(1); expect(findResponse.cases[0].title).to.eql('A case with a connector'); diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/connectors/cases/cases_connector.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/connectors/cases/cases_connector.ts index b0fcd30fa5a42..060222473c15b 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/connectors/cases/cases_connector.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/connectors/cases/cases_connector.ts @@ -1352,7 +1352,7 @@ const executeConnectorAndVerifyCorrectness = async ({ connectorId, req, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; connectorId: string; req: Record; }) => { diff --git a/x-pack/test/common/services/bsearch_secure.ts b/x-pack/test/common/services/bsearch_secure.ts index 0ab967462767f..01050d1bb60c0 100644 --- a/x-pack/test/common/services/bsearch_secure.ts +++ b/x-pack/test/common/services/bsearch_secure.ts @@ -28,7 +28,7 @@ const getSpaceUrlPrefix = (spaceId?: string): string => { }; interface SendOptions { - supertestWithoutAuth: SuperTest.SuperTest; + supertestWithoutAuth: SuperTest.Agent; auth: { username: string; password: string }; referer?: string; kibanaVersion?: string; diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/create_alerts_index.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/create_alerts_index.ts index 932db176942d9..da7f7b2cf36a6 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/create_alerts_index.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/create_alerts_index.ts @@ -17,7 +17,7 @@ import { countDownTest } from '../count_down_test'; * @param supertest The supertest client library */ export const createAlertsIndex = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { await countDownTest( diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/delete_all_alerts.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/delete_all_alerts.ts index e15aa4453282a..f29237b237694 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/delete_all_alerts.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/delete_all_alerts.ts @@ -16,7 +16,7 @@ import { countDownTest } from '../count_down_test'; * For use inside of afterEach blocks of tests */ export const deleteAllAlerts = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, es: Client, index: Array<'.alerts-security.alerts-*' | '.preview.alerts-security.alerts-*'> = [ diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_id.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_id.ts index 1a3f7e29c26c6..92791edd5ea7e 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_id.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_id.ts @@ -20,7 +20,7 @@ import { getQueryAlertsId } from './get_query_alerts_ids'; * @param ids Rule id */ export const getAlertsById = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, id: string ): Promise> => { diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_ids.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_ids.ts index 1cfc922d76677..d090a39ff3779 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_ids.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/get_alerts_by_ids.ts @@ -23,7 +23,7 @@ import { routeWithNamespace } from '../route_with_namespace'; * @param ids Array of the rule ids */ export const getAlertsByIds = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, ids: string[], size?: number, diff --git a/x-pack/test/common/utils/security_solution/detections_response/alerts/wait_for_alerts_to_be_present.ts b/x-pack/test/common/utils/security_solution/detections_response/alerts/wait_for_alerts_to_be_present.ts index f7e873f98d4c7..5772861f6e636 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/alerts/wait_for_alerts_to_be_present.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/alerts/wait_for_alerts_to_be_present.ts @@ -18,7 +18,7 @@ import { waitFor } from '../wait_for'; * @param numberOfAlerts The number of alerts to wait for, default is 1 */ export const waitForAlertsToBePresent = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, numberOfAlerts = 1, alertIds: string[], diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/create_rule.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/create_rule.ts index ac6e44b2aab83..b1fe58770abfd 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/create_rule.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/create_rule.ts @@ -27,7 +27,7 @@ import { routeWithNamespace } from '../route_with_namespace'; * @param rule The rule to create */ export const createRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, rule: RuleCreateProps, namespace?: string diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/delete_all_rules.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/delete_all_rules.ts index 8fe87827e4e25..66bf65ccfbc20 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/delete_all_rules.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/delete_all_rules.ts @@ -19,7 +19,7 @@ import { countDownTest } from '../count_down_test'; * @param supertest The supertest agent. */ export const deleteAllRules = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { await countDownTest( diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/delete_rule.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/delete_rule.ts index f4eff397aba0b..5b3e28f05e093 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/delete_rule.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/delete_rule.ts @@ -17,7 +17,7 @@ import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common * @param ruleId The rule id to delete */ export const deleteRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, ruleId: string ): Promise => { const response = await supertest diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/wait_for_rule_status.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/wait_for_rule_status.ts index af0838b29613d..975b8dbef3b72 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/wait_for_rule_status.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/wait_for_rule_status.ts @@ -16,7 +16,7 @@ import { waitFor } from '../wait_for'; import { routeWithNamespace } from '../route_with_namespace'; interface WaitForRuleStatusBaseParams { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; afterDate?: Date; namespace?: string; diff --git a/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts b/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts index 79818e970a2ab..66823c794b017 100644 --- a/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts +++ b/x-pack/test/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts @@ -12,7 +12,7 @@ import type { APIEndpoint } from '@kbn/dataset-quality-plugin/server/routes'; import { formatRequest } from '@kbn/server-route-repository'; import type { APIClientRequestParamsOf, APIReturnType } from '@kbn/dataset-quality-plugin/common'; -export function createDatasetQualityApiClient(st: supertest.SuperTest) { +export function createDatasetQualityApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test/dataset_quality_api_integration/tests/integrations/package_utils.ts b/x-pack/test/dataset_quality_api_integration/tests/integrations/package_utils.ts index 4aa403ee6d62a..ae2b9a01fa475 100644 --- a/x-pack/test/dataset_quality_api_integration/tests/integrations/package_utils.ts +++ b/x-pack/test/dataset_quality_api_integration/tests/integrations/package_utils.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; export interface IntegrationPackage { name: string; @@ -26,7 +26,7 @@ export async function installCustomIntegration({ supertest, customIntegration, }: { - supertest: SuperTest; + supertest: SuperTestAgent; customIntegration: CustomIntegration; }) { const { integrationName, datasets } = customIntegration; @@ -41,7 +41,7 @@ export async function installPackage({ supertest, pkg, }: { - supertest: SuperTest; + supertest: SuperTestAgent; pkg: IntegrationPackage; }) { const { name, version } = pkg; @@ -56,7 +56,7 @@ export async function uninstallPackage({ supertest, pkg, }: { - supertest: SuperTest; + supertest: SuperTestAgent; pkg: IntegrationPackage; }) { const { name, version } = pkg; diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts index f086ad8785d8d..6fe31c67c65a2 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_by_upload.ts @@ -9,6 +9,7 @@ import fs from 'fs'; import path from 'path'; import expect from '@kbn/expect'; import { INGEST_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { HTTPError } from 'superagent'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; @@ -167,7 +168,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/gzip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Archive seems empty. Assumed content type was application/gzip, check if this matches the archive type."}' ); }); @@ -180,7 +181,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Error during extraction of package: Error: end of central directory record signature not found. Assumed content type was application/zip, check if this matches the archive type."}' ); }); @@ -193,7 +194,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Package contains more than one top-level directory; top-level directory found: apache-0.1.4; filePath: apache-0.1.3/manifest.yml"}' ); }); @@ -206,7 +207,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Manifest file apache-0.1.4/manifest.yml not found in paths."}' ); }); @@ -219,7 +220,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Could not parse top-level package manifest at top-level directory apache-0.1.4: YAMLException: bad indentation of a mapping entry at line 2, column 7:\\n name: apache\\n ^."}' ); }); @@ -232,7 +233,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Invalid top-level package manifest at top-level directory apache-0.1.4 (package name: apache): one or more fields missing of name, version, description, title, format_version, owner."}' ); }); @@ -245,7 +246,7 @@ export default function (providerContext: FtrProviderContext) { .type('application/zip') .send(buf) .expect(400); - expect(res.error.text).to.equal( + expect((res.error as HTTPError).text).to.equal( '{"statusCode":400,"error":"Bad Request","message":"Name thisIsATypo and version 0.1.4 do not match top-level directory apache-0.1.4"}' ); }); diff --git a/x-pack/test/fleet_api_integration/helpers.ts b/x-pack/test/fleet_api_integration/helpers.ts index bf8729dba368b..2d9febe190892 100644 --- a/x-pack/test/fleet_api_integration/helpers.ts +++ b/x-pack/test/fleet_api_integration/helpers.ts @@ -15,7 +15,7 @@ import { } from '@kbn/fleet-plugin/common'; import { KbnClient } from '@kbn/test'; import { UNINSTALL_TOKENS_SAVED_OBJECT_TYPE } from '@kbn/fleet-plugin/common'; -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { FtrProviderContext } from '../api_integration/ftr_provider_context'; export function warnAndSkipTest(mochaContext: Mocha.Context, log: ToolingLog) { @@ -109,7 +109,7 @@ export async function generateAgent( }); } -export function setPrereleaseSetting(supertest: SuperTest) { +export function setPrereleaseSetting(supertest: SuperTestAgent) { before(async () => { await supertest .put('/api/fleet/settings') @@ -126,7 +126,7 @@ export function setPrereleaseSetting(supertest: SuperTest) { } export const generateNAgentPolicies = async ( - supertest: SuperTest, + supertest: SuperTestAgent, number: number, overwrite?: Partial ): Promise => { @@ -142,7 +142,7 @@ export const generateNAgentPolicies = async ( }; export const generateAgentPolicy = async ( - supertest: SuperTest, + supertest: SuperTestAgent, overwrite?: Partial ): Promise => { const response = await supertest diff --git a/x-pack/test/functional/services/transform/api.ts b/x-pack/test/functional/services/transform/api.ts index 5aad29fff8942..15751c91253ec 100644 --- a/x-pack/test/functional/services/transform/api.ts +++ b/x-pack/test/functional/services/transform/api.ts @@ -237,7 +237,7 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { ); const { body, status } = await esSupertest .put(`/_transform/${transformId}${deferValidation ? '?defer_validation=true' : ''}`) - .set(headers) + .set(headers as Record) .send(transformConfig); this.assertResponseStatusCode(200, status, body); } else { diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts index c45df81316e81..876e4d2123019 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/group2/attachment_framework.ts @@ -24,7 +24,7 @@ import { import { FtrProviderContext } from '../../../ftr_provider_context'; const createLogStashDataView = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise<{ data_view: { id: string } }> => { const { body } = await supertest .post(`/api/data_views/data_view`) @@ -36,7 +36,7 @@ const createLogStashDataView = async ( }; const deleteLogStashDataView = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, dataViewId: string ): Promise => { await supertest diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/utils.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/utils.ts index 09dffef846cd5..e96ec17eb9c2e 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/utils.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors/utils.ts @@ -48,10 +48,7 @@ export const createSlackConnector = async ({ return connector; }; -export const getConnectorByName = async ( - name: string, - supertest: SuperTest.SuperTest -) => { +export const getConnectorByName = async (name: string, supertest: SuperTest.Agent) => { const { body } = await supertest .get(`/api/actions/connectors`) .set('kbn-xsrf', 'foo') @@ -65,7 +62,7 @@ export async function createRuleWithActionsAndParams( testRunUuid: string, params: Record = {}, overwrites: Record = {}, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ) { return await createAlwaysFiringRule( { @@ -94,7 +91,7 @@ export async function createRuleWithActionsAndParams( async function createAlwaysFiringRule( overwrites: Record = {}, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ) { const { body: createdRule } = await supertest .post(`/api/alerting/rule`) @@ -109,10 +106,7 @@ async function createAlwaysFiringRule( return createdRule; } -export async function getAlertSummary( - ruleId: string, - supertest: SuperTest.SuperTest -) { +export async function getAlertSummary(ruleId: string, supertest: SuperTest.Agent) { const { body: summary } = await supertest .get(`/internal/alerting/rule/${encodeURIComponent(ruleId)}/_alert_summary`) .expect(200); diff --git a/x-pack/test/monitoring_api_integration/packages.ts b/x-pack/test/monitoring_api_integration/packages.ts index 284bba089a55c..981845eb460dc 100644 --- a/x-pack/test/monitoring_api_integration/packages.ts +++ b/x-pack/test/monitoring_api_integration/packages.ts @@ -30,10 +30,7 @@ export const getPackagesArgs = (): string[] => { export const bundledPackagesLocation = path.join(path.dirname(__filename), '/fixtures/packages'); -export function installPackage( - supertest: SuperTest.SuperTest, - packageName: SupportedPackage -) { +export function installPackage(supertest: SuperTest.Agent, packageName: SupportedPackage) { const pkg = PACKAGES.find(({ name }) => name === packageName); const request = supertest .post('/api/fleet/epm/packages') diff --git a/x-pack/test/observability_ai_assistant_api_integration/common/observability_ai_assistant_api_client.ts b/x-pack/test/observability_ai_assistant_api_integration/common/observability_ai_assistant_api_client.ts index cb59adca9a86a..865620a2d028a 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/common/observability_ai_assistant_api_client.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/common/observability_ai_assistant_api_client.ts @@ -15,7 +15,7 @@ import supertest from 'supertest'; import { format } from 'url'; import { Subtract } from 'utility-types'; -export function createObservabilityAIAssistantApiClient(st: supertest.SuperTest) { +export function createObservabilityAIAssistantApiClient(st: supertest.Agent) { return ( options: { type?: 'form-data'; @@ -70,17 +70,62 @@ type WithoutPromise> = Subtract>; // end(one:string) // end(one:string, two:string) // } -// would lose the first signature. This keeps up to four signatures. +// would lose the first signature. This keeps up to eight signatures. type OverloadedParameters = T extends { (...args: infer A1): any; (...args: infer A2): any; (...args: infer A3): any; (...args: infer A4): any; + (...args: infer A5): any; + (...args: infer A6): any; + (...args: infer A7): any; + (...args: infer A8): any; } + ? A1 | A2 | A3 | A4 | A5 | A6 | A7 | A8 + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + (...args: infer A4): any; + (...args: infer A5): any; + (...args: infer A6): any; + (...args: infer A7): any; + } + ? A1 | A2 | A3 | A4 | A5 | A6 | A7 + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + (...args: infer A4): any; + (...args: infer A5): any; + (...args: infer A6): any; + } + ? A1 | A2 | A3 | A4 | A5 | A6 + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + (...args: infer A4): any; + (...args: infer A5): any; + } + ? A1 | A2 | A3 | A4 | A5 + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + (...args: infer A4): any; + } ? A1 | A2 | A3 | A4 - : T extends { (...args: infer A1): any; (...args: infer A2): any; (...args: infer A3): any } + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + (...args: infer A3): any; + } ? A1 | A2 | A3 - : T extends { (...args: infer A1): any; (...args: infer A2): any } + : T extends { + (...args: infer A1): any; + (...args: infer A2): any; + } ? A1 | A2 : T extends (...args: infer A) => any ? A diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/connectors/connectors.spec.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/connectors/connectors.spec.ts index d5e726012c869..d51edffc9a1a8 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/connectors/connectors.spec.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/connectors/connectors.spec.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function ApiTest({ getService }: FtrProviderContext) { @@ -71,7 +71,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); } -export async function deleteAllActionConnectors(supertest: SuperTest): Promise { +export async function deleteAllActionConnectors(supertest: SuperTestAgent): Promise { const res = await supertest.get(`/api/actions/connectors`); const body = res.body as Array<{ id: string; connector_type_id: string; name: string }>; diff --git a/x-pack/test/observability_api_integration/common/obs_api_supertest.ts b/x-pack/test/observability_api_integration/common/obs_api_supertest.ts index e0788dcd6785d..69d7083f838e4 100644 --- a/x-pack/test/observability_api_integration/common/obs_api_supertest.ts +++ b/x-pack/test/observability_api_integration/common/obs_api_supertest.ts @@ -24,7 +24,7 @@ export type APIClientRequestParamsOf = ClientRequ TEndpoint >; -export function createObsApiClient(st: supertest.SuperTest) { +export function createObsApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test/observability_onboarding_api_integration/common/observability_onboarding_api_supertest.ts b/x-pack/test/observability_onboarding_api_integration/common/observability_onboarding_api_supertest.ts index c679c91e96525..199230e92c4ae 100644 --- a/x-pack/test/observability_onboarding_api_integration/common/observability_onboarding_api_supertest.ts +++ b/x-pack/test/observability_onboarding_api_integration/common/observability_onboarding_api_supertest.ts @@ -15,7 +15,7 @@ import type { import type { APIEndpoint } from '@kbn/observability-onboarding-plugin/server/routes'; import { formatRequest } from '@kbn/server-route-repository'; -export function createObservabilityOnboardingApiClient(st: supertest.SuperTest) { +export function createObservabilityOnboardingApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test/profiling_api_integration/common/api_supertest.ts b/x-pack/test/profiling_api_integration/common/api_supertest.ts index 11d7564248caf..dcd5c892d3b77 100644 --- a/x-pack/test/profiling_api_integration/common/api_supertest.ts +++ b/x-pack/test/profiling_api_integration/common/api_supertest.ts @@ -10,7 +10,7 @@ import request from 'superagent'; import supertest from 'supertest'; import { format } from 'url'; -export function createProfilingApiClient(st: supertest.SuperTest) { +export function createProfilingApiClient(st: supertest.Agent) { return async (options: { endpoint: string; params?: { diff --git a/x-pack/test/profiling_api_integration/common/bettertest.ts b/x-pack/test/profiling_api_integration/common/bettertest.ts index ca679a24539ac..ec2fd13763853 100644 --- a/x-pack/test/profiling_api_integration/common/bettertest.ts +++ b/x-pack/test/profiling_api_integration/common/bettertest.ts @@ -23,7 +23,7 @@ export type BetterTest = (options: { * This is useful for tests that expect a 200 response * It also makes it easier to debug tests that fail because of a 500 response. */ -export function getBettertest(st: supertest.SuperTest): BetterTest { +export function getBettertest(st: supertest.Agent): BetterTest { return async ({ pathname, method = 'get', query, body }) => { const url = format({ pathname, query }); @@ -60,7 +60,7 @@ export class BetterTestError extends Error { const req = res.req as any; super( `Unhandled BetterTestError: -Status: "${res.status}" +Status: "${res.status}" Path: "${req.method} ${req.path}" Body: ${JSON.stringify(res.body)}` ); diff --git a/x-pack/test/saved_object_api_integration/common/lib/create_users_and_roles.ts b/x-pack/test/saved_object_api_integration/common/lib/create_users_and_roles.ts index e0fed03efb94d..eb7a729ff5c96 100644 --- a/x-pack/test/saved_object_api_integration/common/lib/create_users_and_roles.ts +++ b/x-pack/test/saved_object_api_integration/common/lib/create_users_and_roles.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { SuperTest } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import { AUTHENTICATION } from './authentication'; -export const createUsersAndRoles = async (es: Client, supertest: SuperTest) => { +export const createUsersAndRoles = async (es: Client, supertest: SuperTestAgent) => { await supertest .put('/api/security/role/kibana_legacy_user') .send({ diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts index 38a4a8d8db8c7..6b79b34489111 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts @@ -258,7 +258,7 @@ export function bulkCreateTestSuiteFactory(context: FtrProviderContext) { const query = test.overwrite ? '?overwrite=true' : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_create${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_delete.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_delete.ts index 578f8a4e0cd1f..f7fe4ba4061ce 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_delete.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_delete.ts @@ -138,7 +138,7 @@ export function bulkDeleteTestSuiteFactory(context: FtrProviderContext) { const query = testForce && testForce === true ? '?force=true' : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_delete${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts index 15a51c3db3364..d25d1e0a5d87c 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_get.ts @@ -125,7 +125,7 @@ export function bulkGetTestSuiteFactory(context: FtrProviderContext) { it(`should return ${test.responseStatusCode} ${test.title}`, async () => { await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_get`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(test.request) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_resolve.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_resolve.ts index a203865e294aa..b3c8669d48216 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_resolve.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_resolve.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { TEST_CASES } from './resolve'; import { SPACES } from '../lib/spaces'; import { @@ -29,7 +29,7 @@ export interface BulkResolveTestCase extends TestCase { export { TEST_CASES }; // re-export the (non-bulk) resolve test cases -export function bulkResolveTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function bulkResolveTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('bulk_get'); const expectResponseBody = ( @@ -119,7 +119,7 @@ export function bulkResolveTestSuiteFactory(esArchiver: any, supertest: SuperTes it(`should return ${test.responseStatusCode} ${test.title}`, async () => { await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_resolve`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(test.request) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts index e79089615e7ee..384b8db8aa0a7 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_update.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; import { expectResponses, getUrlPrefix, getTestTitle } from '../lib/saved_object_test_utils'; @@ -36,7 +36,7 @@ const createRequest = ({ type, id, namespace }: BulkUpdateTestCase) => ({ ...(namespace && { namespace }), // individual "object namespace" string }); -export function bulkUpdateTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function bulkUpdateTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('bulk_update'); const expectResponseBody = ( @@ -118,7 +118,7 @@ export function bulkUpdateTestSuiteFactory(esArchiver: any, supertest: SuperTest const requestBody = test.request.map((x) => ({ ...x, ...attrs })); await supertest .put(`${getUrlPrefix(spaceId)}/api/saved_objects/_bulk_update`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/create.ts b/x-pack/test/saved_object_api_integration/common/suites/create.ts index dfad5e638a708..b12fef1c7cade 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/create.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/create.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES, ALL_SPACES_ID } from '../lib/spaces'; import { @@ -85,7 +85,7 @@ const createRequest = ({ type, id, initialNamespaces }: CreateTestCase) => ({ initialNamespaces, }); -export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function createTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('create'); const expectResponseBody = (testCase: CreateTestCase, user?: TestUser): ExpectResponseBody => @@ -155,7 +155,7 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest = Object.freeze({ */ const createRequest = ({ type, id, force }: DeleteTestCase) => ({ type, id, force }); -export function deleteTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTest) { +export function deleteTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('delete'); const expectResponseBody = (testCase: DeleteTestCase): ExpectResponseBody => @@ -118,7 +118,7 @@ export function deleteTestSuiteFactory(es: Client, esArchiver: any, supertest: S await supertest .delete(`${getUrlPrefix(spaceId)}/api/saved_objects/${type}/${id}`) .query({ ...(force && { force }) }) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .expect(test.responseStatusCode) .then(test.responseBody); }); diff --git a/x-pack/test/saved_object_api_integration/common/suites/export.ts b/x-pack/test/saved_object_api_integration/common/suites/export.ts index 6df4c2f8dd12c..b7936f94a98fb 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/export.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/export.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES, CONFLICT_TEST_CASES, @@ -154,7 +154,7 @@ const EMPTY_RESULT = { missingReferences: [], }; -export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbiddenBulkGet = expectResponses.forbiddenTypes('bulk_get'); const expectResponseBody = (testCase: ExportTestCase): ExpectResponseBody => @@ -259,7 +259,7 @@ export function exportTestSuiteFactory(esArchiver: any, supertest: SuperTest { await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_export`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(test.request) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/find.ts b/x-pack/test/saved_object_api_integration/common/suites/find.ts index 202fc02dbfc1c..c7afc205f5b6f 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/find.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/find.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import querystring from 'querystring'; import { SAVED_OBJECT_TEST_CASES, @@ -187,7 +187,7 @@ export const createRequest = ({ query }: FindTestCase) => ({ query }); const getTestTitle = ({ failure, title }: FindTestCase) => `${failure?.reason || 'success'} ["${title}"]`; -export function findTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function findTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectResponseBody = (testCase: FindTestCase, user?: TestUser): ExpectResponseBody => async (response: Record) => { @@ -298,7 +298,7 @@ export function findTestSuiteFactory(esArchiver: any, supertest: SuperTest) await supertest .get(`${getUrlPrefix(spaceId)}/api/saved_objects/_find${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .expect(test.responseStatusCode) .then(test.responseBody); }); diff --git a/x-pack/test/saved_object_api_integration/common/suites/get.ts b/x-pack/test/saved_object_api_integration/common/suites/get.ts index b106930103f84..024f061d2cb23 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/get.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/get.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; import { @@ -25,7 +25,7 @@ export type GetTestCase = TestCase; const DOES_NOT_EXIST = Object.freeze({ type: 'dashboard', id: 'does-not-exist' }); export const TEST_CASES: Record = Object.freeze({ ...CASES, DOES_NOT_EXIST }); -export function getTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function getTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('get'); const expectResponseBody = (testCase: GetTestCase): ExpectResponseBody => @@ -81,7 +81,7 @@ export function getTestSuiteFactory(esArchiver: any, supertest: SuperTest) const { type, id } = test.request; await supertest .get(`${getUrlPrefix(spaceId)}/api/saved_objects/${type}/${id}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .expect(test.responseStatusCode) .then(test.responseBody); }); diff --git a/x-pack/test/saved_object_api_integration/common/suites/import.ts b/x-pack/test/saved_object_api_integration/common/suites/import.ts index 7580521e170a6..1af1bf07510b2 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/import.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/import.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { SavedObjectReference } from '@kbn/core/server'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; @@ -121,7 +121,7 @@ export const importTestCaseFailures = { condition !== false ? { failureType: 'missing_references' } : {}, }; -export function importTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTest) { +export function importTestSuiteFactory(es: Client, esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = (action: string, typeOrTypes: string | string[]) => expectResponses.forbiddenTypes(action)(typeOrTypes); const expectResponseBody = @@ -310,7 +310,7 @@ export function importTestSuiteFactory(es: Client, esArchiver: any, supertest: S : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_import${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .attach('file', Buffer.from(requestBody, 'utf8'), 'export.ndjson') .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/resolve.ts b/x-pack/test/saved_object_api_integration/common/suites/resolve.ts index ebb71a860d744..8f03686d4cabc 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/resolve.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/resolve.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; import { @@ -70,7 +70,7 @@ export const TEST_CASES = Object.freeze({ HIDDEN: CASES.HIDDEN, }); -export function resolveTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function resolveTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('bulk_get'); const expectResponseBody = (testCase: ResolveTestCase): ExpectResponseBody => @@ -135,7 +135,7 @@ export function resolveTestSuiteFactory(esArchiver: any, supertest: SuperTest + supertest: SuperTestAgent ) { const expectSavedObjectForbidden = (action: string, typeOrTypes: string | string[]) => expectResponses.forbiddenTypes(action)(typeOrTypes); @@ -373,7 +373,7 @@ export function resolveImportErrorsTestSuiteFactory( const query = test.createNewCopies ? '?createNewCopies=true' : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/_resolve_import_errors${query}`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .field('retries', JSON.stringify(test.request.retries)) .attach('file', Buffer.from(requestBody, 'utf8'), 'export.ndjson') .expect(test.responseStatusCode) diff --git a/x-pack/test/saved_object_api_integration/common/suites/update.ts b/x-pack/test/saved_object_api_integration/common/suites/update.ts index 1fc2cef6e051a..ee37e0feccfe2 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/update.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/update.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; import { expectResponses, getUrlPrefix, getTestTitle } from '../lib/saved_object_test_utils'; @@ -34,7 +34,7 @@ export const TEST_CASES: Record = Object.freeze({ const createRequest = ({ type, id, upsert }: UpdateTestCase) => ({ type, id, upsert }); -export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectSavedObjectForbidden = expectResponses.forbiddenTypes('update'); const expectResponseBody = (testCase: UpdateTestCase): ExpectResponseBody => @@ -94,7 +94,7 @@ export function updateTestSuiteFactory(esArchiver: any, supertest: SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, id: string ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/utils.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/utils.ts index ec6fac6cf2bcd..31278019dad3e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/utils.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/utils.ts @@ -10,10 +10,7 @@ import supertest from 'supertest'; import { NodeMetrics } from '@kbn/task-manager-plugin/server/routes/metrics'; import { RetryService } from '@kbn/ftr-common-functional-services'; -export const getMetricsRequest = ( - request: supertest.SuperTest, - reset: boolean = false -) => { +export const getMetricsRequest = (request: supertest.Agent, reset: boolean = false) => { return request .get(`/api/task_manager/metrics${reset ? '' : '?reset=false'}`) .set('kbn-xsrf', 'foo') @@ -22,7 +19,7 @@ export const getMetricsRequest = ( }; export const getMetricsWithRetry = ( - request: supertest.SuperTest, + request: supertest.Agent, retry: RetryService, reset: boolean = false, callback?: (metrics: NodeMetrics) => boolean diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_webhook_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_webhook_action.ts index 79ea9738372f0..79a5b715f9fe1 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_webhook_action.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_webhook_action.ts @@ -17,9 +17,7 @@ import { getWebHookAction } from './get_web_hook_action'; * * @param supertest The supertest deps */ -export const createWebHookRuleAction = async ( - supertest: SuperTest.SuperTest -): Promise => { +export const createWebHookRuleAction = async (supertest: SuperTest.Agent): Promise => { return ( await supertest .post('/api/actions/action') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alerts.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alerts.ts index 21d8233c8496b..70caaa4edfd2c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alerts.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/get_alerts.ts @@ -18,7 +18,7 @@ import { refreshIndex } from '..'; import { getAlertsByIds, waitForRuleStatus } from '../../../../../common/utils/security_solution'; export type GetAlerts = ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, es: Client, rule: RuleResponse, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/finalize_alerts_migration.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/finalize_alerts_migration.ts index 02a7475f54aac..1f7d03cab9c5e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/finalize_alerts_migration.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/finalize_alerts_migration.ts @@ -21,7 +21,7 @@ export const finalizeAlertsMigration = async ({ supertest, log, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; migrationIds: string[]; }): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/start_alerts_migration.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/start_alerts_migration.ts index 5d472221154c1..304cf11d79dce 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/start_alerts_migration.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/migrations/start_alerts_migration.ts @@ -21,7 +21,7 @@ export const startAlertsMigration = async ({ supertest, log, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; indices: string[]; }): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/wait_for_alert_to_complete.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/wait_for_alert_to_complete.ts index 7d942198bc0d5..d6799e6be611c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/wait_for_alert_to_complete.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/wait_for_alert_to_complete.ts @@ -11,7 +11,7 @@ import type SuperTest from 'supertest'; import { waitFor } from '../../../../../common/utils/security_solution'; export const waitForAlertToComplete = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, id: string ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/create_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/create_connector.ts index 0ca8f88166c89..c6c67e37ebf24 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/create_connector.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/create_connector.ts @@ -15,7 +15,7 @@ export interface CreateConnectorBody { } export async function createConnector( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, connector: CreateConnectorBody, id = '' ): Promise { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/delete_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/delete_connector.ts index 683f845fd8bf8..1003c7cd8880b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/delete_connector.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/delete_connector.ts @@ -7,9 +7,6 @@ import type SuperTest from 'supertest'; -export function deleteConnector( - supertest: SuperTest.SuperTest, - connectorId: string -): SuperTest.Test { +export function deleteConnector(supertest: SuperTest.Agent, connectorId: string): SuperTest.Test { return supertest.delete(`/api/actions/connector/${connectorId}`).set('kbn-xsrf', 'foo'); } diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/get_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/get_connector.ts index 8f7e4830372f9..9132b188ecd33 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/get_connector.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/connectors/get_connector.ts @@ -9,7 +9,7 @@ import { Connector } from '@kbn/actions-plugin/server/application/connector/type import type SuperTest from 'supertest'; export async function getConnector( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, connectorId: string ): Promise { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/item/create_exception_list_item.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/item/create_exception_list_item.ts index fccbd3e243b17..39f202c13bce6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/item/create_exception_list_item.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/item/create_exception_list_item.ts @@ -22,7 +22,7 @@ import { EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; * @param log The tooling logger */ export const createExceptionListItem = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, exceptionListItem: CreateExceptionListItemSchema ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_endpoint_entries.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_endpoint_entries.ts index 7541514448b5c..442d341815c2f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_endpoint_entries.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_endpoint_entries.ts @@ -27,7 +27,7 @@ import { createExceptionList } from './create_exception_list'; * @param osTypes The os types to optionally add or not to add to the container */ export const createContainerWithEndpointEntries = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, endpointEntries: Array<{ entries: NonEmptyEntriesArray; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_entries.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_entries.ts index 973e0d1962a75..36aa1fb0e0652 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_entries.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_container_with_entries.ts @@ -23,7 +23,7 @@ import { waitFor } from '../../../../../../common/utils/security_solution'; * @param osTypes The os types to optionally add or not to add to the container */ export const createContainerWithEntries = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, entries: NonEmptyEntriesArray[] ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_exception_list.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_exception_list.ts index 24ebabb5243b2..cae20d074a1c3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_exception_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/create_exception_list.ts @@ -23,7 +23,7 @@ import { deleteExceptionList } from './delete_exception_list'; * @param log The tooling logger */ export const createExceptionList = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, exceptionList: CreateExceptionListSchema ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/delete_exception_list.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/delete_exception_list.ts index 6c5558a005b97..17cc52ac8c4b6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/delete_exception_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/exception_list_and_item/list/delete_exception_list.ts @@ -18,7 +18,7 @@ import type { RuleResponse } from '@kbn/security-solution-plugin/common/api/dete * @param log The tooling logger */ export const deleteExceptionList = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, listId: string ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/get_stats.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/get_stats.ts index 9415f6900a54f..bd8b3d3ea175c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/get_stats.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/get_stats.ts @@ -22,7 +22,7 @@ import { getDetectionMetricsFromBody } from './get_detection_metrics_from_body'; * @returns The detection metrics */ export const getStats = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/machine_learning/machine_learning_setup.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/machine_learning/machine_learning_setup.ts index d7c7e6387c739..a9b9bf1c8ce5b 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/machine_learning/machine_learning_setup.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/machine_learning/machine_learning_setup.ts @@ -15,7 +15,7 @@ export const executeSetupModuleRequest = async ({ }: { module: string; rspCode: number; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; }) => { const { body } = await supertest .post(`/internal/ml/modules/setup/${module}`) @@ -40,7 +40,7 @@ export const forceStartDatafeeds = async ({ }: { jobId: string; rspCode: number; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; }) => { const { body } = await supertest .post(`/internal/ml/jobs/force_start_datafeeds`) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_legacy_rule_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_legacy_rule_action.ts index 8e0cb59d5ee90..439dd6a44f4d8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_legacy_rule_action.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_legacy_rule_action.ts @@ -10,7 +10,7 @@ import type SuperTest from 'supertest'; import { UPDATE_OR_CREATE_LEGACY_ACTIONS } from '@kbn/security-solution-plugin/common/constants'; export const createLegacyRuleAction = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, alertId: string, connectorId: string ): Promise => diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_non_security_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_non_security_rule.ts index 09bc0f9b81a6d..c6fd0f8d1bc78 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_non_security_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_non_security_rule.ts @@ -30,9 +30,7 @@ const SIMPLE_APM_RULE_DATA = { * Created a non security rule. Helpful in tests to verify functionality works with presence of non security rules. * @param supertest The supertest deps */ -export async function createNonSecurityRule( - supertest: SuperTest.SuperTest -): Promise { +export async function createNonSecurityRule(supertest: SuperTest.Agent): Promise { await supertest .post('/api/alerting/rule') .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_saved_object.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_saved_object.ts index 93a6322011623..f4e3f22a0c9a6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_saved_object.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_saved_object.ts @@ -21,7 +21,7 @@ import { * @param supertest */ export const createRuleThroughAlertingEndpoint = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, rule: InternalRuleCreate ): Promise> => { const { body } = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts index 7b5e4435a3dbb..cde330f8c76a1 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_auth.ts @@ -19,7 +19,7 @@ import type { * @param rule The rule to create */ export const createRuleWithAuth = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, rule: RuleCreateProps, auth: { user: string; pass: string } ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_exception_entries.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_exception_entries.ts index ca2a8129b7713..a04ac5950596e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_exception_entries.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/create_rule_with_exception_entries.ts @@ -31,7 +31,7 @@ import { createRule } from '../../../../../common/utils/security_solution'; * @param osTypes The os types to optionally add or not to add to the container */ export const createRuleWithExceptionEntries = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, rule: RuleCreateProps, entries: NonEmptyEntriesArray[], diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/fetch_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/fetch_rule.ts index 8e9e1008ff404..651bb2a595f8f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/fetch_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/fetch_rule.ts @@ -18,7 +18,7 @@ import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common * @param rule The rule to create */ export const fetchRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, idOrRuleId: { id: string; ruleId?: never } | { id?: never; ruleId: string } ): Promise => ( diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/find_immutable_rule_by_id.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/find_immutable_rule_by_id.ts index 55e7375c48986..f4b026748e986 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/find_immutable_rule_by_id.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/find_immutable_rule_by_id.ts @@ -17,7 +17,7 @@ import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common * @param supertest The supertest deps */ export const findImmutableRuleById = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, ruleId: string ): Promise<{ diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_coverage_overview.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_coverage_overview.ts index f93a29b0ec149..b512494f245e2 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_coverage_overview.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_coverage_overview.ts @@ -13,7 +13,7 @@ import { } from '@kbn/security-solution-plugin/common/api/detection_engine'; export const getCoverageOverview = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, filter?: CoverageOverviewFilter ): Promise => { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_actions.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_actions.ts index 3251d2b0a6b56..36784cc47aa1a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_actions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_actions.ts @@ -11,19 +11,16 @@ import { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; import { getSlackAction } from '..'; import { getWebHookAction } from '..'; -const createConnector = async ( - supertest: SuperTest.SuperTest, - payload: Record -) => +const createConnector = async (supertest: SuperTest.Agent, payload: Record) => (await supertest.post('/api/actions/action').set('kbn-xsrf', 'true').send(payload).expect(200)) .body; -const createWebHookConnector = (supertest: SuperTest.SuperTest) => +const createWebHookConnector = (supertest: SuperTest.Agent) => createConnector(supertest, getWebHookAction()); -const createSlackConnector = (supertest: SuperTest.SuperTest) => +const createSlackConnector = (supertest: SuperTest.Agent) => createConnector(supertest, getSlackAction()); export const getActionsWithoutFrequencies = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const webHookAction = await createWebHookConnector(supertest); const slackConnector = await createSlackConnector(supertest); @@ -44,7 +41,7 @@ export const getActionsWithoutFrequencies = async ( }; export const getActionsWithFrequencies = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const webHookAction = await createWebHookConnector(supertest); const slackConnector = await createSlackConnector(supertest); @@ -67,7 +64,7 @@ export const getActionsWithFrequencies = async ( }; export const getSomeActionsWithFrequencies = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const webHookAction = await createWebHookConnector(supertest); const slackConnector = await createSlackConnector(supertest); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/patch_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/patch_rule.ts index f62f49b20d622..44456c93a16cd 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/patch_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/patch_rule.ts @@ -21,7 +21,7 @@ import { * @param rule The rule to create */ export const patchRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, patchedRule: RulePatchProps ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts index 1647ff301a324..a3468ccb32b5a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/delete_prebuilt_rules_fleet_package.ts @@ -13,9 +13,7 @@ import type SuperTest from 'supertest'; * * @param supertest Supertest instance */ -export async function deletePrebuiltRulesFleetPackage( - supertest: SuperTest.SuperTest -) { +export async function deletePrebuiltRulesFleetPackage(supertest: SuperTest.Agent) { const resp = await supertest .get(epmRouteService.getInfoPath('security_detection_engine')) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_installed_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_installed_rules.ts index f796dd06e777b..04a9c52565bf1 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_installed_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_installed_rules.ts @@ -17,9 +17,7 @@ import { FindRulesResponse } from '@kbn/security-solution-plugin/common/api/dete * @returns Fleet install package response */ -export const getInstalledRules = async ( - supertest: SuperTest.SuperTest -): Promise => { +export const getInstalledRules = async (supertest: SuperTest.Agent): Promise => { const { body: rulesResponse } = await supertest .get(`${DETECTION_ENGINE_RULES_URL_FIND}?per_page=10000`) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_and_timelines_status.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_and_timelines_status.ts index 7f683ca9994be..420f8a7aca1ce 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_and_timelines_status.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_and_timelines_status.ts @@ -21,7 +21,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const getPrebuiltRulesAndTimelinesStatus = async ( es: Client, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { await refreshSavedObjectIndices(es); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_fleet_package.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_fleet_package.ts index ec69b6cfb14c2..2441e6f4dbc88 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_fleet_package.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_fleet_package.ts @@ -14,7 +14,7 @@ import type SuperTest from 'supertest'; * @param supertest Supertest instance * @returns The API endpoint response. Will have status 200 if package installed or 404 if not */ -export async function getPrebuiltRulesFleetPackage(supertest: SuperTest.SuperTest) { +export async function getPrebuiltRulesFleetPackage(supertest: SuperTest.Agent) { return await supertest .get(epmRouteService.getInfoPath('security_detection_engine')) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_status.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_status.ts index da044637fc77b..10ca202c66f46 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_status.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/get_prebuilt_rules_status.ts @@ -20,7 +20,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const getPrebuiltRulesStatus = async ( es: Client, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { await refreshSavedObjectIndices(es); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_fleet_package_by_url.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_fleet_package_by_url.ts index 863e4d79fb006..b88a848758a8f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_fleet_package_by_url.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_fleet_package_by_url.ts @@ -26,7 +26,7 @@ const ATTEMPT_TIMEOUT = 120000; export const installPrebuiltRulesPackageViaFleetAPI = async ( es: Client, - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, retryService: RetryService ): Promise => { const fleetResponse = await retryService.tryWithRetries( @@ -66,7 +66,7 @@ export const installPrebuiltRulesPackageViaFleetAPI = async ( export const installPrebuiltRulesPackageByVersion = async ( es: Client, - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, version: string, retryService: RetryService ): Promise => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_mock_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_mock_prebuilt_rules.ts index 0e15f416e1238..843d0531e53ba 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_mock_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_mock_prebuilt_rules.ts @@ -19,7 +19,7 @@ import { installPrebuiltRulesAndTimelines } from './install_prebuilt_rules_and_t * @returns Install prebuilt rules response */ export const installMockPrebuiltRules = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, es: Client ): Promise => { // Ensure there are prebuilt rule saved objects before installing rules diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules.ts index 499f97877bf16..eec88072e7d1e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules.ts @@ -30,7 +30,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const installPrebuiltRules = async ( es: Client, - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, rules?: RuleVersionSpecifier[] ): Promise => { let payload = {}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_and_timelines.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_and_timelines.ts index c83e8693f2390..a52a44a90bfe7 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_and_timelines.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_and_timelines.ts @@ -31,7 +31,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const installPrebuiltRulesAndTimelines = async ( es: Client, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const response = await supertest .put(PREBUILT_RULES_URL) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts index cbe609501a5f2..f7a7337d40241 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/install_prebuilt_rules_fleet_package.ts @@ -36,7 +36,7 @@ export const installPrebuiltRulesFleetPackage = async ({ retryService, }: { es: Client; - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; version?: string; overrideExistingPackage: boolean; retryService: RetryService; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_install_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_install_prebuilt_rules.ts index 573b481a3b30f..487d2dbe53044 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_install_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_install_prebuilt_rules.ts @@ -16,7 +16,7 @@ import type SuperTest from 'supertest'; * @returns Review Install prebuilt rules response */ export const reviewPrebuiltRulesToInstall = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const response = await supertest .post(REVIEW_RULE_INSTALLATION_URL) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_upgrade_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_upgrade_prebuilt_rules.ts index 9bbf980dcccca..17347ffcdd1e3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_upgrade_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/review_upgrade_prebuilt_rules.ts @@ -16,7 +16,7 @@ import type SuperTest from 'supertest'; * @returns Review Upgrade prebuilt rules response */ export const reviewPrebuiltRulesToUpgrade = async ( - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const response = await supertest .post(REVIEW_RULE_UPGRADE_URL) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/upgrade_prebuilt_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/upgrade_prebuilt_rules.ts index c22aa9106a272..f12d0adbc65f3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/upgrade_prebuilt_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/prebuilt_rules/upgrade_prebuilt_rules.ts @@ -26,7 +26,7 @@ import { refreshSavedObjectIndices } from '../../refresh_index'; */ export const upgradePrebuiltRules = async ( es: Client, - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, rules?: RuleVersionSpecifier[] ): Promise => { let payload = {}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule.ts index ec060c4076404..a939454e72c9f 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule.ts @@ -27,7 +27,7 @@ export const previewRule = async ({ invocationCount = 1, timeframeEnd = new Date(), }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; rule: RuleCreateProps; invocationCount?: number; timeframeEnd?: Date; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule_with_exception_entries.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule_with_exception_entries.ts index fb5d480bbbffc..f02a5d370daaa 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule_with_exception_entries.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/preview_rule_with_exception_entries.ts @@ -35,7 +35,7 @@ export const previewRuleWithExceptionEntries = async ({ invocationCount, timeframeEnd, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; rule: RuleCreateProps; entries: NonEmptyEntriesArray[]; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts index 5c6c69a230465..cee439311d2a1 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts @@ -21,7 +21,7 @@ import { * @param rule The rule to create */ export const updateRule = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, updatedRule: RuleUpdateProps ): Promise => ( diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/telemetry/get_security_telemetry_stats.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/telemetry/get_security_telemetry_stats.ts index 462cdecbb498d..d1345597adc95 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/telemetry/get_security_telemetry_stats.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/telemetry/get_security_telemetry_stats.ts @@ -21,7 +21,7 @@ import { * @returns The detection metrics */ export const getSecurityTelemetryStats = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts index 6f13a84504e1d..3a46aa56ef614 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/asset_criticality.ts @@ -104,7 +104,7 @@ export const getAssetCriticalityDoc = async (opts: { }; export const assetCriticalityRouteHelpersFactory = ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, namespace?: string ) => ({ status: async () => @@ -170,7 +170,7 @@ export const assetCriticalityRouteHelpersFactory = ( }); export const assetCriticalityRouteHelpersFactoryNoAuth = ( - supertestWithoutAuth: SuperTest.SuperTest, + supertestWithoutAuth: SuperTest.Agent, namespace?: string ) => ({ privilegesForUser: async ({ username, password }: { username: string; password: string }) => diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/get_risk_engine_stats.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/get_risk_engine_stats.ts index 5629e0da9a89d..fd9ff0eb88177 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/get_risk_engine_stats.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/get_risk_engine_stats.ts @@ -22,7 +22,7 @@ import { getRiskEngineMetricsFromBody } from './get_risk_engine_metrics_from_bod * @returns The detection metrics */ export const getRiskEngineStats = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { const response = await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts index be7010ab1fc7b..dbd4ed78c1896 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts @@ -76,7 +76,7 @@ export const createAndSyncRuleAndAlertsFactory = log, namespace, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; namespace?: string; }) => @@ -412,7 +412,7 @@ export const clearLegacyDashboards = async ({ supertest, log, }: { - supertest: SuperTest.SuperTest; + supertest: SuperTest.Agent; log: ToolingLog; }): Promise => { try { @@ -481,10 +481,7 @@ export const getLegacyRiskScoreDashboards = async ({ return savedObejectLens?.saved_objects.filter((s) => s?.attributes?.title?.includes('Risk')); }; -export const riskEngineRouteHelpersFactory = ( - supertest: SuperTest.SuperTest, - namespace?: string -) => ({ +export const riskEngineRouteHelpersFactory = (supertest: SuperTest.Agent, namespace?: string) => ({ init: async (expectStatusCode: number = 200) => await supertest .post(routeWithNamespace(RISK_ENGINE_INIT_URL, namespace)) @@ -536,7 +533,7 @@ interface Credentials { } export const riskEngineRouteHelpersFactoryNoAuth = ( - supertestWithoutAuth: SuperTest.SuperTest, + supertestWithoutAuth: SuperTest.Agent, namespace?: string ) => ({ privilegesForUser: async ({ username, password }: Credentials) => @@ -576,11 +573,7 @@ export const riskEngineRouteHelpersFactoryNoAuth = ( .expect(expectStatusCode), }); -export const installLegacyRiskScore = async ({ - supertest, -}: { - supertest: SuperTest.SuperTest; -}) => { +export const installLegacyRiskScore = async ({ supertest }: { supertest: SuperTest.Agent }) => { await supertest .post('/internal/risk_score') .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts index ac2d60ad631e2..0c7823ef07885 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/create_connector.ts @@ -45,7 +45,7 @@ const connectorSetup = { * @param spaceId The space id */ export const createConnector = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, objectRemover: ObjectRemover, apiUrl: string, connectorType: 'bedrock' | 'openai', diff --git a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts index 0477401a26533..b5ed8c15b3fb2 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/genai/invoke_ai/utils/post_actions_client_execute.ts @@ -21,7 +21,7 @@ import { Response } from 'superagent'; export const postActionsClientExecute = async ( connectorId: string, args: any, - supertest: SuperTest.SuperTest + supertest: SuperTest.Agent ): Promise => { const response = await supertest .post(`/internal/elastic_assistant/actions/connector/${connectorId}/_execute`) diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/helpers.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/helpers.ts index 38212020ba18a..1f1a4bb821082 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/helpers.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/helpers.ts @@ -9,10 +9,7 @@ import type SuperTest from 'supertest'; import { v4 as uuidv4 } from 'uuid'; import { TimelineType } from '@kbn/security-solution-plugin/common/api/timeline'; -export const createBasicTimeline = async ( - supertest: SuperTest.SuperTest, - titleToSaved: string -) => +export const createBasicTimeline = async (supertest: SuperTest.Agent, titleToSaved: string) => await supertest .post('/api/timeline') .set('kbn-xsrf', 'true') @@ -25,7 +22,7 @@ export const createBasicTimeline = async ( }); export const createBasicTimelineTemplate = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, titleToSaved: string ) => await supertest diff --git a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts index d45a77be0840b..c4bde53fcab7d 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/utils.ts @@ -41,7 +41,7 @@ import { countDownTest } from '../../../common/utils/security_solution'; * @param supertest The supertest client library */ export const createListsIndex = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { return countDownTest( @@ -61,7 +61,7 @@ export const createListsIndex = async ( * @param supertest The supertest client library */ export const deleteListsIndex = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { return countDownTest( @@ -82,7 +82,7 @@ export const deleteListsIndex = async ( * @param supertest The supertest client library */ export const createExceptionListsIndex = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { return countDownTest( @@ -205,7 +205,7 @@ export const binaryToString = (res: any, callback: any): void => { * @param supertest The supertest handle */ export const deleteAllExceptions = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog ): Promise => { await deleteAllExceptionsByType(supertest, log, 'single'); @@ -218,7 +218,7 @@ export const deleteAllExceptions = async ( * @param supertest The supertest handle */ export const deleteAllExceptionsByType = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, type: NamespaceType ): Promise => { @@ -260,7 +260,7 @@ export const deleteAllExceptionsByType = async ( * @param testValues Optional test values in case you're using CIDR or range based lists */ export const importFile = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, type: Type, contents: string[], @@ -297,7 +297,7 @@ export const importFile = async ( * @param fileName filename to import as */ export const importTextFile = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, type: Type, contents: string[], @@ -330,7 +330,7 @@ export const importTextFile = async ( * @param itemValue The item value to wait for */ export const waitForListItem = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, itemValue: string, fileName: string @@ -362,7 +362,7 @@ export const waitForListItem = async ( * @param itemValue The item value to wait for */ export const waitForListItems = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, itemValues: string[], fileName: string @@ -378,7 +378,7 @@ export const waitForListItems = async ( * @param itemValue The item value to wait for */ export const waitForTextListItem = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, itemValue: string, fileName: string @@ -417,7 +417,7 @@ export const waitForTextListItem = async ( * @param itemValue The item value to wait for */ export const waitForTextListItems = async ( - supertest: SuperTest.SuperTest, + supertest: SuperTest.Agent, log: ToolingLog, itemValues: string[], fileName: string diff --git a/x-pack/test/spaces_api_integration/common/lib/create_users_and_roles.ts b/x-pack/test/spaces_api_integration/common/lib/create_users_and_roles.ts index 15ee9785aa690..58ef5ba9f9481 100644 --- a/x-pack/test/spaces_api_integration/common/lib/create_users_and_roles.ts +++ b/x-pack/test/spaces_api_integration/common/lib/create_users_and_roles.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import { AUTHENTICATION } from './authentication'; -export const createUsersAndRoles = async (es: Client, supertest: SuperTest) => { +export const createUsersAndRoles = async (es: Client, supertest: SuperTestAgent) => { await supertest .put('/api/security/role/kibana_legacy_user') .send({ diff --git a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts index 002ed6c3e6515..5889a10479f31 100644 --- a/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts +++ b/x-pack/test/spaces_api_integration/common/suites/disable_legacy_url_aliases.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import type { SuperTest } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { LegacyUrlAlias } from '@kbn/core-saved-objects-base-server-internal'; import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; @@ -48,7 +48,7 @@ const getTestTitle = ({ targetSpace, targetType, sourceId }: DisableLegacyUrlAli export function disableLegacyUrlAliasesTestSuiteFactory( es: Client, esArchiver: any, - supertest: SuperTest + supertest: SuperTestAgent ) { const expectResponseBody = (testCase: DisableLegacyUrlAliasesTestCase, statusCode: 204 | 403): ExpectResponseBody => @@ -117,7 +117,7 @@ export function disableLegacyUrlAliasesTestSuiteFactory( const requestBody = test.request; await supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_disable_legacy_url_aliases`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts b/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts index bedc8a52409b6..c1630bc288169 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_shareable_references.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { deepFreeze } from '@kbn/std'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SavedObjectsCollectMultiNamespaceReferencesResponse, SavedObjectReferenceWithContext, @@ -192,7 +192,7 @@ const getRedactedSpaces = (authorizedSpace: string | undefined, spaces: string[] return redactedSpaces.sort((a, b) => (a === '?' ? 1 : b === '?' ? -1 : 0)); // unknown spaces are always at the end of the array }; -export function getShareableReferencesTestSuiteFactory(esArchiver: any, supertest: SuperTest) { +export function getShareableReferencesTestSuiteFactory(esArchiver: any, supertest: SuperTestAgent) { const expectForbidden = expectResponses.forbiddenTypes('share_to_space'); const expectResponseBody = ( @@ -274,7 +274,7 @@ export function getShareableReferencesTestSuiteFactory(esArchiver: any, supertes const requestBody = test.request; await supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_get_shareable_references`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts b/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts index 14eb97c38c6ee..777581d9aa5a0 100644 --- a/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts +++ b/x-pack/test/spaces_api_integration/common/suites/update_objects_spaces.ts @@ -9,7 +9,7 @@ import expect from '@kbn/expect'; import type { Client } from '@elastic/elasticsearch'; import type { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; import { without, uniq } from 'lodash'; -import { SuperTest } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { SavedObjectsErrorHelpers, SavedObjectsUpdateObjectsSpacesResponse, @@ -61,7 +61,7 @@ const getTestTitle = ({ objects, spacesToAdd, spacesToRemove }: UpdateObjectsSpa export function updateObjectsSpacesTestSuiteFactory( es: Client, esArchiver: any, - supertest: SuperTest + supertest: SuperTestAgent ) { const expectForbidden = expectResponses.forbiddenTypes('share_to_space'); const expectResponseBody = @@ -162,7 +162,7 @@ export function updateObjectsSpacesTestSuiteFactory( const requestBody = test.request; await supertest .post(`${getUrlPrefix(spaceId)}/api/spaces/_update_objects_spaces`) - .auth(user?.username, user?.password) + .auth(user?.username!, user?.password!) .send(requestBody) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test_serverless/api_integration/services/svl_cases/api.ts b/x-pack/test_serverless/api_integration/services/svl_cases/api.ts index 8f23eba1ea981..7089655551fbe 100644 --- a/x-pack/test_serverless/api_integration/services/svl_cases/api.ts +++ b/x-pack/test_serverless/api_integration/services/svl_cases/api.ts @@ -141,7 +141,7 @@ export function SvlCasesApiServiceProvider({ getService }: FtrProviderContext) { params: CasePostRequest, expectedHttpCode: number = 200, auth: { user: User; space: string | null } | null = { user: superUser, space: null }, - headers: Record = {} + headers: Record = {} ): Promise { const apiCall = supertest.post(`${CASES_URL}`); diff --git a/x-pack/test_serverless/api_integration/services/transform/api.ts b/x-pack/test_serverless/api_integration/services/transform/api.ts index 39c2d01c6adb1..c865dcec6e8a0 100644 --- a/x-pack/test_serverless/api_integration/services/transform/api.ts +++ b/x-pack/test_serverless/api_integration/services/transform/api.ts @@ -217,7 +217,7 @@ export function TransformAPIProvider({ getService }: FtrProviderContext) { ); const { body, status } = await esSupertest .put(`/_transform/${transformId}${deferValidation ? '?defer_validation=true' : ''}`) - .set(headers) + .set(headers as Record) .send(transformConfig); this.assertResponseStatusCode(200, status, body); } else { diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts index bd8c64bed6731..88f3f1a76d3bb 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_api_helper.ts @@ -6,7 +6,7 @@ */ import moment from 'moment'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; interface CreateEsQueryRuleParams { size: number; @@ -32,7 +32,7 @@ export async function createIndexConnector({ name, indexName, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name: string; indexName: string; }) { @@ -56,7 +56,7 @@ export async function createSlackConnector({ supertest, name, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name: string; }) { const { body } = await supertest @@ -87,7 +87,7 @@ export async function createEsQueryRule({ notifyWhen, enabled = true, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleTypeId: string; name: string; params: CreateEsQueryRuleParams; @@ -134,7 +134,7 @@ export async function createAnomalyRule({ ruleTypeId = 'apm.anomaly', params, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name?: string; consumer?: string; actions?: any[]; @@ -184,7 +184,7 @@ export async function createLatencyThresholdRule({ ruleTypeId = 'apm.transaction_duration', params, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name?: string; consumer?: string; actions?: any[]; @@ -233,7 +233,7 @@ export async function createInventoryRule({ ruleTypeId = 'metrics.alert.inventory.threshold', params, }: { - supertest: SuperTest; + supertest: SuperTestAgent; name?: string; consumer?: string; actions?: any[]; @@ -287,7 +287,7 @@ export async function disableRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -303,7 +303,7 @@ export async function updateEsQueryRule({ ruleId, updates, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; updates: any; }) { @@ -341,7 +341,7 @@ export async function runRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const response = await supertest @@ -356,7 +356,7 @@ export async function muteRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -371,7 +371,7 @@ export async function enableRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -387,7 +387,7 @@ export async function muteAlert({ ruleId, alertId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; alertId: string; }) { @@ -403,7 +403,7 @@ export async function unmuteRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -418,7 +418,7 @@ export async function snoozeRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { const { body } = await supertest @@ -443,7 +443,7 @@ export async function findRule({ supertest, ruleId, }: { - supertest: SuperTest; + supertest: SuperTestAgent; ruleId: string; }) { if (!ruleId) { diff --git a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts index 00b746697cd23..55bfa422c5f4b 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/alerting/helpers/alerting_wait_for_helpers.ts @@ -6,7 +6,7 @@ */ import pRetry from 'p-retry'; -import type { SuperTest, Test } from 'supertest'; +import type { Agent as SuperTestAgent } from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { AggregationsAggregate, @@ -372,7 +372,7 @@ export async function waitForNumRuleRuns({ esClient, testStart, }: { - supertest: SuperTest; + supertest: SuperTestAgent; numOfRuns: number; ruleId: string; esClient: Client; diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/apm_api_integration/common/apm_api_supertest.ts b/x-pack/test_serverless/api_integration/test_suites/observability/apm_api_integration/common/apm_api_supertest.ts index ac324b3fda087..62db8c2eddda4 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/apm_api_integration/common/apm_api_supertest.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/apm_api_integration/common/apm_api_supertest.ts @@ -16,7 +16,7 @@ import type { APIEndpoint } from '@kbn/apm-plugin/server'; import { formatRequest } from '@kbn/server-route-repository'; import { InheritedFtrProviderContext } from '../../../../services'; -export function createApmApiClient(st: supertest.SuperTest) { +export function createApmApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts b/x-pack/test_serverless/api_integration/test_suites/observability/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts index 2cdb6ec4fd765..f1d746b49ff06 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/dataset_quality_api_integration/common/dataset_quality_api_supertest.ts @@ -13,7 +13,7 @@ import type { APIEndpoint } from '@kbn/dataset-quality-plugin/server/routes'; import { formatRequest } from '@kbn/server-route-repository'; import { InheritedFtrProviderContext } from '../../../../services'; -export function createDatasetQualityApiClient(st: supertest.SuperTest) { +export function createDatasetQualityApiClient(st: supertest.Agent) { return async ( options: { type?: 'form-data'; diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts b/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts index fae09f97cf2a2..9925c00fe5748 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/synthetics/synthetics_enablement.ts @@ -65,7 +65,7 @@ export default function ({ getService }: FtrProviderContext) { async function enablementPut(role: RoleName = 'admin', expectedStatus: number = 200) { return supertestWithoutAuth .put(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT) - .set(internalRequestHeader) + .set(internalRequestHeader as unknown as Record) .set(await svlUserManager.getApiCredentialsForRole(role)) .expect(expectedStatus); } @@ -73,7 +73,7 @@ export default function ({ getService }: FtrProviderContext) { async function enablementDelete(role: RoleName = 'admin', expectedStatus: number = 200) { return supertestWithoutAuth .delete(SYNTHETICS_API_URLS.SYNTHETICS_ENABLEMENT) - .set(internalRequestHeader) + .set(internalRequestHeader as unknown as Record) .set(await svlUserManager.getApiCredentialsForRole(role)) .expect(expectedStatus); } diff --git a/x-pack/test_serverless/shared/lib/object_remover.ts b/x-pack/test_serverless/shared/lib/object_remover.ts index ad029ca579cbd..ef43c70d0ee49 100644 --- a/x-pack/test_serverless/shared/lib/object_remover.ts +++ b/x-pack/test_serverless/shared/lib/object_remover.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { SuperTest, Test } from 'supertest'; +import { Agent as SuperTestAgent } from 'supertest'; import { getUrlPathPrefixForSpace } from './space_path_prefix'; @@ -18,10 +18,10 @@ interface ObjectToRemove { } export class ObjectRemover { - private readonly supertest: SuperTest; + private readonly supertest: SuperTestAgent; private objectsToRemove: ObjectToRemove[] = []; - constructor(supertest: SuperTest) { + constructor(supertest: SuperTestAgent) { this.supertest = supertest; } @@ -60,7 +60,7 @@ export class ObjectRemover { } interface DeleteObjectParams { - supertest: SuperTest; + supertest: SuperTestAgent; url: string; plugin: string; } diff --git a/x-pack/test_serverless/shared/services/bsearch_secure.ts b/x-pack/test_serverless/shared/services/bsearch_secure.ts index 2493c1d76e09c..14a373ab99686 100644 --- a/x-pack/test_serverless/shared/services/bsearch_secure.ts +++ b/x-pack/test_serverless/shared/services/bsearch_secure.ts @@ -25,7 +25,7 @@ const parseBfetchResponse = (resp: request.Response): Array> }; interface SendOptions { - supertestWithoutAuth: SuperTest.SuperTest; + supertestWithoutAuth: SuperTest.Agent; apiKeyHeader: { Authorization: string }; referer?: string; kibanaVersion?: string; diff --git a/yarn.lock b/yarn.lock index cf2d9e2fb8052..195b869f022c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9555,10 +9555,10 @@ dependencies: "@types/node" "*" -"@types/cookiejar@*": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8" - integrity sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog== +"@types/cookiejar@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.5.tgz#14a3e83fa641beb169a2dd8422d91c3c345a9a78" + integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q== "@types/cytoscape@^3.14.0": version "3.14.0" @@ -10163,6 +10163,11 @@ resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== +"@types/methods@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" + integrity sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ== + "@types/micromatch@^4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-4.0.2.tgz#ce29c8b166a73bf980a5727b1e4a4d099965151d" @@ -10701,20 +10706,22 @@ resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.0.tgz#199a3f473f0c3a6f6e4e1b17cdbc967f274bdc6b" integrity sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw== -"@types/superagent@*": - version "3.8.4" - resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-3.8.4.tgz#24a5973c7d1a9c024b4bbda742a79267c33fb86a" - integrity sha512-Dnh0Iw6NO55z1beXvlsvUrfk4cd9eL2nuTmUk+rAhSVCk10PGGFbqCCTwbau9D0d2W3DITiXl4z8VCqppGkMPQ== +"@types/superagent@^8.1.0": + version "8.1.7" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.7.tgz#1153819ed4db34427409a1cc58f3e2f13eeec862" + integrity sha512-NmIsd0Yj4DDhftfWvvAku482PZum4DBW7U51OvS8gvOkDDY0WT1jsVyDV3hK+vplrsYw8oDwi9QxOM7U68iwww== dependencies: - "@types/cookiejar" "*" + "@types/cookiejar" "^2.1.5" + "@types/methods" "^1.1.4" "@types/node" "*" -"@types/supertest@^2.0.12": - version "2.0.12" - resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.12.tgz#ddb4a0568597c9aadff8dbec5b2e8fddbe8692fc" - integrity sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ== +"@types/supertest@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-6.0.2.tgz#2af1c466456aaf82c7c6106c6b5cbd73a5e86588" + integrity sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg== dependencies: - "@types/superagent" "*" + "@types/methods" "^1.1.4" + "@types/superagent" "^8.1.0" "@types/tapable@^1", "@types/tapable@^1.0.5", "@types/tapable@^1.0.6": version "1.0.6" @@ -17726,15 +17733,14 @@ formdata-polyfill@^4.0.10: dependencies: fetch-blob "^3.1.2" -formidable@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" - integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== +formidable@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-3.5.1.tgz#9360a23a656f261207868b1484624c4c8d06ee1a" + integrity sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og== dependencies: dezalgo "^1.0.4" hexoid "^1.0.0" once "^1.4.0" - qs "^6.11.0" formik@^2.4.5: version "2.4.5" @@ -27655,7 +27661,7 @@ semver@7.5.4: dependencies: lru-cache "^6.0.0" -semver@7.6.0, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: +semver@7.6.0, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: version "7.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== @@ -29098,21 +29104,20 @@ stylus-lookup@^5.0.1: dependencies: commander "^10.0.1" -superagent@^8.0.5, superagent@^8.1.2: - version "8.1.2" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" - integrity sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA== +superagent@^9.0.1, superagent@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-9.0.2.tgz#a18799473fc57557289d6b63960610e358bdebc1" + integrity sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w== dependencies: component-emitter "^1.3.0" cookiejar "^2.1.4" debug "^4.3.4" fast-safe-stringify "^2.1.1" form-data "^4.0.0" - formidable "^2.1.2" + formidable "^3.5.1" methods "^1.1.2" mime "2.6.0" qs "^6.11.0" - semver "^7.3.8" supercluster@^8.0.1: version "8.0.1" @@ -29128,13 +29133,13 @@ superjson@^1.10.0: dependencies: copy-anything "^3.0.2" -supertest@^6.3.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.3.tgz#42f4da199fee656106fd422c094cf6c9578141db" - integrity sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA== +supertest@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-7.0.0.tgz#cac53b3d6872a0b317980b2b0cfa820f09cd7634" + integrity sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA== dependencies: methods "^1.1.2" - superagent "^8.0.5" + superagent "^9.0.1" supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: version "8.1.1" From 85639f66ff7ccc0b9e43a0a0d9ef05faf2713c8f Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Fri, 17 May 2024 13:29:34 +0200 Subject: [PATCH 41/71] Add inventory locator (#183418) Relates to #176667 ## Summary This PR adds an inventory locator and replaces the old redirect logic in `RedirectToInventory` ## Testing To simulate the usage of `link-to` use similar params to what alerts link is using, for example: `/app/metrics/link-to/inventory?customMetric=&metric=(type%3Acpu)&nodeType=pod×tamp=1715703274459` Compare the pages with the edge cluster to make sure that it looks the same as before --- .../common/alerting/metrics/alert_link.ts | 57 ++++++----- .../pages/link_to/redirect_to_inventory.tsx | 61 +++++------- .../observability_shared/public/index.ts | 1 + .../locators/infra/inventory_locator.ts | 94 +++++++++++++++++++ .../public/locators/infra/locators.test.ts | 65 +++++++++++++ .../observability_shared/public/plugin.ts | 6 ++ 6 files changed, 219 insertions(+), 65 deletions(-) create mode 100644 x-pack/plugins/observability_solution/observability_shared/public/locators/infra/inventory_locator.ts diff --git a/x-pack/plugins/observability_solution/infra/common/alerting/metrics/alert_link.ts b/x-pack/plugins/observability_solution/infra/common/alerting/metrics/alert_link.ts index ff6b6d2c752ce..a1f0da3531215 100644 --- a/x-pack/plugins/observability_solution/infra/common/alerting/metrics/alert_link.ts +++ b/x-pack/plugins/observability_solution/infra/common/alerting/metrics/alert_link.ts @@ -9,7 +9,7 @@ import { ALERT_RULE_PARAMETERS, TIMESTAMP } from '@kbn/rule-data-utils'; import { encode } from '@kbn/rison'; import { stringify } from 'query-string'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common/parse_technical_fields'; -import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; +import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { fifteenMinutesInMilliseconds, HOST_FIELD, @@ -59,37 +59,36 @@ export const getInventoryViewInAppUrl = ( if (nodeType) { if (hostName) { return getLinkToHostDetails({ hostName, timestamp: inventoryFields[TIMESTAMP] }); - } else { - const linkToParams = { - nodeType: inventoryFields[nodeTypeField][0], - timestamp: Date.parse(inventoryFields[TIMESTAMP]), - customMetric: '', - metric: '', - }; + } + const linkToParams = { + nodeType: inventoryFields[nodeTypeField][0], + timestamp: Date.parse(inventoryFields[TIMESTAMP]), + customMetric: '', + metric: '', + }; - // We always pick the first criteria metric for the URL - const criteriaMetric = inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.metric`][0]; - if (criteriaMetric === 'custom') { - const criteriaCustomMetricId = - inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.id`][0]; - const criteriaCustomMetricAggregation = - inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.aggregation`][0]; - const criteriaCustomMetricField = - inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.field`][0]; + // We always pick the first criteria metric for the URL + const criteriaMetric = inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.metric`][0]; + if (criteriaMetric === 'custom') { + const criteriaCustomMetricId = + inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.id`][0]; + const criteriaCustomMetricAggregation = + inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.aggregation`][0]; + const criteriaCustomMetricField = + inventoryFields[`${ALERT_RULE_PARAMETERS}.criteria.customMetric.field`][0]; - const customMetric = encode({ - id: criteriaCustomMetricId, - type: 'custom', - field: criteriaCustomMetricField, - aggregation: criteriaCustomMetricAggregation, - }); - linkToParams.customMetric = customMetric; - linkToParams.metric = customMetric; - } else { - linkToParams.metric = encode({ type: criteriaMetric }); - } - return `${LINK_TO_INVENTORY}?${stringify(linkToParams)}`; + const customMetric = encode({ + id: criteriaCustomMetricId, + type: 'custom', + field: criteriaCustomMetricField, + aggregation: criteriaCustomMetricAggregation, + }); + linkToParams.customMetric = customMetric; + linkToParams.metric = customMetric; + } else { + linkToParams.metric = encode({ type: criteriaMetric }); } + return `${LINK_TO_INVENTORY}?${stringify(linkToParams)}`; } return LINK_TO_INVENTORY; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_inventory.tsx b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_inventory.tsx index 37ddbacf72488..c6d0bed51ed9c 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_inventory.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_inventory.tsx @@ -5,43 +5,32 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { parse } from 'query-string'; -import { Redirect, RouteComponentProps } from 'react-router-dom'; - -// FIXME what would be the right way to build this query string? -const QUERY_STRING_TEMPLATE = - "?waffleFilter=(expression:'',kind:kuery)&waffleTime=(currentTime:{timestamp},isAutoReloading:!f)&waffleOptions=(accountId:'',autoBounds:!t,boundsOverride:(max:1,min:0),customMetrics:!({customMetric}),customOptions:!(),groupBy:!(),legend:(palette:cool,reverseColors:!f,steps:10),metric:{metric},nodeType:{nodeType},region:'',sort:(by:name,direction:desc),timelineOpen:!f,view:map)"; +import { RouteComponentProps } from 'react-router-dom'; +import type { SerializableRecord } from '@kbn/utility-types'; +import { INVENTORY_LOCATOR_ID } from '@kbn/observability-shared-plugin/public'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; export const RedirectToInventory: React.FC = ({ location }) => { - const parsedQueryString = parseQueryString(location.search); - - const inventoryQueryString = QUERY_STRING_TEMPLATE.replace( - /{(\w+)}/g, - (_, key) => parsedQueryString[key] || '' - ); - - return ; + const { + services: { share }, + } = useKibanaContextForPlugin(); + const baseLocator = share.url.locators.get(INVENTORY_LOCATOR_ID); + + useEffect(() => { + const parsedQueryString = parse(location.search || '', { sort: false }); + const currentTime = parseFloat((parsedQueryString.timestamp ?? '') as string); + + baseLocator?.navigate({ + ...parsedQueryString, + waffleTime: { + currentTime, + isAutoReloading: false, + }, + state: location.state as SerializableRecord, + }); + }, [baseLocator, location.search, location.state]); + + return null; }; - -function parseQueryString(search: string): Record { - if (search.length === 0) { - return {}; - } - - const obj = parse(search.substring(1)); - - // Force all values into string. If they are empty don't create the keys - for (const key in obj) { - if (Object.hasOwnProperty.call(obj, key)) { - if (!obj[key]) { - delete obj[key]; - } - if (Array.isArray(obj.key)) { - obj[key] = obj[key]![0]; - } - } - } - - return obj as Record; -} diff --git a/x-pack/plugins/observability_solution/observability_shared/public/index.ts b/x-pack/plugins/observability_solution/observability_shared/public/index.ts index 93094c79369a9..a06f086d6588d 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/index.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/index.ts @@ -103,6 +103,7 @@ export { export { BottomBarActions } from './components/bottom_bar_actions/bottom_bar_actions'; export { FieldValueSelection, FieldValueSuggestions } from './components'; export { ASSET_DETAILS_FLYOUT_LOCATOR_ID } from './locators/infra/asset_details_flyout_locator'; +export { INVENTORY_LOCATOR_ID } from './locators/infra/inventory_locator'; export { ASSET_DETAILS_LOCATOR_ID, type AssetDetailsLocatorParams, diff --git a/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/inventory_locator.ts b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/inventory_locator.ts new file mode 100644 index 0000000000000..ca6e997468b5b --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/inventory_locator.ts @@ -0,0 +1,94 @@ +/* + * 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 { SerializableRecord } from '@kbn/utility-types'; +import rison from '@kbn/rison'; +import { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/common'; +import querystring from 'querystring'; + +export type InventoryLocator = LocatorPublic; + +export interface InventoryLocatorParams extends SerializableRecord { + inventoryViewId?: string; + waffleFilter?: { + expression: string; + kind: string; + }; + waffleTime?: { + currentTime: number; + isAutoReloading: boolean; + }; + waffleOptions?: { + accountId: string; + autoBounds: boolean; + boundsOverride: { + max: number; + min: number; + }; + }; + customMetrics?: string; // encoded value + customOptions?: string; // encoded value + groupBy?: { field: string }; + legend?: { + palette: string; + reverseColors: boolean; + steps: number; + }; + metric: string; // encoded value + nodeType: string; + region?: string; + sort: { + by: string; + direction: 'desc' | 'async'; + }; + timelineOpen: boolean; + view: 'map' | 'table'; + state?: SerializableRecord; +} + +export const INVENTORY_LOCATOR_ID = 'INVENTORY_LOCATOR'; + +export class InventoryLocatorDefinition implements LocatorDefinition { + public readonly id = INVENTORY_LOCATOR_ID; + + public readonly getLocation = async (params: InventoryLocatorParams) => { + const paramsWithDefaults = { + waffleFilter: rison.encodeUnknown(params.waffleFilter ?? { kind: 'kuery', expression: '' }), + waffleTime: rison.encodeUnknown( + params.waffleTime ?? { + currentTime: new Date().getTime(), + isAutoReloading: false, + } + ), + waffleOptions: rison.encodeUnknown( + params.waffleOptions ?? { + accountId: '', + autoBounds: true, + boundsOverride: { max: 1, min: 0 }, + } + ), + customMetrics: params.customMetrics, + customOptions: params.customOptions, + groupBy: rison.encodeUnknown(params.groupBy ?? {}), + legend: rison.encodeUnknown( + params.legend ?? { palette: 'cool', reverseColors: false, steps: 10 } + ), + metric: params.metric, + nodeType: rison.encodeUnknown(params.nodeType), + region: rison.encodeUnknown(params.region ?? ''), + sort: rison.encodeUnknown(params.sort ?? { by: 'name', direction: 'desc' }), + timelineOpen: rison.encodeUnknown(params.timelineOpen ?? false), + view: rison.encodeUnknown(params.view ?? 'map'), + }; + + const queryStringParams = querystring.stringify(paramsWithDefaults); + return { + app: 'metrics', + path: `/inventory?${queryStringParams}`, + state: params.state ? params.state : {}, + }; + }; +} diff --git a/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/locators.test.ts b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/locators.test.ts index ff02eff6dc76f..c7b5e16625e03 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/locators.test.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/locators.test.ts @@ -9,6 +9,8 @@ import rison from '@kbn/rison'; import { AssetDetailsLocatorDefinition } from './asset_details_locator'; import { AssetDetailsFlyoutLocatorDefinition } from './asset_details_flyout_locator'; import { HostsLocatorDefinition } from './hosts_locator'; +import { InventoryLocatorDefinition } from './inventory_locator'; +import querystring from 'querystring'; const setupAssetDetailsLocator = async () => { const assetDetailsLocator = new AssetDetailsLocatorDefinition(); @@ -28,6 +30,14 @@ const setupHostsLocator = async () => { }; }; +const setupInventoryLocator = async () => { + const inventoryLocator = new InventoryLocatorDefinition(); + + return { + inventoryLocator, + }; +}; + describe('Infra Locators', () => { describe('Asset Details Locator', () => { const params = { @@ -162,4 +172,59 @@ describe('Infra Locators', () => { expect(Object.keys(state)).toHaveLength(0); }); }); + + describe('Inventory Locator', () => { + const params = { + waffleFilter: { kind: 'kuery', expression: '' }, + waffleTime: { + currentTime: 1715688477985, + isAutoReloading: false, + }, + waffleOptions: { + accountId: '', + autoBounds: true, + boundsOverride: { max: 1, min: 0 }, + }, + customMetrics: undefined, + customOptions: undefined, + groupBy: { field: 'cloud.provider' }, + legend: { palette: 'cool', reverseColors: false, steps: 10 }, + metric: '(type:cpu)', + nodeType: 'host', + region: '', + sort: { by: 'name', direction: 'desc' as const }, + timelineOpen: false, + view: 'map' as const, + }; + + const expected = Object.keys(params).reduce((acc: Record, key) => { + acc[key] = + key === 'metric' || key === 'customOptions' || key === 'customMetrics' + ? params[key] + : rison.encodeUnknown(params[key as keyof typeof params]); + return acc; + }, {}); + + const queryStringParams = querystring.stringify(expected); + + it('should create a link to Inventory with no state', async () => { + const { inventoryLocator } = await setupInventoryLocator(); + const { app, path, state } = await inventoryLocator.getLocation(params); + + expect(app).toBe('metrics'); + expect(path).toBe(`/inventory?${queryStringParams}`); + expect(state).toBeDefined(); + expect(Object.keys(state)).toHaveLength(0); + }); + + it('should return correct structured url', async () => { + const { inventoryLocator } = await setupInventoryLocator(); + const { app, path, state } = await inventoryLocator.getLocation(params); + + expect(app).toBe('metrics'); + expect(path).toBe(`/inventory?${queryStringParams}`); + expect(state).toBeDefined(); + expect(Object.keys(state)).toHaveLength(0); + }); + }); }); diff --git a/x-pack/plugins/observability_solution/observability_shared/public/plugin.ts b/x-pack/plugins/observability_solution/observability_shared/public/plugin.ts index 625bdbaaeeb3a..97808e516f320 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/plugin.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/plugin.ts @@ -27,6 +27,10 @@ import { AssetDetailsLocatorDefinition, } from './locators/infra/asset_details_locator'; import { type HostsLocator, HostsLocatorDefinition } from './locators/infra/hosts_locator'; +import { + type InventoryLocator, + InventoryLocatorDefinition, +} from './locators/infra/inventory_locator'; import { type FlamegraphLocator, FlamegraphLocatorDefinition, @@ -69,6 +73,7 @@ interface ObservabilitySharedLocators { assetDetailsLocator: AssetDetailsLocator; assetDetailsFlyoutLocator: AssetDetailsFlyoutLocator; hostsLocator: HostsLocator; + inventoryLocator: InventoryLocator; }; profiling: { flamegraphLocator: FlamegraphLocator; @@ -137,6 +142,7 @@ export class ObservabilitySharedPlugin implements Plugin { new AssetDetailsFlyoutLocatorDefinition() ), hostsLocator: urlService.locators.create(new HostsLocatorDefinition()), + inventoryLocator: urlService.locators.create(new InventoryLocatorDefinition()), }, profiling: { flamegraphLocator: urlService.locators.create(new FlamegraphLocatorDefinition()), From 6eeffd3cfba1dc129ad344f53c5997149870eaea Mon Sep 17 00:00:00 2001 From: Nikita Indik Date: Fri, 17 May 2024 13:37:05 +0200 Subject: [PATCH 42/71] [Security Solution] Allow users to edit required_fields field for custom rules (#180682) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Resolves: https://github.com/elastic/kibana/issues/173594** **Flaky test runner:** https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5915 ## Summary This PR adds an ability to add and edit custom rule's required fields. "Required fields" is an optional field that shows the user which Elasticsearch fields are needed for the rule to run properly. The values in "required fields" don't affect rule execution in any way. It's purely documentational, similar to "setup guide" and "investigation guide". This functionality is added to both rule creation and rule editing screens. It's available for all rule types except ML. Scherm­afbeelding 2024-05-07 om 12 28 50 ## Details The basic flow goes like this: first you specify your index patterns (or a data view), then you can set required fields for these index patterns. Once a user adds a required field and chooses its name, he can then choose its type from the dropdown. The first available type for the field name selected automatically. User can also add their own custom names and types. ### Warnings If a field that is not present in the selected index pattern, you will see a warning message. This can happen in the following cases: - You have specified an index pattern, selected a required field from this index pattern, and then removed this index pattern. - The index doesn't yet exist. For example, you have installed a prebuilt rule but the data for it hasn't been ingested yet, so there's no index yet. - The index was removed. - The mappings for the index were changed and the field is no longer present. In any of these cases, you'll see a general warning message above the form section. And then also a more specific warning message next to the field that is causing the issue. ### ESQL and ML rules Here's how available dropdown options are determined for different rule types: For all rule types except ESQL and ML, we take the index patterns specified by the user and fetch their mappings. Then we use these fields and types to populate the dropdowns. For ESQL rules we parse index patterns out of the query since there's no explicit index pattern form field. We then fetch the mappings for these index patterns and use them to populate the dropdowns. For ML rules, we don't show "required fields" at all. ML rules are a special case. 1. The concept of "required fields" is sort of handled during creation of the ML job itself, where the user specifies the fields that are required for the job to run. 2. In the ML rule creation/editing interface, we don't display the index patterns a rule operates on. So, even if we allowed specifying required fields, the user would need to check the ML job details to see the index patterns the job uses. 3. The ML job dropdown includes both existing and not-yet-created jobs. We can't get index patterns for jobs that don't exist yet, so we can't fill the dropdowns with fields and types. ## Screenshots screen1_ screen2_ --- packages/kbn-doc-links/src/get_doc_links.ts | 1 + packages/kbn-doc-links/src/types.ts | 1 + .../rule_schema/common_attributes.gen.ts | 27 + .../rule_schema/common_attributes.schema.yaml | 18 + .../model/rule_schema/rule_schemas.gen.ts | 2 + .../rule_schema/rule_schemas.schema.yaml | 10 + .../import_rules/rule_to_import.ts | 9 + .../related_integration_field.tsx | 15 +- .../related_integrations.test.tsx | 83 +- .../related_integrations_help_info.tsx | 2 +- .../components/required_fields/index.ts | 8 + .../make_validate_required_field.ts | 53 ++ .../required_fields/name_combobox.tsx | 126 +++ .../required_fields/required_fields.test.tsx | 724 ++++++++++++++++++ .../required_fields/required_fields.tsx | 213 ++++++ .../required_fields_help_info.tsx | 48 ++ .../required_fields/required_fields_row.tsx | 163 ++++ .../required_fields/translations.ts | 105 +++ .../required_fields/type_combobox.tsx | 151 ++++ .../components/required_fields/utils.test.tsx | 58 ++ .../components/required_fields/utils.ts | 28 + .../components/description_step/helpers.tsx | 11 +- .../step_define_rule/index.test.tsx | 187 ++++- .../components/step_define_rule/index.tsx | 17 +- .../components/step_define_rule/schema.tsx | 6 + .../hooks/use_esql_index.test.ts | 27 +- .../rule_creation_ui/hooks/use_esql_index.ts | 43 +- .../pages/rule_creation/helpers.test.ts | 16 + .../pages/rule_creation/helpers.ts | 15 + .../pages/rule_creation/index.tsx | 8 +- .../pages/rule_editing/index.tsx | 10 +- .../rule_details/required_field_icon.tsx | 67 ++ .../rule_details/rule_definition_section.tsx | 10 +- .../components/rules_table/__mocks__/mock.ts | 2 +- .../pages/detection_engine/rules/types.ts | 5 +- .../normalization/convert_rule_to_diffable.ts | 3 +- .../model/rule_assets/prebuilt_rule_asset.ts | 2 - .../logic/crud/update_rules.ts | 4 +- .../normalization/rule_converters.ts | 10 +- .../rule_management/utils/utils.ts | 20 + .../factories/utils/strip_non_ecs_fields.ts | 4 +- .../plugins/security_solution/tsconfig.json | 3 +- .../perform_bulk_action.ts | 4 + .../create_rules.ts | 44 +- .../create_rules_bulk.ts | 16 +- .../export_rules.ts | 15 +- .../import_rules.ts | 16 +- .../patch_rules.ts | 13 +- .../patch_rules_bulk.ts | 13 +- .../update_rules.ts | 37 +- .../update_rules_bulk.ts | 10 +- .../rule_creation/common_flows.cy.ts | 12 +- .../prebuilt_rules_preview.cy.ts | 4 +- .../cypress/screens/create_new_rule.ts | 3 + .../cypress/tasks/create_new_rule.ts | 13 + 55 files changed, 2305 insertions(+), 210 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/index.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/make_validate_required_field.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/name_combobox.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_help_info.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/translations.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/type_combobox.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/required_field_icon.tsx diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 136e5f7bc95b1..68bd793b3d5c9 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -479,6 +479,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D }, privileges: `${SECURITY_SOLUTION_DOCS}endpoint-management-req.html`, manageDetectionRules: `${SECURITY_SOLUTION_DOCS}rules-ui-management.html`, + createDetectionRules: `${SECURITY_SOLUTION_DOCS}rules-ui-create.html`, createEsqlRuleType: `${SECURITY_SOLUTION_DOCS}rules-ui-create.html#create-esql-rule`, ruleUiAdvancedParams: `${SECURITY_SOLUTION_DOCS}rules-ui-create.html#rule-ui-advanced-params`, entityAnalytics: { diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 29e09d8b25672..fb59c867cff9d 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -354,6 +354,7 @@ export interface DocLinks { }; readonly privileges: string; readonly manageDetectionRules: string; + readonly createDetectionRules: string; readonly createEsqlRuleType: string; readonly ruleUiAdvancedParams: string; readonly entityAnalytics: { diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts index 6b6bc018c8e5c..ac6eb3dd18a7e 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts @@ -303,13 +303,40 @@ export const TimestampOverride = z.string(); export type TimestampOverrideFallbackDisabled = z.infer; export const TimestampOverrideFallbackDisabled = z.boolean(); +/** + * Describes an Elasticsearch field that is needed for the rule to function + */ export type RequiredField = z.infer; export const RequiredField = z.object({ + /** + * Name of an Elasticsearch field + */ name: NonEmptyString, + /** + * Type of the Elasticsearch field + */ type: NonEmptyString, + /** + * Whether the field is an ECS field + */ ecs: z.boolean(), }); +/** + * Input parameters to create a RequiredField. Does not include the `ecs` field, because `ecs` is calculated on the backend based on the field name and type. + */ +export type RequiredFieldInput = z.infer; +export const RequiredFieldInput = z.object({ + /** + * Name of an Elasticsearch field + */ + name: NonEmptyString, + /** + * Type of an Elasticsearch field + */ + type: NonEmptyString, +}); + export type RequiredFieldArray = z.infer; export const RequiredFieldArray = z.array(RequiredField); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml index fadb38ef1ce5f..cd5e238723f6a 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml @@ -315,18 +315,36 @@ components: RequiredField: type: object + description: Describes an Elasticsearch field that is needed for the rule to function properties: name: $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + description: Name of an Elasticsearch field type: $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + description: Type of the Elasticsearch field ecs: type: boolean + description: Whether the field is an ECS field required: - name - type - ecs + RequiredFieldInput: + type: object + description: Input parameters to create a RequiredField. Does not include the `ecs` field, because `ecs` is calculated on the backend based on the field name and type. + properties: + name: + $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + description: Name of an Elasticsearch field + type: + $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + description: Type of an Elasticsearch field + required: + - name + - type + RequiredFieldArray: type: array items: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts index d05a272337534..d2523a9a5c557 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts @@ -54,6 +54,7 @@ import { ThreatArray, SetupGuide, RelatedIntegrationArray, + RequiredFieldInput, RuleObjectId, RuleSignatureId, IsRuleImmutable, @@ -137,6 +138,7 @@ export const BaseDefaultableFields = z.object({ threat: ThreatArray.optional(), setup: SetupGuide.optional(), related_integrations: RelatedIntegrationArray.optional(), + required_fields: z.array(RequiredFieldInput).optional(), }); export type BaseCreateProps = z.infer; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml index f8998624f99b1..ae1a5657d2ab4 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml @@ -117,12 +117,15 @@ components: author: $ref: './common_attributes.schema.yaml#/components/schemas/RuleAuthorArray' + # False positive examples false_positives: $ref: './common_attributes.schema.yaml#/components/schemas/RuleFalsePositiveArray' + # Reference URLs references: $ref: './common_attributes.schema.yaml#/components/schemas/RuleReferenceArray' + # Max alerts per run max_signals: $ref: './common_attributes.schema.yaml#/components/schemas/MaxSignals' threat: @@ -130,9 +133,16 @@ components: setup: $ref: './common_attributes.schema.yaml#/components/schemas/SetupGuide' + # Related integrations related_integrations: $ref: './common_attributes.schema.yaml#/components/schemas/RelatedIntegrationArray' + # Required fields + required_fields: + type: array + items: + $ref: './common_attributes.schema.yaml#/components/schemas/RequiredFieldInput' + BaseCreateProps: x-inline: true allOf: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts index 9634d773b121d..9dc1e218f6ceb 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts @@ -9,6 +9,7 @@ import * as z from 'zod'; import { BaseCreateProps, ResponseFields, + RequiredFieldInput, RuleSignatureId, TypeSpecificCreateProps, } from '../../model/rule_schema'; @@ -29,5 +30,13 @@ export const RuleToImport = BaseCreateProps.and(TypeSpecificCreateProps).and( ResponseFields.partial().extend({ rule_id: RuleSignatureId, immutable: z.literal(false).default(false), + /* + Overriding `required_fields` from ResponseFields because + in ResponseFields `required_fields` has the output type, + but for importing rules, we need to use the input type. + Otherwise importing rules without the "ecs" property in + `required_fields` will fail. + */ + required_fields: z.array(RequiredFieldInput).optional(), }) ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integration_field.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integration_field.tsx index c24220923441b..fb6e89fb44acc 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integration_field.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integration_field.tsx @@ -24,7 +24,6 @@ import type { FieldHook } from '../../../../shared_imports'; import type { Integration, RelatedIntegration } from '../../../../../common/api/detection_engine'; import { useIntegrations } from '../../../../detections/components/rules/related_integrations/use_integrations'; import { IntegrationStatusBadge } from './integration_status_badge'; -import { DEFAULT_RELATED_INTEGRATION } from './default_related_integration'; import * as i18n from './translations'; interface RelatedIntegrationItemFormProps { @@ -95,16 +94,6 @@ export function RelatedIntegrationField({ ); const hasError = Boolean(packageErrorMessage) || Boolean(versionErrorMessage); - const isLastField = relatedIntegrations.length === 1; - const isLastEmptyField = isLastField && field.value.package === ''; - const handleRemove = useCallback(() => { - if (isLastField) { - field.setValue(DEFAULT_RELATED_INTEGRATION); - return; - } - - onRemove(); - }, [onRemove, field, isLastField]); return ( ({ docLinks: { links: { securitySolution: { - ruleUiAdvancedParams: 'http://link-to-docs', + createDetectionRules: 'http://link-to-docs', }, }, }, @@ -669,82 +669,6 @@ describe('RelatedIntegrations form part', () => { }); }); }); - - describe('sticky last form row', () => { - it('does not remove the last item', async () => { - render(, { wrapper: createReactQueryWrapper() }); - - await addRelatedIntegrationRow(); - await removeLastRelatedIntegrationRow(); - - expect(screen.getAllByTestId(RELATED_INTEGRATION_ROW)).toHaveLength(1); - }); - - it('disables remove button after clicking remove button on the last item', async () => { - render(, { wrapper: createReactQueryWrapper() }); - - await addRelatedIntegrationRow(); - await removeLastRelatedIntegrationRow(); - - expect(screen.getByTestId(REMOVE_INTEGRATION_ROW_BUTTON_TEST_ID)).toBeDisabled(); - }); - - it('clears selected integration when clicking remove the last form row button', async () => { - render(, { wrapper: createReactQueryWrapper() }); - - await addRelatedIntegrationRow(); - await selectFirstEuiComboBoxOption({ - comboBoxToggleButton: getLastByTestId(COMBO_BOX_TOGGLE_BUTTON_TEST_ID), - }); - await removeLastRelatedIntegrationRow(); - - expect(screen.queryByTestId(COMBO_BOX_SELECTION_TEST_ID)).not.toBeInTheDocument(); - }); - - it('submits an empty integration after clicking remove the last form row button', async () => { - const handleSubmit = jest.fn(); - - render(, { wrapper: createReactQueryWrapper() }); - - await addRelatedIntegrationRow(); - await selectFirstEuiComboBoxOption({ - comboBoxToggleButton: getLastByTestId(COMBO_BOX_TOGGLE_BUTTON_TEST_ID), - }); - await removeLastRelatedIntegrationRow(); - await submitForm(); - await waitFor(() => { - expect(handleSubmit).toHaveBeenCalled(); - }); - - expect(handleSubmit).toHaveBeenCalledWith({ - data: [{ package: '', version: '' }], - isValid: true, - }); - }); - - it('submits an empty integration after previously saved integrations were removed', async () => { - const initialRelatedIntegrations: RelatedIntegration[] = [ - { package: 'package-a', version: '^1.2.3' }, - ]; - const handleSubmit = jest.fn(); - - render(, { - wrapper: createReactQueryWrapper(), - }); - - await waitForIntegrationsToBeLoaded(); - await removeLastRelatedIntegrationRow(); - await submitForm(); - await waitFor(() => { - expect(handleSubmit).toHaveBeenCalled(); - }); - - expect(handleSubmit).toHaveBeenCalledWith({ - data: [{ package: '', version: '' }], - isValid: true, - }); - }); - }); }); }); @@ -778,11 +702,6 @@ function TestForm({ initialState, onSubmit }: TestFormProps): JSX.Element { ); } -function getLastByTestId(testId: string): HTMLElement { - // getAllByTestId throws an error when there are no `testId` elements found - return screen.getAllByTestId(testId).at(-1)!; -} - function waitForIntegrationsToBeLoaded(): Promise { return waitForElementToBeRemoved(screen.queryAllByRole('progressbar')); } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations_help_info.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations_help_info.tsx index b694d17a80435..08c4a8e22edfd 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations_help_info.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/related_integrations/related_integrations_help_info.tsx @@ -40,7 +40,7 @@ export function RelatedIntegrationsHelpInfo(): JSX.Element { defaultMessage="Choose the {integrationsDocLink} this rule depends on, and correct if necessary each integration’s version constraint in {semverLink} format. Only tilde, caret, and plain versions are supported, such as ~1.2.3, ^1.2.3, or 1.2.3." values={{ integrationsDocLink: ( - + > + ): ReturnType> | undefined { + const [{ value, path, form }] = args; + + const formData = form.getFormData(); + const parentFieldData: RequiredFieldInput[] = formData[parentFieldPath]; + + const isFieldNameUsedMoreThanOnce = + parentFieldData.filter((field) => field.name === value.name).length > 1; + + if (isFieldNameUsedMoreThanOnce) { + return { + code: 'ERR_FIELD_FORMAT', + path: `${path}.name`, + message: i18n.FIELD_NAME_USED_MORE_THAN_ONCE(value.name), + }; + } + + /* Allow empty rows. They are going to be removed before submission. */ + if (value.name.trim().length === 0 && value.type.trim().length === 0) { + return; + } + + if (value.name.trim().length === 0) { + return { + code: 'ERR_FIELD_MISSING', + path: `${path}.name`, + message: i18n.FIELD_NAME_REQUIRED, + }; + } + + if (value.type.trim().length === 0) { + return { + code: 'ERR_FIELD_MISSING', + path: `${path}.type`, + message: i18n.FIELD_TYPE_REQUIRED, + }; + } + }; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/name_combobox.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/name_combobox.tsx new file mode 100644 index 0000000000000..848e3c9a3c558 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/name_combobox.tsx @@ -0,0 +1,126 @@ +/* + * 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, useEffect } from 'react'; +import { EuiComboBox, EuiIcon } from '@elastic/eui'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import type { FieldHook } from '../../../../shared_imports'; +import type { RequiredFieldInput } from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; +import { pickTypeForName } from './utils'; +import * as i18n from './translations'; + +interface NameComboBoxProps { + field: FieldHook; + itemId: string; + availableFieldNames: string[]; + typesByFieldName: Record; + nameWarning: string; + nameError: { message: string } | undefined; +} + +export function NameComboBox({ + field, + itemId, + availableFieldNames, + typesByFieldName, + nameWarning, + nameError, +}: NameComboBoxProps) { + const { value, setValue } = field; + + const selectableNameOptions: Array> = useMemo( + () => + /* Not adding an empty string to the list of selectable field names */ + (value.name ? [value.name] : []).concat(availableFieldNames).map((name) => ({ + label: name, + value: name, + })), + [availableFieldNames, value.name] + ); + + /* + Using a state for `selectedNameOptions` instead of using the field value directly + to fix the issue where pressing the backspace key in combobox input would clear the field value + and trigger a validation error. By using a separate state, we can clear the selected option + without clearing the field value. + */ + const [selectedNameOption, setSelectedNameOption] = useState< + EuiComboBoxOptionOption | undefined + >(selectableNameOptions.find((option) => option.label === value.name)); + + useEffect(() => { + /* Re-computing the new selected name option when the field value changes */ + setSelectedNameOption(selectableNameOptions.find((option) => option.label === value.name)); + }, [value.name, selectableNameOptions]); + + const handleNameChange = useCallback( + (selectedOptions: Array>) => { + const newlySelectedOption: EuiComboBoxOptionOption | undefined = selectedOptions[0]; + + if (!newlySelectedOption) { + /* This occurs when the user hits backspace in combobox */ + setSelectedNameOption(undefined); + return; + } + + const updatedName = newlySelectedOption?.value || ''; + + const updatedType = pickTypeForName({ + name: updatedName, + type: value.type, + typesByFieldName, + }); + + const updatedFieldValue: RequiredFieldInput = { + name: updatedName, + type: updatedType, + }; + + setValue(updatedFieldValue); + }, + [setValue, value.type, typesByFieldName] + ); + + const handleAddCustomName = useCallback( + (newName: string) => { + const updatedFieldValue: RequiredFieldInput = { + name: newName, + type: pickTypeForName({ name: newName, type: value.type, typesByFieldName }), + }; + + setValue(updatedFieldValue); + }, + [setValue, value.type, typesByFieldName] + ); + + return ( + + ) : undefined + } + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.test.tsx new file mode 100644 index 0000000000000..2812c147d9c2d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.test.tsx @@ -0,0 +1,724 @@ +/* + * 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 { I18nProvider } from '@kbn/i18n-react'; +import { screen, render, act, fireEvent, waitFor } from '@testing-library/react'; +import { Form, useForm } from '../../../../shared_imports'; + +import type { DataViewFieldBase } from '@kbn/es-query'; +import { RequiredFields } from './required_fields'; +import type { RequiredFieldInput } from '../../../../../common/api/detection_engine'; + +const ADD_REQUIRED_FIELD_BUTTON_TEST_ID = 'addRequiredFieldButton'; +const REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID = 'requiredFieldsGeneralWarning'; + +describe('RequiredFields form part', () => { + it('displays the required fields label', () => { + render(); + + expect(screen.getByText('Required fields')); + }); + + it('displays previously saved required fields', () => { + const initialState = [ + { name: 'field1', type: 'string' }, + { name: 'field2', type: 'number' }, + ]; + + render(); + + expect(screen.getByDisplayValue('field1')).toBeVisible(); + expect(screen.getByDisplayValue('string')).toBeVisible(); + + expect(screen.getByDisplayValue('field2')).toBeVisible(); + expect(screen.getByDisplayValue('number')).toBeVisible(); + }); + + it('user can add a new required field to an empty form', async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + await addRequiredFieldRow(); + await selectFirstEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + }); + + expect(screen.getByDisplayValue('field1')).toBeVisible(); + expect(screen.getByDisplayValue('string')).toBeVisible(); + }); + + it('user can add a new required field to a previously saved form', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field2', esTypes: ['keyword'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + await selectFirstEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + }); + + expect(screen.getByDisplayValue('field2')).toBeVisible(); + expect(screen.getByDisplayValue('keyword')).toBeVisible(); + }); + + it('user can select any field name that is available in index patterns', async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + createIndexPatternField({ name: 'field2', esTypes: ['keyword'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionIndex: 0, + }); + + expect(screen.getByDisplayValue('field1')).toBeVisible(); + expect(screen.getByDisplayValue('string')).toBeVisible(); + + await addRequiredFieldRow(); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionIndex: 0, + }); + + expect(screen.getByDisplayValue('field2')).toBeVisible(); + expect(screen.getByDisplayValue('keyword')).toBeVisible(); + }); + + it('user can add his own custom field name and type', async () => { + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'customField', + }); + + expect(screen.getByDisplayValue('customField')).toBeVisible(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('empty'), + optionText: 'customType', + }); + + expect(screen.getByDisplayValue('customType')).toBeVisible(); + expect(screen.queryByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + }); + + it('field type dropdown allows to choose from options if multiple types are available', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string', 'keyword'] }), + ]; + + render(); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('string'), + optionText: 'keyword', + }); + + expect(screen.getByDisplayValue('keyword')).toBeVisible(); + }); + + it('user can remove a required field', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + await act(async () => { + fireEvent.click(screen.getByTestId('removeRequiredFieldButton-field1')); + }); + + expect(screen.queryByDisplayValue('field1')).toBeNull(); + }); + + it('user can not select the same field twice', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + createIndexPatternField({ name: 'field2', esTypes: ['keyword'] }), + createIndexPatternField({ name: 'field3', esTypes: ['date'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + const emptyRowOptions = await getDropdownOptions(getSelectToggleButtonForName('empty')); + expect(emptyRowOptions).toEqual(['field2', 'field3']); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'field2', + }); + + const firstRowNameOptions = await getDropdownOptions(getSelectToggleButtonForName('field1')); + expect(firstRowNameOptions).toEqual(['field1', 'field3']); + }); + + it('adding a new required field is disabled when index patterns are loading', async () => { + render(); + + expect(screen.getByTestId(ADD_REQUIRED_FIELD_BUTTON_TEST_ID)).toBeDisabled(); + }); + + it('adding a new required field is disabled when an empty row is already displayed', async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.getByTestId(ADD_REQUIRED_FIELD_BUTTON_TEST_ID)).toBeEnabled(); + + await addRequiredFieldRow(); + + expect(screen.getByTestId(ADD_REQUIRED_FIELD_BUTTON_TEST_ID)).toBeDisabled(); + }); + + describe('warnings', () => { + it('displays a warning when a selected field name is not found within index patterns', async () => { + const initialState = [{ name: 'field-that-does-not-exist', type: 'keyword' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.getByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + + expect( + screen.getByText( + `Field "field-that-does-not-exist" is not found within the rule's specified index patterns` + ) + ).toBeVisible(); + + const nameWarningIcon = screen + .getByTestId(`requiredFieldNameSelect-field-that-does-not-exist`) + .querySelector('[data-euiicon-type="warning"]'); + + expect(nameWarningIcon).toBeVisible(); + + /* Make sure only one warning icon is displayed - the one for name */ + expect(document.querySelectorAll('[data-test-subj="warningIcon"]')).toHaveLength(1); + }); + + it('displays a warning when a selected field type is not found within index patterns', async () => { + const initialState = [{ name: 'field1', type: 'type-that-does-not-exist' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.getByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + + expect( + screen.getByText( + `Field "field1" with type "type-that-does-not-exist" is not found within the rule's specified index patterns` + ) + ).toBeVisible(); + + const typeWarningIcon = screen + .getByTestId(`requiredFieldTypeSelect-type-that-does-not-exist`) + .querySelector('[data-euiicon-type="warning"]'); + + expect(typeWarningIcon).toBeVisible(); + + /* Make sure only one warning icon is displayed - the one for type */ + expect(document.querySelectorAll('[data-test-subj="warningIcon"]')).toHaveLength(1); + }); + + it('displays a warning only for field name when both field name and type are not found within index patterns', async () => { + const initialState = [ + { name: 'field-that-does-not-exist', type: 'type-that-does-not-exist' }, + ]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.getByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + + expect( + screen.getByText( + `Field "field-that-does-not-exist" is not found within the rule's specified index patterns` + ) + ).toBeVisible(); + + const nameWarningIcon = screen + .getByTestId(`requiredFieldNameSelect-field-that-does-not-exist`) + .querySelector('[data-euiicon-type="warning"]'); + + expect(nameWarningIcon).toBeVisible(); + + /* Make sure only one warning icon is displayed - the one for name */ + expect(document.querySelectorAll('[data-test-subj="warningIcon"]')).toHaveLength(1); + }); + + it(`doesn't display a warning when all selected fields are found within index patterns`, async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + expect(screen.queryByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeNull(); + }); + + it(`doesn't display a warning for an empty row`, async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + expect(screen.queryByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeNull(); + }); + + it(`doesn't display a warning when field is invalid`, async () => { + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'customField', + }); + + expect(screen.getByText('Field type is required')).toBeVisible(); + + expect(screen.queryByTestId(`customField-warningText`)).toBeNull(); + }); + }); + + describe('validation', () => { + it('form is invalid when only field name is empty', async () => { + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('empty'), + optionText: 'customType', + }); + + expect(screen.getByText('Field name is required')).toBeVisible(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'customField', + }); + + expect(screen.queryByText('Field name is required')).toBeNull(); + }); + + it('form is invalid when only field type is empty', async () => { + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'customField', + }); + + expect(screen.getByText('Field type is required')).toBeVisible(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('empty'), + optionText: 'customType', + }); + + expect(screen.queryByText('Field type is required')).toBeNull(); + }); + + it('form is invalid when same field name is selected more than once', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + createIndexPatternField({ name: 'field2', esTypes: ['string'] }), + ]; + + render(); + + await addRequiredFieldRow(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + optionText: 'field1', + }); + + expect(screen.getByText('Field name "field1" is already used')).toBeVisible(); + + await typeInCustomComboBoxOption({ + comboBoxToggleButton: getLastSelectToggleButtonForName(), + optionText: 'field2', + }); + + expect(screen.queryByText('Field name "field1" is already used')).toBeNull(); + }); + + it('form is valid when both field name and type are empty', async () => { + const handleSubmit = jest.fn(); + + render(); + + await addRequiredFieldRow(); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: '', type: '' }], + isValid: true, + }); + }); + }); + + describe('form submission', () => { + it('submits undefined when no required fields are selected', async () => { + const handleSubmit = jest.fn(); + + render(); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalledWith({ + data: undefined, + isValid: true, + }); + }); + }); + + it('submits undefined when all selected fields were removed', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const handleSubmit = jest.fn(); + + render(); + + await act(async () => { + fireEvent.click(screen.getByTestId('removeRequiredFieldButton-field1')); + }); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalledWith({ + data: undefined, + isValid: true, + }); + }); + }); + + it('submits newly added required fields', async () => { + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + const handleSubmit = jest.fn(); + + render(); + + await addRequiredFieldRow(); + + await selectFirstEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + }); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: 'field1', type: 'string' }], + isValid: true, + }); + }); + + it('submits previously saved required fields', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + const handleSubmit = jest.fn(); + + render( + + ); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: 'field1', type: 'string' }], + isValid: true, + }); + }); + + it('submits updated required fields', async () => { + const initialState = [{ name: 'field1', type: 'string' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + createIndexPatternField({ name: 'field2', esTypes: ['keyword', 'date'] }), + ]; + + const handleSubmit = jest.fn(); + + render( + + ); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('field1'), + optionText: 'field2', + }); + + await selectEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForType('keyword'), + optionText: 'date', + }); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: 'field2', type: 'date' }], + isValid: true, + }); + }); + + it('submits a form with warnings', async () => { + const initialState = [{ name: 'name-that-does-not-exist', type: 'type-that-does-not-exist' }]; + + const indexPatternFields: DataViewFieldBase[] = [ + createIndexPatternField({ name: 'field1', esTypes: ['string'] }), + ]; + + const handleSubmit = jest.fn(); + + render( + + ); + + expect(screen.queryByTestId(REQUIRED_FIELDS_GENERAL_WARNING_TEST_ID)).toBeVisible(); + + await submitForm(); + + expect(handleSubmit).toHaveBeenCalledWith({ + data: [{ name: 'name-that-does-not-exist', type: 'type-that-does-not-exist' }], + isValid: true, + }); + }); + }); +}); + +export function createIndexPatternField(overrides: Partial): DataViewFieldBase { + return { + name: 'one', + type: 'string', + esTypes: [], + ...overrides, + }; +} + +async function getDropdownOptions(dropdownToggleButton: HTMLElement): Promise { + await showEuiComboBoxOptions(dropdownToggleButton); + + const options = screen.getAllByRole('option').map((option) => option.textContent) as string[]; + + fireEvent.click(dropdownToggleButton); + + return options; +} + +export function addRequiredFieldRow(): Promise { + return act(async () => { + fireEvent.click(screen.getByText('Add required field')); + }); +} + +function showEuiComboBoxOptions(comboBoxToggleButton: HTMLElement): Promise { + fireEvent.click(comboBoxToggleButton); + + return waitFor(() => { + const listWithOptionsElement = document.querySelector('[role="listbox"]'); + const emptyListElement = document.querySelector('.euiComboBoxOptionsList__empty'); + + expect(listWithOptionsElement || emptyListElement).toBeInTheDocument(); + }); +} + +type SelectEuiComboBoxOptionParameters = + | { + comboBoxToggleButton: HTMLElement; + optionIndex: number; + optionText?: undefined; + } + | { + comboBoxToggleButton: HTMLElement; + optionText: string; + optionIndex?: undefined; + }; + +function selectEuiComboBoxOption({ + comboBoxToggleButton, + optionIndex, + optionText, +}: SelectEuiComboBoxOptionParameters): Promise { + return act(async () => { + await showEuiComboBoxOptions(comboBoxToggleButton); + + const options = Array.from( + document.querySelectorAll('[data-test-subj*="comboBoxOptionsList"] [role="option"]') + ); + + if (typeof optionText === 'string') { + const optionToSelect = options.find((option) => option.textContent === optionText); + + if (!optionToSelect) { + throw new Error( + `Could not find option with text "${optionText}". Available options: ${options + .map((option) => option.textContent) + .join(', ')}` + ); + } + + fireEvent.click(optionToSelect); + } else { + fireEvent.click(options[optionIndex]); + } + }); +} + +function selectFirstEuiComboBoxOption({ + comboBoxToggleButton, +}: { + comboBoxToggleButton: HTMLElement; +}): Promise { + return selectEuiComboBoxOption({ comboBoxToggleButton, optionIndex: 0 }); +} + +function typeInCustomComboBoxOption({ + comboBoxToggleButton, + optionText, +}: { + comboBoxToggleButton: HTMLElement; + optionText: string; +}) { + return act(async () => { + await showEuiComboBoxOptions(comboBoxToggleButton); + + fireEvent.change(document.activeElement as HTMLInputElement, { target: { value: optionText } }); + fireEvent.keyDown(document.activeElement as HTMLInputElement, { key: 'Enter' }); + }); +} + +function getLastSelectToggleButtonForName(): HTMLElement { + const allNameSelects = screen.getAllByTestId(/requiredFieldNameSelect-.*/); + const lastNameSelect = allNameSelects[allNameSelects.length - 1]; + + return lastNameSelect.querySelector('[data-test-subj="comboBoxToggleListButton"]') as HTMLElement; +} + +export function getSelectToggleButtonForName(value: string): HTMLElement { + return screen + .getByTestId(`requiredFieldNameSelect-${value}`) + .querySelector('[data-test-subj="comboBoxToggleListButton"]') as HTMLElement; +} + +function getSelectToggleButtonForType(value: string): HTMLElement { + return screen + .getByTestId(`requiredFieldTypeSelect-${value}`) + .querySelector('[data-test-subj="comboBoxToggleListButton"]') as HTMLElement; +} + +function submitForm(): Promise { + return act(async () => { + fireEvent.click(screen.getByText('Submit')); + }); +} + +interface TestFormProps { + initialState?: RequiredFieldInput[]; + onSubmit?: (args: { data: RequiredFieldInput[]; isValid: boolean }) => void; + indexPatternFields?: DataViewFieldBase[]; + isIndexPatternLoading?: boolean; +} + +function TestForm({ + indexPatternFields, + initialState = [], + isIndexPatternLoading, + onSubmit, +}: TestFormProps): JSX.Element { + const { form } = useForm({ + defaultValue: { + requiredFieldsField: initialState, + }, + onSubmit: async (formData, isValid) => + onSubmit?.({ data: formData.requiredFieldsField, isValid }), + }); + + return ( + +
+ + + +
+ ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx new file mode 100644 index 0000000000000..f53c41ce98d00 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields.tsx @@ -0,0 +1,213 @@ +/* + * 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, { useMemo } from 'react'; +import { EuiButtonEmpty, EuiCallOut, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import type { DataViewFieldBase } from '@kbn/es-query'; +import type { RequiredFieldInput } from '../../../../../common/api/detection_engine'; +import { UseArray, useFormData } from '../../../../shared_imports'; +import type { FormHook, ArrayItem } from '../../../../shared_imports'; +import { RequiredFieldsHelpInfo } from './required_fields_help_info'; +import { RequiredFieldRow } from './required_fields_row'; +import * as defineRuleI18n from '../../../rule_creation_ui/components/step_define_rule/translations'; +import * as i18n from './translations'; + +interface RequiredFieldsComponentProps { + path: string; + indexPatternFields?: DataViewFieldBase[]; + isIndexPatternLoading?: boolean; +} + +const RequiredFieldsComponent = ({ + path, + indexPatternFields = [], + isIndexPatternLoading = false, +}: RequiredFieldsComponentProps) => { + return ( + + {({ items, addItem, removeItem, form }) => ( + + )} + + ); +}; + +interface RequiredFieldsListProps { + items: ArrayItem[]; + addItem: () => void; + removeItem: (id: number) => void; + indexPatternFields: DataViewFieldBase[]; + isIndexPatternLoading: boolean; + path: string; + form: FormHook; +} + +const RequiredFieldsList = ({ + items, + addItem, + removeItem, + indexPatternFields, + isIndexPatternLoading, + path, + form, +}: RequiredFieldsListProps) => { + /* + This component should only re-render when either the "index" form field (index patterns) or the required fields change. + + By default, the `useFormData` hook triggers a re-render whenever any form field changes. + It also allows optimization by passing a "watch" array of field names. The component then only re-renders when these specified fields change. + + However, it doesn't work with fields created using the `UseArray` component. + In `useFormData`, these array fields are stored as "flattened" objects with numbered keys, like { "requiredFields[0]": { ... }, "requiredFields[1]": { ... } }. + The "watch" feature of `useFormData` only works if you pass these "flattened" field names, such as ["requiredFields[0]", "requiredFields[1]", ...], not just "requiredFields". + + To work around this, we manually construct a list of "flattened" field names to watch, based on the current state of the form. + This is a temporary solution and ideally, `useFormData` should be updated to handle this scenario. + */ + + const internalField = form.getFields()[`${path}__array__`] ?? {}; + const internalFieldValue = (internalField?.value ?? []) as ArrayItem[]; + const flattenedFieldNames = internalFieldValue.map((item) => item.path); + + /* + Not using "watch" for the initial render, to let row components render and initialize form fields. + Then we can use the "watch" feature to track their changes. + */ + const hasRenderedInitially = flattenedFieldNames.length > 0; + const fieldsToWatch = hasRenderedInitially ? ['index', ...flattenedFieldNames] : []; + + const [formData] = useFormData({ watch: fieldsToWatch }); + + const fieldValue: RequiredFieldInput[] = formData[path] ?? []; + + const typesByFieldName: Record = useMemo( + () => + indexPatternFields.reduce((accumulator, field) => { + if (field.esTypes?.length) { + accumulator[field.name] = field.esTypes; + } + return accumulator; + }, {} as Record), + [indexPatternFields] + ); + + const allFieldNames = useMemo(() => Object.keys(typesByFieldName), [typesByFieldName]); + + const selectedFieldNames = fieldValue.map(({ name }) => name); + + const availableFieldNames = allFieldNames.filter((name) => !selectedFieldNames.includes(name)); + + const nameWarnings = fieldValue.reduce>((warnings, { name }) => { + if ( + !isIndexPatternLoading && + /* Creating a warning only if "name" value is filled in */ + name !== '' && + !allFieldNames.includes(name) + ) { + warnings[name] = i18n.FIELD_NAME_NOT_FOUND_WARNING(name); + } + return warnings; + }, {}); + + const typeWarnings = fieldValue.reduce>((warnings, { name, type }) => { + if ( + !isIndexPatternLoading && + /* Creating a warning for "type" only if "name" value is filled in */ + name !== '' && + typesByFieldName[name] && + !typesByFieldName[name].includes(type) + ) { + warnings[`${name}-${type}`] = i18n.FIELD_TYPE_NOT_FOUND_WARNING(name, type); + } + return warnings; + }, {}); + + const getWarnings = ({ name, type }: { name: string; type: string }) => ({ + nameWarning: nameWarnings[name] || '', + typeWarning: typeWarnings[`${name}-${type}`] || '', + }); + + const hasEmptyFieldName = fieldValue.some(({ name }) => name === ''); + + const hasWarnings = Object.keys(nameWarnings).length > 0 || Object.keys(typeWarnings).length > 0; + + return ( + <> + {hasWarnings && ( + +

+ {defineRuleI18n.SOURCE}, + }} + /> +

+
+ )} + + + {i18n.REQUIRED_FIELDS_LABEL} + + + } + labelAppend={ + + {i18n.OPTIONAL} + + } + hasChildLabel={false} + labelType="legend" + > + <> + {items.map((item) => ( + + ))} + + + + {i18n.ADD_REQUIRED_FIELD} + + + + + ); +}; + +export const RequiredFields = React.memo(RequiredFieldsComponent); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_help_info.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_help_info.tsx new file mode 100644 index 0000000000000..187f05880d205 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_help_info.tsx @@ -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 React from 'react'; +import { useToggle } from 'react-use'; +import { EuiPopover, EuiText, EuiButtonIcon } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import * as defineRuleI18n from '../../../rule_creation_ui/components/step_define_rule/translations'; +import * as i18n from './translations'; + +/** + * Theme doesn't expose width variables. Using provided size variables will require + * multiplying it by another magic constant. + * + * 320px width looks + * like a [commonly used width in EUI](https://github.com/search?q=repo%3Aelastic%2Feui%20320&type=code). + */ +const POPOVER_WIDTH = 320; + +export function RequiredFieldsHelpInfo(): JSX.Element { + const [isPopoverOpen, togglePopover] = useToggle(false); + + const button = ( + + ); + + return ( + + + {defineRuleI18n.SOURCE}, + }} + /> + + + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx new file mode 100644 index 0000000000000..755f1de413760 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/required_fields_row.tsx @@ -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 React, { useCallback, useMemo } from 'react'; +import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiTextColor } from '@elastic/eui'; +import { UseField } from '../../../../shared_imports'; +import { NameComboBox } from './name_combobox'; +import { TypeComboBox } from './type_combobox'; +import { makeValidateRequiredField } from './make_validate_required_field'; +import * as i18n from './translations'; + +import type { ArrayItem, FieldConfig, FieldHook } from '../../../../shared_imports'; +import type { + RequiredField, + RequiredFieldInput, +} from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; + +interface RequiredFieldRowProps { + item: ArrayItem; + removeItem: (id: number) => void; + typesByFieldName: Record; + availableFieldNames: string[]; + getWarnings: ({ name, type }: { name: string; type: string }) => { + nameWarning: string; + typeWarning: string; + }; + parentFieldPath: string; +} + +export const RequiredFieldRow = ({ + item, + removeItem, + typesByFieldName, + availableFieldNames, + getWarnings, + parentFieldPath, +}: RequiredFieldRowProps) => { + const handleRemove = useCallback(() => removeItem(item.id), [removeItem, item.id]); + + const rowFieldConfig: FieldConfig = + useMemo( + () => ({ + deserializer: (value) => { + const rowValueWithoutEcs: RequiredFieldInput = { + name: value.name, + type: value.type, + }; + + return rowValueWithoutEcs; + }, + validations: [{ validator: makeValidateRequiredField(parentFieldPath) }], + defaultValue: { name: '', type: '' }, + }), + [parentFieldPath] + ); + + return ( + + ); +}; + +interface RequiredFieldFieldProps { + field: FieldHook; + onRemove: () => void; + typesByFieldName: Record; + availableFieldNames: string[]; + getWarnings: ({ name, type }: { name: string; type: string }) => { + nameWarning: string; + typeWarning: string; + }; + itemId: string; +} + +const RequiredFieldField = ({ + field, + typesByFieldName, + onRemove, + availableFieldNames, + getWarnings, + itemId, +}: RequiredFieldFieldProps) => { + const { nameWarning, typeWarning } = getWarnings(field.value); + const warningMessage = nameWarning || typeWarning; + + const [nameError, typeError] = useMemo(() => { + return [ + field.errors.find((error) => 'path' in error && error.path === `${field.path}.name`), + field.errors.find((error) => 'path' in error && error.path === `${field.path}.type`), + ]; + }, [field.path, field.errors]); + const hasError = Boolean(nameError) || Boolean(typeError); + const errorMessage = nameError?.message || typeError?.message; + + return ( + + {warningMessage} + + ) : ( + '' + ) + } + color="warning" + > + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/translations.ts new file mode 100644 index 0000000000000..bed9a4ea0024c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/translations.ts @@ -0,0 +1,105 @@ +/* + * 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'; + +export const REQUIRED_FIELDS_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.requiredFieldsLabel', + { + defaultMessage: 'Required fields', + } +); + +export const FIELD_NAME = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.fieldNameLabel', + { + defaultMessage: 'Field name', + } +); + +export const FIELD_TYPE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.fieldTypeLabel', + { + defaultMessage: 'Field type', + } +); + +export const OPEN_HELP_POPOVER_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.openHelpPopoverAriaLabel', + { + defaultMessage: 'Open help popover', + } +); + +export const REQUIRED_FIELDS_GENERAL_WARNING_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.generalWarningTitle', + { + defaultMessage: `Some fields aren't found within the rule's specified index patterns.`, + } +); + +export const OPTIONAL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.optionalText', + { + defaultMessage: 'Optional', + } +); + +export const REMOVE_REQUIRED_FIELD_BUTTON_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.removeRequiredFieldButtonAriaLabel', + { + defaultMessage: 'Remove required field', + } +); + +export const ADD_REQUIRED_FIELD = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.addRequiredFieldButtonLabel', + { + defaultMessage: 'Add required field', + } +); + +export const FIELD_NAME_NOT_FOUND_WARNING = (name: string) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.fieldNameNotFoundWarning', + { + values: { name }, + defaultMessage: `Field "{name}" is not found within the rule's specified index patterns`, + } + ); + +export const FIELD_TYPE_NOT_FOUND_WARNING = (name: string, type: string) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.fieldTypeNotFoundWarning', + { + values: { name, type }, + defaultMessage: `Field "{name}" with type "{type}" is not found within the rule's specified index patterns`, + } + ); + +export const FIELD_NAME_REQUIRED = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.validation.fieldNameRequired', + { + defaultMessage: 'Field name is required', + } +); + +export const FIELD_TYPE_REQUIRED = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.validation.fieldTypeRequired', + { + defaultMessage: 'Field type is required', + } +); + +export const FIELD_NAME_USED_MORE_THAN_ONCE = (name: string) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDescription.requiredFields.validation.fieldNameUsedMoreThanOnce', + { + values: { name }, + defaultMessage: 'Field name "{name}" is already used', + } + ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/type_combobox.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/type_combobox.tsx new file mode 100644 index 0000000000000..48d5a009c4abe --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/type_combobox.tsx @@ -0,0 +1,151 @@ +/* + * 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, useEffect } from 'react'; +import { EuiComboBox, EuiIcon } from '@elastic/eui'; +import type { EuiComboBoxOptionOption } from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import type { FieldHook } from '../../../../shared_imports'; +import type { RequiredFieldInput } from '../../../../../common/api/detection_engine/model/rule_schema/common_attributes.gen'; +import * as i18n from './translations'; + +interface TypeComboBoxProps { + field: FieldHook; + itemId: string; + typesByFieldName: Record; + typeWarning: string; + typeError: { message: string } | undefined; +} + +export function TypeComboBox({ + field, + itemId, + typesByFieldName, + typeWarning, + typeError, +}: TypeComboBoxProps) { + const { value, setValue } = field; + + const selectableTypeOptions: Array> = useMemo(() => { + const typesAvailableForSelectedName = typesByFieldName[value.name]; + const isSelectedTypeAvailable = (typesAvailableForSelectedName || []).includes(value.type); + + if (typesAvailableForSelectedName && isSelectedTypeAvailable) { + /* + Case: name is available, type is not available + Selected field name is present in index patterns, so it has one or more types available for it. + Allowing the user to select from them. + */ + + return typesAvailableForSelectedName.map((type) => ({ + label: type, + value: type, + })); + } else if (typesAvailableForSelectedName) { + /* + Case: name is available, type is not available + Selected field name is present in index patterns, but the selected type doesn't exist for it. + Adding the selected type to the list of selectable options since it was selected before. + */ + return typesAvailableForSelectedName + .map((type) => ({ + label: type, + value: type, + })) + .concat({ label: value.type, value: value.type }); + } else if (value.name) { + /* + Case: name is not available (so the type is also not available) + Field name is set (not an empty string), but it's not present in index patterns. + In such case the only selectable type option is the currenty selected type. + */ + return [ + { + label: value.type, + value: value.type, + }, + ]; + } + + return []; + }, [value.name, value.type, typesByFieldName]); + + /* + Using a state for `selectedTypeOptions` instead of using the field value directly + to fix the issue where pressing the backspace key in combobox input would clear the field value + and trigger a validation error. By using a separate state, we can clear the selected option + without clearing the field value. + */ + const [selectedTypeOption, setSelectedTypeOption] = useState< + EuiComboBoxOptionOption | undefined + >(selectableTypeOptions.find((option) => option.value === value.type)); + + useEffect(() => { + /* Re-computing the new selected type option when the field value changes */ + setSelectedTypeOption(selectableTypeOptions.find((option) => option.value === value.type)); + }, [value.type, selectableTypeOptions]); + + const handleTypeChange = useCallback( + (selectedOptions: Array>) => { + const newlySelectedOption: EuiComboBoxOptionOption | undefined = selectedOptions[0]; + + if (!newlySelectedOption) { + /* This occurs when the user hits backspace in combobox */ + setSelectedTypeOption(undefined); + return; + } + + const updatedType = newlySelectedOption?.value || ''; + + const updatedFieldValue: RequiredFieldInput = { + name: value.name, + type: updatedType, + }; + + setValue(updatedFieldValue); + }, + [value.name, setValue] + ); + + const handleAddCustomType = useCallback( + (newType: string) => { + const updatedFieldValue: RequiredFieldInput = { + name: value.name, + type: newType, + }; + + setValue(updatedFieldValue); + }, + [value.name, setValue] + ); + + return ( + + ) : undefined + } + /> + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.test.tsx new file mode 100644 index 0000000000000..235da2208a43b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.test.tsx @@ -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 { pickTypeForName } from './utils'; + +describe('pickTypeForName', () => { + it('returns the current type if it is available for the current name', () => { + const typesByFieldName = { + name1: ['text', 'keyword'], + }; + + expect( + pickTypeForName({ + name: 'name1', + type: 'keyword', + typesByFieldName, + }) + ).toEqual('keyword'); + }); + + it('returns the first available type if the current type is not available for the current name', () => { + const typesByFieldName = { + name1: ['text', 'keyword'], + }; + + expect( + pickTypeForName({ + name: 'name1', + type: 'long', + typesByFieldName, + }) + ).toEqual('text'); + }); + + it('returns the current type if no types are available for the current name', () => { + expect( + pickTypeForName({ + name: 'name1', + type: 'keyword', + typesByFieldName: {}, + }) + ).toEqual('keyword'); + + expect( + pickTypeForName({ + name: 'name1', + type: 'keyword', + typesByFieldName: { + name1: [], + }, + }) + ).toEqual('keyword'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts new file mode 100644 index 0000000000000..55beca264e120 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/required_fields/utils.ts @@ -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. + */ + +interface PickTypeForNameParameters { + name: string; + type: string; + typesByFieldName?: Record; +} + +export function pickTypeForName({ name, type, typesByFieldName = {} }: PickTypeForNameParameters) { + const typesAvailableForName = typesByFieldName[name] || []; + const isCurrentTypeAvailableForNewName = typesAvailableForName.includes(type); + + /* First try to keep the type if it's available for the name */ + if (isCurrentTypeAvailableForNewName) { + return type; + } + + /* + If current type is not available, pick the first available type. + If no type is available, use the current type. + */ + return typesAvailableForName[0] ?? type; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx index aaf588edd3099..8ef2a3751a036 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/helpers.tsx @@ -18,12 +18,9 @@ import { } from '@elastic/eui'; import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils'; -import { castEsToKbnFieldTypeName } from '@kbn/field-types'; - import { isEmpty } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; -import { FieldIcon } from '@kbn/react-field'; import type { ThreatMapping, Type, Threats } from '@kbn/securitysolution-io-ts-alerting-types'; import { FilterBadgeGroup } from '@kbn/unified-search-plugin/public'; @@ -50,8 +47,10 @@ import type { } from '../../../../detections/pages/detection_engine/rules/types'; import { GroupByOptions } from '../../../../detections/pages/detection_engine/rules/types'; import { defaultToEmptyTag } from '../../../../common/components/empty_value'; +import { RequiredFieldIcon } from '../../../rule_management/components/rule_details/required_field_icon'; import { ThreatEuiFlexGroup } from './threat_description'; import { AlertSuppressionLabel } from './alert_suppression_label'; + const NoteDescriptionContainer = styled(EuiFlexItem)` height: 105px; overflow-y: hidden; @@ -566,11 +565,7 @@ export const buildRequiredFieldsDescription = ( - + diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx index de34718ef050f..be1306d706357 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.test.tsx @@ -8,6 +8,7 @@ import React, { useEffect, useState } from 'react'; import { screen, fireEvent, render, within, act, waitFor } from '@testing-library/react'; import type { Type as RuleType } from '@kbn/securitysolution-io-ts-alerting-types'; +import type { DataViewBase } from '@kbn/es-query'; import { StepDefineRule, aggregatableFields } from '.'; import { mockBrowserFields } from '../../../../common/containers/source/mock'; import { useRuleFromTimeline } from '../../../../detections/containers/detection_engine/rules/use_rule_from_timeline'; @@ -18,6 +19,11 @@ import type { FormSubmitHandler } from '../../../../shared_imports'; import { useForm } from '../../../../shared_imports'; import type { DefineStepRule } from '../../../../detections/pages/detection_engine/rules/types'; import { fleetIntegrationsApi } from '../../../fleet_integrations/api/__mocks__'; +import { + addRequiredFieldRow, + createIndexPatternField, + getSelectToggleButtonForName, +} from '../../../rule_creation/components/required_fields/required_fields.test'; // Mocks integrations jest.mock('../../../fleet_integrations/api'); @@ -410,6 +416,116 @@ describe('StepDefineRule', () => { }); }); + describe('required fields', () => { + it('submits a form without selected required fields', async () => { + const initialState = { + index: ['test-index'], + queryBar: { + query: { query: '*:*', language: 'kuery' }, + filters: [], + saved_id: null, + }, + }; + const handleSubmit = jest.fn(); + + render(, { + wrapper: TestProviders, + }); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalled(); + }); + + expect(handleSubmit).toHaveBeenCalledWith( + expect.not.objectContaining({ + requiredFields: expect.anything(), + }), + true + ); + }); + + it('submits saved early required fields without the "ecs" property', async () => { + const initialState = { + index: ['test-index'], + queryBar: { + query: { query: '*:*', language: 'kuery' }, + filters: [], + saved_id: null, + }, + requiredFields: [{ name: 'host.name', type: 'string', ecs: false }], + }; + + const handleSubmit = jest.fn(); + + render(, { + wrapper: TestProviders, + }); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalled(); + }); + + expect(handleSubmit).toHaveBeenCalledWith( + expect.objectContaining({ + requiredFields: [{ name: 'host.name', type: 'string' }], + }), + true + ); + }); + + it('submits newly added required fields', async () => { + const initialState = { + index: ['test-index'], + queryBar: { + query: { query: '*:*', language: 'kuery' }, + filters: [], + saved_id: null, + }, + }; + + const indexPattern: DataViewBase = { + fields: [createIndexPatternField({ name: 'host.name', esTypes: ['string'] })], + title: '', + }; + + const handleSubmit = jest.fn(); + + render( + , + { + wrapper: TestProviders, + } + ); + + await addRequiredFieldRow(); + + await selectFirstEuiComboBoxOption({ + comboBoxToggleButton: getSelectToggleButtonForName('empty'), + }); + + await submitForm(); + + await waitFor(() => { + expect(handleSubmit).toHaveBeenCalled(); + }); + + expect(handleSubmit).toHaveBeenCalledWith( + expect.objectContaining({ + requiredFields: [{ name: 'host.name', type: 'string' }], + }), + true + ); + }); + }); + describe('handleSetRuleFromTimeline', () => { it('updates KQL query correctly', () => { const kqlQuery = { @@ -497,14 +613,16 @@ describe('StepDefineRule', () => { }); interface TestFormProps { - ruleType?: RuleType; initialState?: Partial; + ruleType?: RuleType; + indexPattern?: DataViewBase; onSubmit?: FormSubmitHandler; } function TestForm({ - ruleType = stepDefineDefaultValue.ruleType, initialState, + ruleType = stepDefineDefaultValue.ruleType, + indexPattern = { fields: [], title: '' }, onSubmit, }: TestFormProps): JSX.Element { const [selectedEqlOptions, setSelectedEqlOptions] = useState(stepDefineDefaultValue.eqlOptions); @@ -524,7 +642,7 @@ function TestForm({ threatIndicesConfig={[]} optionsSelected={selectedEqlOptions} setOptionsSelected={setSelectedEqlOptions} - indexPattern={{ fields: [], title: '' }} + indexPattern={indexPattern} isIndexPatternLoading={false} browserFields={{}} isQueryBarValid={true} @@ -560,32 +678,71 @@ function addRelatedIntegrationRow(): Promise { }); } +function setVersion({ input, value }: { input: HTMLInputElement; value: string }): Promise { + return act(async () => { + fireEvent.input(input, { + target: { value }, + }); + }); +} + function showEuiComboBoxOptions(comboBoxToggleButton: HTMLElement): Promise { fireEvent.click(comboBoxToggleButton); return waitFor(() => { - expect(screen.getByRole('listbox')).toBeInTheDocument(); + const listWithOptionsElement = document.querySelector('[role="listbox"]'); + const emptyListElement = document.querySelector('.euiComboBoxOptionsList__empty'); + + expect(listWithOptionsElement || emptyListElement).toBeInTheDocument(); }); } +type SelectEuiComboBoxOptionParameters = + | { + comboBoxToggleButton: HTMLElement; + optionIndex: number; + optionText?: undefined; + } + | { + comboBoxToggleButton: HTMLElement; + optionText: string; + optionIndex?: undefined; + }; + function selectEuiComboBoxOption({ comboBoxToggleButton, optionIndex, -}: { - comboBoxToggleButton: HTMLElement; - optionIndex: number; -}): Promise { + optionText, +}: SelectEuiComboBoxOptionParameters): Promise { return act(async () => { await showEuiComboBoxOptions(comboBoxToggleButton); - fireEvent.click(within(screen.getByRole('listbox')).getAllByRole('option')[optionIndex]); + const options = Array.from( + document.querySelectorAll('[data-test-subj*="comboBoxOptionsList"] [role="option"]') + ); + + if (typeof optionText === 'string') { + const optionToSelect = options.find((option) => option.textContent === optionText); + + if (optionToSelect) { + fireEvent.click(optionToSelect); + } else { + throw new Error( + `Could not find option with text "${optionText}". Available options: ${options + .map((option) => option.textContent) + .join(', ')}` + ); + } + } else { + fireEvent.click(options[optionIndex]); + } }); } -function setVersion({ input, value }: { input: HTMLInputElement; value: string }): Promise { - return act(async () => { - fireEvent.input(input, { - target: { value }, - }); - }); +function selectFirstEuiComboBoxOption({ + comboBoxToggleButton, +}: { + comboBoxToggleButton: HTMLElement; +}): Promise { + return selectEuiComboBoxOption({ comboBoxToggleButton, optionIndex: 0 }); } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx index f0dcadc056315..56073e2a6af59 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx @@ -90,6 +90,7 @@ import type { BrowserField } from '../../../../common/containers/source'; import { useFetchIndex } from '../../../../common/containers/source'; import { NewTermsFields } from '../new_terms_fields'; import { ScheduleItem } from '../../../rule_creation/components/schedule_item_form'; +import { RequiredFields } from '../../../rule_creation/components/required_fields'; import { DocLink } from '../../../../common/components/links_to_docs/doc_link'; import { defaultCustomQuery } from '../../../../detections/pages/detection_engine/rules/utils'; import { MultiSelectFieldsAutocomplete } from '../multi_select_fields'; @@ -915,7 +916,6 @@ const StepDefineRuleComponent: FC = ({ )} - {isQueryRule(ruleType) && ( <> @@ -940,7 +940,6 @@ const StepDefineRuleComponent: FC = ({ )} - <> = ({ /> - <> @@ -1116,6 +1114,19 @@ const StepDefineRuleComponent: FC = ({ + + + {!isMlRule(ruleType) && ( + <> + + + + )} + = { }, relatedIntegrations: { type: FIELD_TYPES.JSON, + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldRelatedIntegrationsLabel', + { + defaultMessage: 'Related integrations', + } + ), }, requiredFields: { label: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.test.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.test.ts index dc4394be257e5..2f5065eb113be 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.test.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.test.ts @@ -10,29 +10,28 @@ import { useEsqlIndex } from './use_esql_index'; const validEsqlQuery = 'from auditbeat* metadata _id, _index, _version'; describe('useEsqlIndex', () => { - it('should return empty array if isQueryReadEnabled is undefined', () => { - const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'esql', undefined)); + it('should return parsed index array from a valid query', async () => { + const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'esql')); - expect(result.current).toEqual([]); + expect(result.current).toEqual(['auditbeat*']); }); - it('should return empty array if isQueryReadEnabled is false', () => { - const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'esql', false)); + it('should return empty array if rule type is not esql', async () => { + const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'query')); expect(result.current).toEqual([]); }); - it('should return empty array if rule type is not esql', () => { - const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'query', true)); - expect(result.current).toEqual([]); - }); - it('should return empty array if query is empty', () => { - const { result } = renderHook(() => useEsqlIndex('', 'esql', true)); + it('should return empty array if query is empty', async () => { + const { result } = renderHook(() => useEsqlIndex('', 'esql')); expect(result.current).toEqual([]); }); - it('should return parsed index array from a valid query', () => { - const { result } = renderHook(() => useEsqlIndex(validEsqlQuery, 'esql', true)); - expect(result.current).toEqual(['auditbeat*']); + it('should return empty array if invalid query is causing a TypeError in ES|QL parser', async () => { + const typeErrorCausingQuery = 'from auditbeat* []'; + + const { result } = renderHook(() => useEsqlIndex(typeErrorCausingQuery, 'esql')); + + expect(result.current).toEqual([]); }); }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.ts index 508f2272b69ab..358c1fc70a945 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/hooks/use_esql_index.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; import { getIndexListFromIndexString } from '@kbn/securitysolution-utils'; @@ -15,23 +16,39 @@ import { isEsqlRule } from '../../../../common/detection_engine/utils'; /** * parses ES|QL query and returns memoized array of indices - * @param query - ES|QL query to retrieve index from + * @param query - ES|QL query to retrieve indices from * @param ruleType - rule type value - * @param isQueryReadEnabled - if not enabled, return empty array. Useful if we know form or query is not valid and we don't want to retrieve index - * @returns + * @returns string[] - array of indices. Array is empty if query is invalid or ruleType is not 'esql'. */ -export const useEsqlIndex = ( - query: Query['query'], - ruleType: Type, - isQueryReadEnabled: boolean | undefined -) => { +export const useEsqlIndex = (query: Query['query'], ruleType: Type): string[] => { + const [debouncedQuery, setDebouncedQuery] = useState(query); + + useDebounce( + () => { + /* + Triggerring the ES|QL parser a few moments after the user has finished typing + to avoid unnecessary calls to the parser. + */ + setDebouncedQuery(query); + }, + 300, + [query] + ); + const indexString = useMemo(() => { - if (!isQueryReadEnabled) { + const esqlQuery = + typeof debouncedQuery === 'string' && isEsqlRule(ruleType) ? debouncedQuery : undefined; + + try { + return getIndexPatternFromESQLQuery(esqlQuery); + } catch (error) { + /* + Some invalid queries cause ES|QL parser to throw a TypeError. + Treating such cases as if parser returned an empty string. + */ return ''; } - const esqlQuery = typeof query === 'string' && isEsqlRule(ruleType) ? query : undefined; - return getIndexPatternFromESQLQuery(esqlQuery); - }, [query, isQueryReadEnabled, ruleType]); + }, [debouncedQuery, ruleType]); const index = useMemo(() => getIndexListFromIndexString(indexString), [indexString]); return index; 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 20a432cdc1420..b61cdbc386ee1 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 @@ -140,6 +140,7 @@ describe('helpers', () => { version: '^1.2.3', }, ], + required_fields: [{ name: 'host.name', type: 'keyword' }], }; expect(result).toEqual(expected); @@ -178,6 +179,20 @@ describe('helpers', () => { }); }); + test('filters out empty required fields', () => { + const result = formatDefineStepData({ + ...mockData, + requiredFields: [ + { name: 'host.name', type: 'keyword' }, + { name: '', type: '' }, + ], + }); + + expect(result).toMatchObject({ + required_fields: [{ name: 'host.name', type: 'keyword' }], + }); + }); + describe('saved_query and query rule types', () => { test('returns query rule if savedId provided but shouldLoadQueryDynamically != true', () => { const mockStepData: DefineStepRule = { @@ -567,6 +582,7 @@ describe('helpers', () => { version: '^1.2.3', }, ], + required_fields: [{ name: 'host.name', type: 'keyword' }], }; 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 3054a894d0df1..11da431e3e602 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 @@ -48,6 +48,7 @@ import { import type { RuleCreateProps, AlertSuppression, + RequiredFieldInput, } from '../../../../../common/api/detection_engine/model/rule_schema'; import { stepActionsDefaultValue } from '../../../rule_creation/components/step_rule_actions'; import { DEFAULT_SUPPRESSION_MISSING_FIELDS_STRATEGY } from '../../../../../common/detection_engine/constants'; @@ -395,6 +396,12 @@ export const getStepDataDataSource = ( return copiedStepData; }; +/** + * Strips away form rows that were not filled out by the user + */ +const removeEmptyRequiredFields = (requiredFields: RequiredFieldInput[]): RequiredFieldInput[] => + requiredFields.filter((field) => field.name !== '' && field.type !== ''); + export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStepRuleJson => { const stepData = getStepDataDataSource(defineStepData); @@ -426,6 +433,8 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep } : {}; + const requiredFields = removeEmptyRequiredFields(defineStepData.requiredFields ?? []); + const typeFields = isMlFields(ruleFields) ? { anomaly_threshold: ruleFields.anomalyThreshold, @@ -438,6 +447,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, saved_id: ruleFields.queryBar?.saved_id ?? undefined, + required_fields: requiredFields, ...(ruleType === 'threshold' && { threshold: { field: ruleFields.threshold?.field ?? [], @@ -465,6 +475,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, saved_id: ruleFields.queryBar?.saved_id ?? undefined, + required_fields: requiredFields, threat_index: ruleFields.threatIndex, threat_query: ruleFields.threatQueryBar?.query?.query as string, threat_filters: ruleFields.threatQueryBar?.filters, @@ -479,6 +490,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, saved_id: ruleFields.queryBar?.saved_id ?? undefined, + required_fields: requiredFields, timestamp_field: ruleFields.eqlOptions?.timestampField, event_category_override: ruleFields.eqlOptions?.eventCategoryField, tiebreaker_field: ruleFields.eqlOptions?.tiebreakerField, @@ -490,6 +502,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep filters: ruleFields.queryBar?.filters, language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, + required_fields: requiredFields, new_terms_fields: ruleFields.newTermsFields, history_window_start: `now-${ruleFields.historyWindowSize}`, ...alertSuppressionFields, @@ -498,6 +511,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep ? { language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, + required_fields: requiredFields, } : { ...alertSuppressionFields, @@ -506,6 +520,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep language: ruleFields.queryBar?.query?.language, query: ruleFields.queryBar?.query?.query as string, saved_id: undefined, + required_fields: requiredFields, type: 'query' as const, // rule only be updated as saved_query type if it has saved_id and shouldLoadQueryDynamically checkbox checked ...(['query', 'saved_query'].includes(ruleType) && diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/index.tsx index d57198522b388..806ea9f336bd5 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_creation/index.tsx @@ -210,11 +210,9 @@ const CreateRulePageComponent: React.FC = () => { const [isThreatQueryBarValid, setIsThreatQueryBarValid] = useState(false); const esqlQueryForAboutStep = useEsqlQueryForAboutStep({ defineStepData, activeStep }); - const esqlIndex = useEsqlIndex( - defineStepData.queryBar.query.query, - ruleType, - defineStepForm.isValid - ); + + const esqlIndex = useEsqlIndex(defineStepData.queryBar.query.query, ruleType); + const memoizedIndex = useMemo( () => (isEsqlRuleValue ? esqlIndex : defineStepData.index), [defineStepData.index, esqlIndex, isEsqlRuleValue] diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx index 768b5a9903691..47b67c8ed720a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/pages/rule_editing/index.tsx @@ -150,13 +150,9 @@ const EditRulePageComponent: FC<{ rule: RuleResponse }> = ({ rule }) => { }); const esqlQueryForAboutStep = useEsqlQueryForAboutStep({ defineStepData, activeStep }); - const esqlIndex = useEsqlIndex( - defineStepData.queryBar.query.query, - defineStepData.ruleType, - // allow to compute index from query only when query is valid or user switched to another tab - // to prevent multiple data view initiations with partly typed index names - defineStepForm.isValid || activeStep !== RuleStep.defineRule - ); + + const esqlIndex = useEsqlIndex(defineStepData.queryBar.query.query, defineStepData.ruleType); + const memoizedIndex = useMemo( () => (isEsqlRule(defineStepData.ruleType) ? esqlIndex : defineStepData.index), [defineStepData.index, esqlIndex, defineStepData.ruleType] diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/required_field_icon.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/required_field_icon.tsx new file mode 100644 index 0000000000000..0001bae25dd89 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/required_field_icon.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 { ES_FIELD_TYPES } from '@kbn/field-types'; +import { FieldIcon } from '@kbn/field-utils'; +import type { FieldIconProps } from '@kbn/field-utils'; + +function mapEsTypesToIconProps(type: string) { + switch (type) { + case ES_FIELD_TYPES._ID: + case ES_FIELD_TYPES._INDEX: + /* In Discover "_id" and "_index" have the "keyword" icon. Doing same here for consistency */ + return { type: 'keyword' }; + case ES_FIELD_TYPES.OBJECT: + return { type, iconType: 'tokenObject' }; + case ES_FIELD_TYPES.DATE_NANOS: + return { type: 'date' }; + case ES_FIELD_TYPES.FLOAT: + case ES_FIELD_TYPES.HALF_FLOAT: + case ES_FIELD_TYPES.SCALED_FLOAT: + case ES_FIELD_TYPES.DOUBLE: + case ES_FIELD_TYPES.INTEGER: + case ES_FIELD_TYPES.LONG: + case ES_FIELD_TYPES.SHORT: + case ES_FIELD_TYPES.UNSIGNED_LONG: + case ES_FIELD_TYPES.AGGREGATE_METRIC_DOUBLE: + case ES_FIELD_TYPES.FLOAT_RANGE: + case ES_FIELD_TYPES.DOUBLE_RANGE: + case ES_FIELD_TYPES.INTEGER_RANGE: + case ES_FIELD_TYPES.LONG_RANGE: + case ES_FIELD_TYPES.BYTE: + case ES_FIELD_TYPES.TOKEN_COUNT: + return { type: 'number' }; + default: + return { type }; + } +} + +interface RequiredFieldIconProps extends FieldIconProps { + type: string; + label?: string; + 'data-test-subj': string; +} + +/** + * `FieldIcon` component with addtional icons for types that are not handled by the `FieldIcon` component. + */ +export function RequiredFieldIcon({ + type, + label = type, + 'data-test-subj': dataTestSubj, + ...props +}: RequiredFieldIconProps) { + return ( + + ); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx index 912af1b0c4589..35f429f776fab 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx @@ -24,8 +24,6 @@ import type { Filter } from '@kbn/es-query'; import type { SavedQuery } from '@kbn/data-plugin/public'; import { mapAndFlattenFilters } from '@kbn/data-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { FieldIcon } from '@kbn/react-field'; -import { castEsToKbnFieldTypeName } from '@kbn/field-types'; import { FilterItems } from '@kbn/unified-search-plugin/public'; import type { AlertSuppressionMissingFieldsStrategy, @@ -53,6 +51,7 @@ import { BadgeList } from './badge_list'; import { DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; import * as i18n from './translations'; import { useAlertSuppression } from '../../logic/use_alert_suppression'; +import { RequiredFieldIcon } from './required_field_icon'; import { filtersStyles, queryStyles, @@ -254,17 +253,14 @@ interface RequiredFieldsProps { const RequiredFields = ({ requiredFields }: RequiredFieldsProps) => { const styles = useRequiredFieldsStyles(); + return ( {requiredFields.map((rF, index) => ( - + ({ dataViewId: undefined, queryBar: mockQueryBar, threatQueryBar: mockQueryBar, - requiredFields: [], + requiredFields: [{ name: 'host.name', type: 'keyword' }], relatedIntegrations: [ { package: 'aws', 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 4757c9f29dfdc..1bf915e1a122f 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 @@ -26,7 +26,6 @@ import type { FieldValueThreshold } from '../../../../detection_engine/rule_crea import type { BuildingBlockType, RelatedIntegrationArray, - RequiredFieldArray, RuleAuthorArray, RuleLicense, RuleNameOverride, @@ -38,6 +37,7 @@ import type { AlertSuppression, ThresholdAlertSuppression, RelatedIntegration, + RequiredFieldInput, } from '../../../../../common/api/detection_engine/model/rule_schema'; import type { SortOrder } from '../../../../../common/api/detection_engine'; import type { EqlOptionsSelected } from '../../../../../common/search_strategy'; @@ -147,7 +147,7 @@ export interface DefineStepRule { dataViewId?: string; dataViewTitle?: string; relatedIntegrations?: RelatedIntegrationArray; - requiredFields: RequiredFieldArray; + requiredFields?: RequiredFieldInput[]; ruleType: Type; timeline: FieldValueTimeline; threshold: FieldValueThreshold; @@ -226,6 +226,7 @@ export interface DefineStepRuleJson { tiebreaker_field?: string; alert_suppression?: AlertSuppression | ThresholdAlertSuppression; related_integrations?: RelatedIntegration[]; + required_fields?: RequiredFieldInput[]; } export interface AboutStepRuleJson { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts index 95a8687324d1c..c08edbc0c5cc5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts @@ -53,6 +53,7 @@ import { extractRuleNameOverrideObject } from './extract_rule_name_override_obje import { extractRuleSchedule } from './extract_rule_schedule'; import { extractTimelineTemplateReference } from './extract_timeline_template_reference'; import { extractTimestampOverrideObject } from './extract_timestamp_override_object'; +import { addEcsToRequiredFields } from '../../../../rule_management/utils/utils'; /** * Normalizes a given rule to the form which is suitable for passing to the diff algorithm. @@ -133,7 +134,7 @@ const extractDiffableCommonFields = ( note: rule.note ?? '', setup: rule.setup ?? '', related_integrations: rule.related_integrations ?? [], - required_fields: rule.required_fields ?? [], + required_fields: addEcsToRequiredFields(rule.required_fields), author: rule.author ?? [], license: rule.license ?? '', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts index 40a3d048fb518..38ead608a3b75 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.ts @@ -7,7 +7,6 @@ import * as z from 'zod'; import { - RequiredFieldArray, RuleSignatureId, RuleVersion, BaseCreateProps, @@ -33,6 +32,5 @@ export const PrebuiltRuleAsset = BaseCreateProps.and(TypeSpecificCreateProps).an z.object({ rule_id: RuleSignatureId, version: RuleVersion, - required_fields: RequiredFieldArray.optional(), }) ); 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 d36f0ab4ad66e..ec790f9f6f71b 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 @@ -14,6 +14,7 @@ import { transformRuleToAlertAction } from '../../../../../../common/detection_e import type { InternalRuleUpdate, RuleParams, RuleAlertType } from '../../../rule_schema'; import { transformToActionFrequency } from '../../normalization/rule_actions'; import { typeSpecificSnakeToCamel } from '../../normalization/rule_converters'; +import { addEcsToRequiredFields } from '../../utils/utils'; export interface UpdateRulesOptions { rulesClient: RulesClient; @@ -38,6 +39,7 @@ export const updateRules = async ({ const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); const enabled = ruleUpdate.enabled ?? true; + const newInternalRule: InternalRuleUpdate = { name: ruleUpdate.name, tags: ruleUpdate.tags ?? [], @@ -58,7 +60,7 @@ export const updateRules = async ({ meta: ruleUpdate.meta, maxSignals: ruleUpdate.max_signals ?? DEFAULT_MAX_SIGNALS, relatedIntegrations: ruleUpdate.related_integrations ?? [], - requiredFields: existingRule.params.requiredFields, + requiredFields: addEcsToRequiredFields(ruleUpdate.required_fields), riskScore: ruleUpdate.risk_score, riskScoreMapping: ruleUpdate.risk_score_mapping ?? [], ruleNameOverride: ruleUpdate.rule_name_override, 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 50a1cecce88c8..355fa626f7848 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 @@ -22,7 +22,6 @@ import { import type { PatchRuleRequestBody } from '../../../../../common/api/detection_engine/rule_management'; import type { RelatedIntegrationArray, - RequiredFieldArray, RuleCreateProps, TypeSpecificCreateProps, TypeSpecificResponse, @@ -78,6 +77,7 @@ import type { } from '../../rule_schema'; import { transformFromAlertThrottle, transformToActionFrequency } from './rule_actions'; import { + addEcsToRequiredFields, convertAlertSuppressionToCamel, convertAlertSuppressionToSnake, migrateLegacyInvestigationFields, @@ -431,7 +431,6 @@ export const patchTypeSpecificSnakeToCamel = ( export const convertPatchAPIToInternalSchema = ( nextParams: PatchRuleRequestBody & { related_integrations?: RelatedIntegrationArray; - required_fields?: RequiredFieldArray; }, existingRule: SanitizedRule ): InternalRuleUpdate => { @@ -462,7 +461,7 @@ export const convertPatchAPIToInternalSchema = ( meta: nextParams.meta ?? existingParams.meta, maxSignals: nextParams.max_signals ?? existingParams.maxSignals, relatedIntegrations: nextParams.related_integrations ?? existingParams.relatedIntegrations, - requiredFields: nextParams.required_fields ?? existingParams.requiredFields, + requiredFields: addEcsToRequiredFields(nextParams.required_fields), riskScore: nextParams.risk_score ?? existingParams.riskScore, riskScoreMapping: nextParams.risk_score_mapping ?? existingParams.riskScoreMapping, ruleNameOverride: nextParams.rule_name_override ?? existingParams.ruleNameOverride, @@ -487,11 +486,9 @@ export const convertPatchAPIToInternalSchema = ( }; }; -// eslint-disable-next-line complexity export const convertCreateAPIToInternalSchema = ( input: RuleCreateProps & { related_integrations?: RelatedIntegrationArray; - required_fields?: RequiredFieldArray; }, immutable = false, defaultEnabled = true @@ -537,7 +534,7 @@ export const convertCreateAPIToInternalSchema = ( version: input.version ?? 1, exceptionsList: input.exceptions_list ?? [], relatedIntegrations: input.related_integrations ?? [], - requiredFields: input.required_fields ?? [], + requiredFields: addEcsToRequiredFields(input.required_fields), setup: input.setup ?? '', ...typeSpecificParams, }, @@ -776,6 +773,7 @@ export const convertPrebuiltRuleAssetToRuleResponse = ( return RuleResponse.parse({ ...prebuiltRuleAssetDefaults, ...prebuiltRuleAsset, + required_fields: addEcsToRequiredFields(prebuiltRuleAsset.required_fields), ...ruleResponseSpecificFields, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts index bda8cd7a688ca..66fa635e768ad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/utils.ts @@ -9,6 +9,8 @@ import { partition } from 'lodash/fp'; import pMap from 'p-map'; import { v4 as uuidv4 } from 'uuid'; +import { ecsFieldMap } from '@kbn/alerts-as-data-utils'; + import type { ActionsClient, FindActionResult } from '@kbn/actions-plugin/server'; import type { FindResult, PartialRule } from '@kbn/alerting-plugin/server'; import type { SavedObjectsClientContract } from '@kbn/core/server'; @@ -18,6 +20,8 @@ import type { AlertSuppression, AlertSuppressionCamel, InvestigationFields, + RequiredField, + RequiredFieldInput, RuleResponse, } from '../../../../../common/api/detection_engine/model/rule_schema'; import type { @@ -388,3 +392,19 @@ export const migrateLegacyInvestigationFields = ( return investigationFields; }; + +/* + Computes the boolean "ecs" property value for each required field based on the ECS field map. + "ecs" property indicates whether the required field is an ECS field or not. +*/ +export const addEcsToRequiredFields = (requiredFields?: RequiredFieldInput[]): RequiredField[] => + (requiredFields ?? []).map((requiredFieldWithoutEcs) => { + const isEcsField = Boolean( + ecsFieldMap[requiredFieldWithoutEcs.name]?.type === requiredFieldWithoutEcs.type + ); + + return { + ...requiredFieldWithoutEcs, + ecs: isEcsField, + }; + }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts index 86a3dc4ff6c1f..08e5fa5fd879d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/strip_non_ecs_fields.ts @@ -67,9 +67,9 @@ const getIsEcsFieldObject = (path: string) => { /** * checks if path is in Ecs mapping */ -const getIsEcsField = (path: string) => { +export const getIsEcsField = (path: string): boolean => { const ecsField = ecsFieldMap[path as keyof typeof ecsFieldMap]; - const isEcsField = !!ecsField || ecsObjectFields[path]; + const isEcsField = Boolean(!!ecsField || ecsObjectFields[path]); return isEcsField; }; diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index b5778dbf20e39..bb26581356fa1 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -200,6 +200,7 @@ "@kbn/security-plugin-types-server", "@kbn/deeplinks-security", "@kbn/react-kibana-context-render", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/field-utils" ] } diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action.ts index f728b011b6801..22ac52dc2a333 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action.ts @@ -142,6 +142,10 @@ export default ({ getService }: FtrProviderContext): void => { ], max_signals: 100, setup: '# some setup markdown', + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }; const mockRule = getCustomQueryRuleParams(defaultableFields); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules.ts index 63b1a4dfecdc7..925961c5a3720 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules.ts @@ -9,8 +9,8 @@ import expect from 'expect'; import { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; import { - getSimpleRule, getCustomQueryRuleParams, + getSimpleRule, getSimpleRuleOutputWithoutRuleId, getSimpleRuleWithoutRuleId, removeServerGeneratedProperties, @@ -70,7 +70,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should create a rule with defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const ruleCreateProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -78,10 +78,22 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }); + const expectedRule = { + ...ruleCreateProperties, + required_fields: [ + { name: '@timestamp', type: 'date', ecs: true }, + { name: 'my-non-ecs-field', type: 'keyword', ecs: false }, + ], + }; + const { body: createdRuleResponse } = await securitySolutionApi - .createRule({ body: expectedRule }) + .createRule({ body: ruleCreateProperties }) .expect(200); expect(createdRuleResponse).toMatchObject(expectedRule); @@ -207,6 +219,32 @@ export default ({ getService }: FtrProviderContext) => { ); }); }); + + describe('required_fields', () => { + it('creates a rule with required_fields defaulted to an empty array when not present', async () => { + const customQueryRuleParams = getCustomQueryRuleParams({ + rule_id: 'rule-without-required-fields', + }); + + expect(customQueryRuleParams.required_fields).toBeUndefined(); + + const { body } = await securitySolutionApi + .createRule({ + body: customQueryRuleParams, + }) + .expect(200); + + expect(body.required_fields).toEqual([]); + + const { body: createdRule } = await securitySolutionApi + .readRule({ + query: { rule_id: 'rule-without-required-fields' }, + }) + .expect(200); + + expect(createdRule.required_fields).toEqual([]); + }); + }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules_bulk.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules_bulk.ts index dc02f8450f411..4356c8b82b8b4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules_bulk.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_creation/basic_license_essentials_tier/create_rules_bulk.ts @@ -69,7 +69,7 @@ export default ({ getService }: FtrProviderContext): void => { }); it('should create a rule with defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const ruleCreateProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -77,10 +77,22 @@ export default ({ getService }: FtrProviderContext): void => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }); + const expectedRule = { + ...ruleCreateProperties, + required_fields: [ + { name: '@timestamp', type: 'date', ecs: true }, + { name: 'my-non-ecs-field', type: 'keyword', ecs: false }, + ], + }; + const { body: createdRulesBulkResponse } = await securitySolutionApi - .bulkCreateRules({ body: [expectedRule] }) + .bulkCreateRules({ body: [ruleCreateProperties] }) .expect(200); expect(createdRulesBulkResponse[0]).toMatchObject(expectedRule); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/export_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/export_rules.ts index 5986e4d40fe3a..783d0bb42fd87 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/export_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/export_rules.ts @@ -57,9 +57,22 @@ export default ({ getService }: FtrProviderContext): void => { { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], setup: '# some setup markdown', + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }; + const ruleToExport = getCustomQueryRuleParams(defaultableFields); + const expectedRule = { + ...ruleToExport, + required_fields: [ + { name: '@timestamp', type: 'date', ecs: true }, + { name: 'my-non-ecs-field', type: 'keyword', ecs: false }, + ], + }; + await securitySolutionApi.createRule({ body: ruleToExport }); const { body } = await securitySolutionApi @@ -69,7 +82,7 @@ export default ({ getService }: FtrProviderContext): void => { const exportedRule = JSON.parse(body.toString().split(/\n/)[0]); - expect(exportedRule).toMatchObject(defaultableFields); + expect(exportedRule).toMatchObject(expectedRule); }); it('should have export summary reflecting a number of rules', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/import_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/import_rules.ts index 71f40086a29f6..fb02b47067f8e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/import_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_import_export/basic_license_essentials_tier/import_rules.ts @@ -125,11 +125,25 @@ export default ({ getService }: FtrProviderContext): void => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [ + { name: '@timestamp', type: 'date' }, + { name: 'my-non-ecs-field', type: 'keyword' }, + ], }; + const ruleToImport = getCustomQueryRuleParams({ ...defaultableFields, rule_id: 'rule-1', }); + + const expectedRule = { + ...ruleToImport, + required_fields: [ + { name: '@timestamp', type: 'date', ecs: true }, + { name: 'my-non-ecs-field', type: 'keyword', ecs: false }, + ], + }; + const ndjson = combineToNdJson(ruleToImport); await securitySolutionApi @@ -143,7 +157,7 @@ export default ({ getService }: FtrProviderContext): void => { }) .expect(200); - expect(importedRule).toMatchObject(ruleToImport); + expect(importedRule).toMatchObject(expectedRule); }); it('should be able to import two rules', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts index bdbbc271c26e5..a2658ed2fb285 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts @@ -61,7 +61,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should patch defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const rulePatchProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -69,8 +69,14 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [{ name: '@timestamp', type: 'date' }], }); + const expectedRule = { + ...rulePatchProperties, + required_fields: [{ name: '@timestamp', type: 'date', ecs: true }], + }; + await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1' }), }); @@ -78,10 +84,7 @@ export default ({ getService }: FtrProviderContext) => { const { body: patchedRuleResponse } = await securitySolutionApi .patchRule({ body: { - rule_id: 'rule-1', - max_signals: expectedRule.max_signals, - setup: expectedRule.setup, - related_integrations: expectedRule.related_integrations, + ...rulePatchProperties, }, }) .expect(200); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules_bulk.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules_bulk.ts index 539c39061aa5f..a04245eac5517 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules_bulk.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules_bulk.ts @@ -60,7 +60,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should patch defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const rulePatchProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -68,8 +68,14 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [{ name: '@timestamp', type: 'date' }], }); + const expectedRule = { + ...rulePatchProperties, + required_fields: [{ name: '@timestamp', type: 'date', ecs: true }], + }; + await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1' }), }); @@ -78,10 +84,7 @@ export default ({ getService }: FtrProviderContext) => { .bulkPatchRules({ body: [ { - rule_id: 'rule-1', - max_signals: expectedRule.max_signals, - setup: expectedRule.setup, - related_integrations: expectedRule.related_integrations, + ...rulePatchProperties, }, ], }) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules.ts index ccf598a00da2e..33505f9d150d6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules.ts @@ -66,7 +66,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should update a rule with defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const ruleUpdateProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -74,15 +74,21 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [{ name: '@timestamp', type: 'date' }], }); + const expectedRule = { + ...ruleUpdateProperties, + required_fields: [{ name: '@timestamp', type: 'date', ecs: true }], + }; + await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1' }), }); const { body: updatedRuleResponse } = await securitySolutionApi .updateRule({ - body: expectedRule, + body: ruleUpdateProperties, }) .expect(200); @@ -273,6 +279,33 @@ export default ({ getService }: FtrProviderContext) => { ); }); }); + + describe('required_fields', () => { + it('should reset required fields field to default value on update when not present', async () => { + const expectedRule = getCustomQueryRuleParams({ + rule_id: 'required-fields-default-value-test', + required_fields: [], + }); + + await securitySolutionApi.createRule({ + body: getCustomQueryRuleParams({ + rule_id: 'required-fields-default-value-test', + required_fields: [{ name: 'host.name', type: 'keyword' }], + }), + }); + + const { body: updatedRuleResponse } = await securitySolutionApi + .updateRule({ + body: getCustomQueryRuleParams({ + rule_id: 'required-fields-default-value-test', + required_fields: undefined, + }), + }) + .expect(200); + + expect(updatedRuleResponse).toMatchObject(expectedRule); + }); + }); }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules_bulk.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules_bulk.ts index fc7c7229ef107..effc64a241cc5 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules_bulk.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_update/basic_license_essentials_tier/update_rules_bulk.ts @@ -65,7 +65,7 @@ export default ({ getService }: FtrProviderContext) => { }); it('should update a rule with defaultable fields', async () => { - const expectedRule = getCustomQueryRuleParams({ + const ruleUpdateProperties = getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 200, setup: '# some setup markdown', @@ -73,15 +73,21 @@ export default ({ getService }: FtrProviderContext) => { { package: 'package-a', version: '^1.2.3' }, { package: 'package-b', integration: 'integration-b', version: '~1.1.1' }, ], + required_fields: [{ name: '@timestamp', type: 'date' }], }); + const expectedRule = { + ...ruleUpdateProperties, + required_fields: [{ name: '@timestamp', type: 'date', ecs: true }], + }; + await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1' }), }); const { body: updatedRulesBulkResponse } = await securitySolutionApi .bulkUpdateRules({ - body: [expectedRule], + body: [ruleUpdateProperties], }) .expect(200); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts index 6bf3ea611dc7f..abf8bc3934e21 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/rule_creation/common_flows.cy.ts @@ -33,6 +33,8 @@ import { fillMaxSignals, fillNote, fillReferenceUrls, + // fillRelatedIntegrations, + // fillRequiredFields, fillRiskScore, fillRuleName, fillRuleTags, @@ -67,9 +69,13 @@ describe('Common rule creation flows', { tags: ['@ess', '@serverless'] }, () => it('Creates and enables a rule', function () { cy.log('Filling define section'); importSavedQuery(this.timelineId); - // The following step is flaky due to a recent EUI upgrade. - // Underlying EUI issue: https://github.com/elastic/eui/issues/7761 - // Issue to uncomment this once EUI fix is in place: https://github.com/elastic/kibana/issues/183485 + /* + The following steps are flaky due to a recent EUI upgrade. + + Underlying EUI issue: https://github.com/elastic/eui/issues/7761 + Issue to uncomment these once the EUI fix is in place: https://github.com/elastic/kibana/issues/183485 + */ + // fillRequiredFields(); // fillRelatedIntegrations(); cy.get(DEFINE_CONTINUE_BUTTON).click(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts index 84198ccd702dd..15229445e54f0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/prebuilt_rules/prebuilt_rules_preview.cy.ts @@ -137,8 +137,8 @@ describe('Detection rules, Prebuilt Rules Installation and Update workflow', () { package: 'windows', version: '^1.5.0' }, ], required_fields: [ - { ecs: true, name: 'event.type', type: 'keyword' }, - { ecs: true, name: 'file.extension', type: 'keyword' }, + { name: 'event.type', type: 'keyword' }, + { name: 'file.extension', type: 'keyword' }, ], timeline_id: '3e827bab-838a-469f-bd1e-5e19a2bff2fd', timeline_title: 'Alerts Involving a Single User Timeline', diff --git a/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts index d8feace6af1d0..b0af7275ae808 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts @@ -130,6 +130,9 @@ export const IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK = export const RELATED_INTEGRATION_COMBO_BOX_INPUT = '[data-test-subj="relatedIntegrationComboBox"] [data-test-subj="comboBoxSearchInput"]'; +export const REQUIRED_FIELD_COMBO_BOX_INPUT = + '[data-test-subj^="requiredFieldNameSelect"] [data-test-subj="comboBoxSearchInput"]'; + export const INDICATOR_MATCH_TYPE = '[data-test-subj="threatMatchRuleType"]'; export const INPUT = '[data-test-subj="input"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts index 7ee1811760480..a71d990ec31a9 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/create_new_rule.ts @@ -82,6 +82,7 @@ import { MITRE_TACTIC, QUERY_BAR, REFERENCE_URLS_INPUT, + REQUIRED_FIELD_COMBO_BOX_INPUT, RISK_MAPPING_OVERRIDE_OPTION, RISK_OVERRIDE, RULE_DESCRIPTION_INPUT, @@ -480,6 +481,18 @@ export const fillScheduleRuleAndContinue = (rule: RuleCreateProps) => { cy.get(SCHEDULE_CONTINUE_BUTTON).click({ force: true }); }; +export const fillRequiredFields = (): void => { + addRequiredField(); + addRequiredField(); +}; + +const addRequiredField = (): void => { + cy.contains('button', 'Add required field').should('be.enabled').click(); + + cy.get(REQUIRED_FIELD_COMBO_BOX_INPUT).last().should('be.enabled').click(); + cy.get(COMBO_BOX_OPTION).first().click(); +}; + /** * use default schedule options */ From b306f007fbcc723bffffd67b3867f6a563ffae2d Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Fri, 17 May 2024 13:39:52 +0200 Subject: [PATCH 43/71] Use spec-compliant URL parser to parse `next` URL parameter. (#183521) ## Summary The Node.js URL parser [has been deprecated](https://nodejs.org/api/url.html#urlparseurlstring-parsequerystring-slashesdenotehost) and we should start slowly switching to the [spec-compliant URL parser](https://url.spec.whatwg.org/#url-parsing) to parse URLs in Kibana starting from the `next` query string parameter. __Flaky Test Runner:__ [x65 for every added functional test](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6010) --- .github/CODEOWNERS | 4 + packages/kbn-std/src/is_internal_url.test.ts | 156 ++++++++++++++++++ packages/kbn-std/src/is_internal_url.ts | 40 +++-- .../login_selector/basic_functionality.ts | 38 +++++ .../tests/oidc/url_capture.ts | 38 +++++ .../tests/saml/url_capture.ts | 38 +++++ 6 files changed, 293 insertions(+), 21 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 726e9c2223d39..cddb6388566ee 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1278,6 +1278,10 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib /src/dev/eslint/security_eslint_rule_tests.ts @elastic/kibana-security /src/core/server/integration_tests/config/check_dynamic_config.test.ts @elastic/kibana-security /src/plugins/telemetry/server/config/telemetry_labels.ts @elastic/kibana-security +/packages/kbn-std/src/is_internal_url.test.ts @elastic/kibana-core @elastic/kibana-security +/packages/kbn-std/src/is_internal_url.ts @elastic/kibana-core @elastic/kibana-security +/packages/kbn-std/src/parse_next_url.test.ts @elastic/kibana-core @elastic/kibana-security +/packages/kbn-std/src/parse_next_url.ts @elastic/kibana-core @elastic/kibana-security /test/interactive_setup_api_integration/ @elastic/kibana-security /test/interactive_setup_functional/ @elastic/kibana-security /test/plugin_functional/test_suites/core_plugins/rendering.ts @elastic/kibana-security diff --git a/packages/kbn-std/src/is_internal_url.test.ts b/packages/kbn-std/src/is_internal_url.test.ts index cc1f5a32d7db4..24e043a72073c 100644 --- a/packages/kbn-std/src/is_internal_url.test.ts +++ b/packages/kbn-std/src/is_internal_url.test.ts @@ -61,6 +61,84 @@ describe('isInternalURL', () => { const href = `${basePath}/app/kibana/../../../management`; expect(isInternalURL(href, basePath)).toBe(false); }); + + it('should return `false` if absolute URL contains tabs or new lines', () => { + // These URLs can either be treated as internal or external depending on the presence of the special characters. + const getURLsWithCharInRelativeScheme = (char: string) => [ + `/${char}${basePath}app/kibana`, + `/${char}/${basePath}/app/kibana`, + `/${char}${basePath}/example.com`, + `/${char}/${basePath}/example.com`, + `/${char}${basePath}/example.org`, + `/${char}/${basePath}/example.org`, + `/${char}${basePath}/example.org:5601`, + `/${char}/${basePath}/example.org:5601`, + ]; + + // These URLs can either be treated as internal or external depending on the presence of the special characters + // AND spaces since these affect how URL's scheme is parsed. + const getURLsWithCharInScheme = (char: string) => [ + `htt${char}ps://example.com${basePath}`, + `htt${char}ps://example.org${basePath}`, + `htt${char}ps://example.org:5601${basePath}`, + `java${char}script:${basePath}alert(1)`, + `htt${char}p:/${basePath}/example.org:5601`, + `file${char}:/${basePath}/example.com`, + ]; + + // These URLs should always be recognized as external irrespective to the presence of the special characters or + // spaces since these affect URLs hostname. + const getURLsWithCharInHost = (char: string) => [ + `//${char}${basePath}/app/kibana`, + `//${char}/example.com${basePath}`, + `//${char}/example.org${basePath}`, + `//${char}/example.org:5601${basePath}`, + `https:/${char}/example.com${basePath}`, + // This URL is only valid if the `char` is a tab or newline character. + `https://example${char}.com${basePath}/path`, + ]; + + // Detection logic should treat URLs as if tab and newline characters weren't present. + for (const char of ['', '\t', '\n', '\r', '\t\n\r']) { + for (const url of [ + ...getURLsWithCharInRelativeScheme(char), + ...getURLsWithCharInScheme(char), + ...getURLsWithCharInHost(char), + ]) { + expect(isInternalURL(url)).toBe(false); + } + } + + // Characters that aren't tabs, spaces, or newline characters turn absolute scheme-relative URLs to relative URLs. + for (const char of ['1', 'a']) { + for (const url of getURLsWithCharInRelativeScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInScheme(char)) { + expect(isInternalURL(url)).toBe(false); + } + + for (const url of getURLsWithCharInHost(char)) { + expect(isInternalURL(url)).toBe(false); + } + } + + // Spaces aren't allowed in scheme definition and turn absolute URLs into relative URLs. + for (const char of [' ']) { + for (const url of getURLsWithCharInRelativeScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInHost(char)) { + expect(isInternalURL(url)).toBe(false); + } + } + }); }); describe('without basePath defined', () => { @@ -86,5 +164,83 @@ describe('isInternalURL', () => { const hrefWithThreeSlashes = `///app/kibana`; expect(isInternalURL(hrefWithThreeSlashes)).toBe(false); }); + + it('should properly handle tabs or newline characters in the URL', () => { + // These URLs can either be treated as internal or external depending on the presence of the special characters. + const getURLsWithCharInRelativeScheme = (char: string) => [ + `/${char}/app/kibana`, + `/${char}//app/kibana`, + `/${char}/example.com`, + `/${char}//example.com`, + `/${char}/example.org`, + `/${char}//example.org`, + `/${char}/example.org:5601`, + `/${char}//example.org:5601`, + ]; + + // These URLs can either be treated as internal or external depending on the presence of the special characters + // AND spaces since these affect how URL's scheme is parsed. + const getURLsWithCharInScheme = (char: string) => [ + `htt${char}ps://example.com`, + `htt${char}ps://example.org`, + `htt${char}ps://example.org:5601`, + `java${char}script:alert(1)`, + `htt${char}p://example.org:5601`, + `file${char}://example.com`, + ]; + + // These URLs should always be recognized as external irrespective to the presence of the special characters or + // spaces since these affect URLs hostname. + const getURLsWithCharInHost = (char: string) => [ + `//${char}/app/kibana`, + `//${char}/example.com`, + `//${char}/example.org`, + `//${char}/example.org:5601`, + `https:/${char}/example.com`, + // This URL is only valid if the `char` is a tab or newline character. + `https://example${char}.com/path`, + ]; + + // Detection logic should treat URLs as if tab and newline characters weren't present. + for (const char of ['', '\t', '\n', '\r', '\t\n\r']) { + for (const url of [ + ...getURLsWithCharInRelativeScheme(char), + ...getURLsWithCharInScheme(char), + ...getURLsWithCharInHost(char), + ]) { + expect(isInternalURL(url)).toBe(false); + } + } + + // Characters that aren't tabs, spaces, or newline characters turn absolute scheme-relative URLs to relative URLs. + for (const char of ['1', 'a']) { + for (const url of getURLsWithCharInRelativeScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInScheme(char)) { + expect(isInternalURL(url)).toBe(false); + } + + for (const url of getURLsWithCharInHost(char)) { + expect(isInternalURL(url)).toBe(false); + } + } + + // Spaces aren't allowed in scheme definition and turn absolute URLs into relative URLs. + for (const char of [' ']) { + for (const url of getURLsWithCharInRelativeScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInScheme(char)) { + expect(isInternalURL(url)).toBe(true); + } + + for (const url of getURLsWithCharInHost(char)) { + expect(isInternalURL(url)).toBe(false); + } + } + }); }); }); diff --git a/packages/kbn-std/src/is_internal_url.ts b/packages/kbn-std/src/is_internal_url.ts index 434fa6eaf7c27..d6251594aafc0 100644 --- a/packages/kbn-std/src/is_internal_url.ts +++ b/packages/kbn-std/src/is_internal_url.ts @@ -6,37 +6,35 @@ * Side Public License, v 1. */ -import { parse as parseUrl } from 'url'; - /** * Determine if url is outside of this Kibana install. */ export function isInternalURL(url: string, basePath = '') { - const { protocol, hostname, port, pathname } = parseUrl( - url, - false /* parseQueryString */, - true /* slashesDenoteHost */ - ); - - // We should explicitly compare `protocol`, `port` and `hostname` to null to make sure these are not - // detected in the URL at all. For example `hostname` can be empty string for Node URL parser, but - // browser (because of various bwc reasons) processes URL differently (e.g. `///abc.com` - for browser - // hostname is `abc.com`, but for Node hostname is an empty string i.e. everything between schema (`//`) - // and the first slash that belongs to path. - if (protocol !== null || hostname !== null || port !== null) { + // We use the WHATWG parser TWICE with completely different dummy base URLs to ensure that the parsed URL always + // inherits the origin of the base URL. This means that the specified URL isn't an absolute URL, or a scheme-relative + // URL (//), or a scheme-relative URL with an empty host (///). Browsers may process such URLs unexpectedly due to + // backward compatibility reasons (e.g., a browser may treat `///abc.com` as just `abc.com`). For more details, refer + // to https://url.spec.whatwg.org/#concept-basic-url-parser and https://url.spec.whatwg.org/#url-representation. + let normalizedURL: URL; + try { + for (const baseURL of ['http://example.org:5601', 'https://example.com']) { + normalizedURL = new URL(url, baseURL); + if (normalizedURL.origin !== baseURL) { + return false; + } + } + } catch { return false; } + // Now we need to normalize URL to make sure any relative path segments (`..`) cannot escape expected base path. if (basePath) { - // Now we need to normalize URL to make sure any relative path segments (`..`) cannot escape expected - // base path. We can rely on `URL` with a localhost to automatically "normalize" the URL. - const normalizedPathname = new URL(String(pathname), 'https://localhost').pathname; - return ( // Normalized pathname can add a leading slash, but we should also make sure it's included in - // the original URL too - pathname?.startsWith('/') && - (normalizedPathname === basePath || normalizedPathname.startsWith(`${basePath}/`)) + // the original URL too. We can safely use non-null assertion operator here since we know `normalizedURL` is + // always defined, otherwise we would have returned `false` already. + url.startsWith('/') && + (normalizedURL!.pathname === basePath || normalizedURL!.pathname.startsWith(`${basePath}/`)) ); } diff --git a/x-pack/test/security_functional/tests/login_selector/basic_functionality.ts b/x-pack/test/security_functional/tests/login_selector/basic_functionality.ts index 2c60d50650647..5e55f216eb800 100644 --- a/x-pack/test/security_functional/tests/login_selector/basic_functionality.ts +++ b/x-pack/test/security_functional/tests/login_selector/basic_functionality.ts @@ -65,6 +65,44 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(currentURL.pathname).to.eql('/app/management/security/users'); }); + it('can login with Login Form resetting target URL', async () => { + this.timeout(120000); + + for (const targetURL of [ + '///example.com', + '//example.com', + 'https://example.com', + + '/\t/example.com', + '/\n/example.com', + '/\r/example.com', + + '/\t//example.com', + '/\n//example.com', + '/\r//example.com', + + '//\t/example.com', + '//\n/example.com', + '//\r/example.com', + + 'ht\ttps://example.com', + 'ht\ntps://example.com', + 'ht\rtps://example.com', + ]) { + await browser.get( + `${deployment.getHostPort()}/login?next=${encodeURIComponent(targetURL)}` + ); + + await PageObjects.common.waitUntilUrlIncludes('next='); + await PageObjects.security.loginSelector.login('basic', 'basic1'); + // We need to make sure that both path and hash are respected. + const currentURL = parse(await browser.getCurrentUrl()); + + expect(currentURL.pathname).to.eql('/app/home'); + await PageObjects.security.forceLogout(); + } + }); + it('can login with SSO preserving original URL', async () => { await PageObjects.common.navigateToUrl('management', 'security/users', { ensureCurrentUrl: false, diff --git a/x-pack/test/security_functional/tests/oidc/url_capture.ts b/x-pack/test/security_functional/tests/oidc/url_capture.ts index 10ef8ff01c95e..6553ef193fc3b 100644 --- a/x-pack/test/security_functional/tests/oidc/url_capture.ts +++ b/x-pack/test/security_functional/tests/oidc/url_capture.ts @@ -63,5 +63,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const currentURL = parse(await browser.getCurrentUrl()); expect(currentURL.path).to.eql('/authentication/app?one=two'); }); + + it('resets invalid target URL', async () => { + this.timeout(120000); + + for (const targetURL of [ + '///example.com', + '//example.com', + 'https://example.com', + + '/\t/example.com', + '/\n/example.com', + '/\r/example.com', + + '/\t//example.com', + '/\n//example.com', + '/\r//example.com', + + '//\t/example.com', + '//\n/example.com', + '//\r/example.com', + + 'ht\ttps://example.com', + 'ht\ntps://example.com', + 'ht\rtps://example.com', + ]) { + await browser.get( + `${deployment.getHostPort()}/internal/security/capture-url?next=${encodeURIComponent( + targetURL + )}` + ); + + await find.byCssSelector('[data-test-subj="userMenuButton"]', 20000); + expect(parse(await browser.getCurrentUrl()).pathname).to.eql('/app/home'); + + await browser.get(deployment.getHostPort() + '/logout'); + await PageObjects.common.waitUntilUrlIncludes('logged_out'); + } + }); }); } diff --git a/x-pack/test/security_functional/tests/saml/url_capture.ts b/x-pack/test/security_functional/tests/saml/url_capture.ts index 1199255f96bf1..0193d3d870701 100644 --- a/x-pack/test/security_functional/tests/saml/url_capture.ts +++ b/x-pack/test/security_functional/tests/saml/url_capture.ts @@ -63,5 +63,43 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const currentURL = parse(await browser.getCurrentUrl()); expect(currentURL.path).to.eql('/authentication/app?one=two'); }); + + it('resets invalid target URL', async () => { + this.timeout(120000); + + for (const targetURL of [ + '///example.com', + '//example.com', + 'https://example.com', + + '/\t/example.com', + '/\n/example.com', + '/\r/example.com', + + '/\t//example.com', + '/\n//example.com', + '/\r//example.com', + + '//\t/example.com', + '//\n/example.com', + '//\r/example.com', + + 'ht\ttps://example.com', + 'ht\ntps://example.com', + 'ht\rtps://example.com', + ]) { + await browser.get( + `${deployment.getHostPort()}/internal/security/capture-url?next=${encodeURIComponent( + targetURL + )}` + ); + + await find.byCssSelector('[data-test-subj="userMenuButton"]', 20000); + expect(parse(await browser.getCurrentUrl()).pathname).to.eql('/app/home'); + + await browser.get(deployment.getHostPort() + '/logout'); + await PageObjects.common.waitUntilUrlIncludes('logged_out'); + } + }); }); } From f240808c6b620c60e8eecceb4c51d3444ab23481 Mon Sep 17 00:00:00 2001 From: Coen Warmer Date: Fri, 17 May 2024 14:40:19 +0200 Subject: [PATCH 44/71] [Observability AI Assistant] Add keyboard handler to open assistant, and add tooltip to icon (#183708) Resolves https://github.com/elastic/obs-ai-assistant-team/issues/151 Resolves https://github.com/elastic/obs-ai-assistant-team/issues/152 ## Summary This adds: * a hotkey to open the assistant with `control` + `;` which got lost in a refactor * a tooltip which labels the AI Assistant icon in the Kibana navigation https://github.com/elastic/kibana/assets/535564/4e2b635e-c5fd-48c7-b226-59f390508c92 --- .../public/components/nav_control/index.tsx | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx index cf8a5cd9dffe7..8757b9f8235bf 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/nav_control/index.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect, useRef, useState } from 'react'; import { AssistantAvatar, useAbortableAsync } from '@kbn/observability-ai-assistant-plugin/public'; -import { EuiButton, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiButton, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui'; import { css } from '@emotion/react'; import { v4 } from 'uuid'; import useObservable from 'react-use/lib/useObservable'; @@ -91,31 +91,46 @@ export function NavControl({}: {}) { } `; + useEffect(() => { + const keyboardListener = (event: KeyboardEvent) => { + if (event.ctrlKey && event.code === 'Semicolon') { + service.conversations.openNewConversation({ + messages: [], + }); + } + }; + + window.addEventListener('keypress', keyboardListener); + + return () => { + window.removeEventListener('keypress', keyboardListener); + }; + }, [service.conversations]); + if (!isVisible) { return null; } return ( <> - { - service.conversations.openNewConversation({ - messages: [], - }); - }} - color="primary" - size="s" - fullWidth={false} - minWidth={0} - > - {chatService.loading ? : } - + + { + service.conversations.openNewConversation({ + messages: [], + }); + }} + color="primary" + size="s" + fullWidth={false} + minWidth={0} + > + {chatService.loading ? : } + + {chatService.value ? ( ); } + +const buttonLabel = i18n.translate( + 'xpack.observabilityAiAssistant.navControl.openTheAIAssistantPopoverLabel', + { defaultMessage: 'Open the AI Assistant' } +); From 01cb16838d3a4fc47be51f629b4071c1794660e1 Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Fri, 17 May 2024 08:45:18 -0400 Subject: [PATCH 45/71] [Entity Analytics] Move scripted metric painless scripts to static file & remove category based weighting (#182038) ## Summary The scripted metric aggregation is being deprecated but an exception is being made for our team, moving the painless to static files allows for this exception to be made. We have had to remove category weighting to make the script less dynamic, weights weren't used anyway so not a breaking change. The scripts are loaded once when they are first used and then cached. A unit test verifies the content of the script hasnt changed. Here is a diff of the scripted metric before and after https://www.diffchecker.com/gefuBoYK/ --------- Co-authored-by: Mark Hopkin Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../api/entity_analytics/common/common.gen.ts | 32 +---- .../common/common.schema.yaml | 55 +------- .../common/risk_weights.schema.test.ts | 81 +----------- .../risk_score/calculate_risk_scores.ts | 106 +++------------ .../entity_analytics/risk_score/constants.ts | 5 - .../risk_score/painless/index.test.ts | 24 ++++ .../risk_score/painless/index.ts | 40 ++++++ .../painless/risk_scoring_combine.painless | 1 + .../painless/risk_scoring_init.painless | 1 + .../painless/risk_scoring_map.painless | 8 ++ .../painless/risk_scoring_reduce.painless | 38 ++++++ .../risk_score/risk_weights.test.ts | 123 ------------------ .../risk_score/risk_weights.ts | 103 --------------- .../risk_score/routes/preview.test.ts | 15 +-- .../risk_score_preview.ts | 50 ------- 15 files changed, 140 insertions(+), 542 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts index b037534248042..f17cb2a5ee0fb 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts @@ -155,8 +155,8 @@ export const RiskScoreWeightGlobalShared = z.object({ type: z.literal('global_identifier'), }); -export type RiskScoreWeightGlobal = z.infer; -export const RiskScoreWeightGlobal = z.union([ +export type RiskScoreWeight = z.infer; +export const RiskScoreWeight = z.union([ RiskScoreWeightGlobalShared.merge( z.object({ host: RiskScoreEntityIdentifierWeights, @@ -171,34 +171,6 @@ export const RiskScoreWeightGlobal = z.union([ ), ]); -export type RiskScoreWeightCategoryShared = z.infer; -export const RiskScoreWeightCategoryShared = z.object({ - type: z.literal('risk_category'), - value: RiskScoreCategories, -}); - -export type RiskScoreWeightCategory = z.infer; -export const RiskScoreWeightCategory = z.union([ - RiskScoreWeightCategoryShared.merge( - z.object({ - host: RiskScoreEntityIdentifierWeights, - user: RiskScoreEntityIdentifierWeights.optional(), - }) - ), - RiskScoreWeightCategoryShared.merge( - z.object({ - host: RiskScoreEntityIdentifierWeights.optional(), - user: RiskScoreEntityIdentifierWeights, - }) - ), -]); - -/** - * Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1'). - */ -export type RiskScoreWeight = z.infer; -export const RiskScoreWeight = z.union([RiskScoreWeightGlobal, RiskScoreWeightCategory]); - /** * A list of weights to be applied to the scoring calculation. */ diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml index 7b6634876d8a6..63aa739d2133d 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml @@ -201,7 +201,7 @@ components: enum: - global_identifier - RiskScoreWeightGlobal: + RiskScoreWeight: oneOf: - allOf: - $ref: '#/components/schemas/RiskScoreWeightGlobalShared' @@ -225,65 +225,12 @@ components: user: $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - RiskScoreWeightCategoryShared: - x-inline: true - type: object - required: - - type - - value - properties: - type: - type: string - enum: - - risk_category - value: - $ref: '#/components/schemas/RiskScoreCategories' - - RiskScoreWeightCategory: - oneOf: - - allOf: - - $ref: '#/components/schemas/RiskScoreWeightCategoryShared' - - type: object - required: - - host - properties: - host: - $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - user: - $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - - - allOf: - - $ref: '#/components/schemas/RiskScoreWeightCategoryShared' - - type: object - required: - - user - properties: - host: - $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - user: - $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' - - RiskScoreWeight: - description: "Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1')." - oneOf: - - $ref: '#/components/schemas/RiskScoreWeightGlobal' - - $ref: '#/components/schemas/RiskScoreWeightCategory' - example: - type: 'risk_category' - value: 'category_1' - host: 0.8 - user: 0.4 - RiskScoreWeights: description: 'A list of weights to be applied to the scoring calculation.' type: array items: $ref: '#/components/schemas/RiskScoreWeight' example: - - type: 'risk_category' - value: 'category_1' - host: 0.8 - user: 0.4 - type: 'global_identifier' host: 0.5 user: 0.1 diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts index 59b0859300f88..e4afc38badd24 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts @@ -59,9 +59,7 @@ describe('risk weight schema', () => { const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; expect(decoded.success).toBeFalsy(); - expect(stringifyZodError(decoded.error)).toEqual( - 'host: Required, user: Required, type: Invalid literal value, expected "risk_category", value: Invalid literal value, expected "category_1", host: Required, and 3 more' - ); + expect(stringifyZodError(decoded.error)).toContain('host: Required, user: Required'); }); it('allows a single host weight', () => { @@ -123,44 +121,10 @@ describe('risk weight schema', () => { expect(decoded.success).toBeFalsy(); expect(stringifyZodError(decoded.error)).toEqual( - 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier", value: Invalid literal value, expected "category_1", host: Required, and 1 more' + 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier"' ); }); - it('rejects if neither host nor user weight are specified', () => { - const payload = { type, value: RiskCategories.category_1 }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; - - expect(decoded.success).toBeFalsy(); - expect(stringifyZodError(decoded.error)).toEqual( - 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier", user: Required, host: Required, and 1 more' - ); - }); - - it('allows a single host weight', () => { - const payload = { type, value: RiskCategories.category_1, host: 0.1 }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual(payload); - }); - - it('allows a single user weight', () => { - const payload = { type, value: RiskCategories.category_1, user: 0.1 }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual(payload); - }); - - it('allows both a host and user weight', () => { - const payload = { type, value: RiskCategories.category_1, user: 0.1, host: 0.5 }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual(payload); - }); - it('rejects a weight outside of 0-1', () => { const payload = { type, value: RiskCategories.category_1, host: -5 }; const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; @@ -170,47 +134,6 @@ describe('risk weight schema', () => { `host: Number must be greater than or equal to 0` ); }); - - it('removes extra keys if specified', () => { - const payload = { - type, - value: RiskCategories.category_1, - host: 0.1, - extra: 'even more', - }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual({ type, value: RiskCategories.category_1, host: 0.1 }); - }); - - describe('allowed category values', () => { - it('allows the alerts type for a category', () => { - const payload = { - type, - value: RiskCategories.category_1, - host: 0.1, - }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; - - expect(decoded.success).toBeTruthy(); - expect(decoded.data).toEqual(payload); - }); - - it('rejects an unknown category value', () => { - const payload = { - type, - value: 'unknown', - host: 0.1, - }; - const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; - - expect(decoded.success).toBeFalsy(); - expect(stringifyZodError(decoded.error)).toContain( - 'type: Invalid literal value, expected "global_identifier", type: Invalid literal value, expected "global_identifier", user: Required, value: Invalid literal value, expected "category_1", value: Invalid literal value, expected "category_1", and 1 more' - ); - }); - }); }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts index 3c930ec07e666..9f99a9ae4a561 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts @@ -13,10 +13,7 @@ import type { import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import { ALERT_RISK_SCORE, - ALERT_RULE_NAME, - ALERT_UUID, ALERT_WORKFLOW_STATUS, - EVENT_KIND, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; import type { RiskScoresPreviewResponse } from '../../../../common/api/entity_analytics/risk_engine/preview_route.gen'; import type { @@ -28,6 +25,7 @@ import { type IdentifierType, getRiskLevel, RiskCategories, + RiskWeightTypes, } from '../../../../common/entity_analytics/risk_engine'; import { withSecuritySpan } from '../../../utils/with_security_span'; import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics'; @@ -38,24 +36,13 @@ import { normalize, } from '../asset_criticality/helpers'; import { getAfterKeyForIdentifierType, getFieldForIdentifier } from './helpers'; -import { - buildCategoryCountDeclarations, - buildCategoryAssignment, - buildCategoryScoreDeclarations, - buildWeightingOfScoreByCategory, - getGlobalWeightForIdentifierType, -} from './risk_weights'; import type { CalculateRiskScoreAggregations, CalculateScoresParams, RiskScoreBucket, } from '../types'; -import { - MAX_INPUTS_COUNT, - RISK_SCORING_INPUTS_COUNT_MAX, - RISK_SCORING_SUM_MAX, - RISK_SCORING_SUM_VALUE, -} from './constants'; +import { RISK_SCORING_SUM_MAX, RISK_SCORING_SUM_VALUE } from './constants'; +import { getPainlessScripts, type PainlessScripts } from './painless'; const formatForResponse = ({ bucket, @@ -118,67 +105,22 @@ const filterFromRange = (range: CalculateScoresParams['range']): QueryDslQueryCo range: { '@timestamp': { lt: range.end, gte: range.start } }, }); -const buildReduceScript = ({ - globalIdentifierTypeWeight, -}: { - globalIdentifierTypeWeight?: number; -}): string => { - return ` - Map results = new HashMap(); - List inputs = []; - for (state in states) { - inputs.addAll(state.inputs) - } - Collections.sort(inputs, (a, b) -> b.get('weighted_score').compareTo(a.get('weighted_score'))); - - double num_inputs_to_score = Math.min(inputs.length, params.max_risk_inputs_per_identity); - results['notes'] = []; - if (num_inputs_to_score == params.max_risk_inputs_per_identity) { - results['notes'].add('Number of risk inputs (' + inputs.length + ') exceeded the maximum allowed (' + params.max_risk_inputs_per_identity + ').'); - } - - ${buildCategoryScoreDeclarations()} - ${buildCategoryCountDeclarations()} - - double total_score = 0; - double current_score = 0; - List risk_inputs = []; - for (int i = 0; i < num_inputs_to_score; i++) { - current_score = inputs[i].weighted_score / Math.pow(i + 1, params.p); - - if (i < ${MAX_INPUTS_COUNT}) { - inputs[i]["contribution"] = 100 * current_score / params.risk_cap; - risk_inputs.add(inputs[i]); - } - - ${buildCategoryAssignment()} - total_score += current_score; - } - - ${globalIdentifierTypeWeight != null ? `total_score *= ${globalIdentifierTypeWeight};` : ''} - double score_norm = 100 * total_score / params.risk_cap; - results['score'] = total_score; - results['normalized_score'] = score_norm; - results['risk_inputs'] = risk_inputs; - - return results; - `; -}; - const buildIdentifierTypeAggregation = ({ afterKeys, identifierType, pageSize, weights, alertSampleSizePerShard, + scriptedMetricPainless, }: { afterKeys: AfterKeys; identifierType: IdentifierType; pageSize: number; weights?: RiskScoreWeights; alertSampleSizePerShard: number; + scriptedMetricPainless: PainlessScripts; }): AggregationsAggregationContainer => { - const globalIdentifierTypeWeight = getGlobalWeightForIdentifierType({ identifierType, weights }); + const globalIdentifierTypeWeight = getGlobalWeightForIdentifierType(identifierType, weights); const identifierField = getFieldForIdentifier(identifierType); return { @@ -204,33 +146,15 @@ const buildIdentifierTypeAggregation = ({ aggs: { risk_details: { scripted_metric: { - init_script: 'state.inputs = []', - map_script: ` - Map fields = new HashMap(); - String category = doc['${EVENT_KIND}'].value; - double score = doc['${ALERT_RISK_SCORE}'].value; - double weighted_score = 0.0; - - fields.put('time', doc['@timestamp'].value); - fields.put('rule_name', doc['${ALERT_RULE_NAME}'].value); - - fields.put('category', category); - fields.put('index', doc['_index'].value); - fields.put('id', doc['${ALERT_UUID}'].value); - fields.put('score', score); - - ${buildWeightingOfScoreByCategory({ userWeights: weights, identifierType })} - fields.put('weighted_score', weighted_score); - - state.inputs.add(fields); - `, - combine_script: 'return state;', + init_script: scriptedMetricPainless.init, + map_script: scriptedMetricPainless.map, + combine_script: scriptedMetricPainless.combine, params: { - max_risk_inputs_per_identity: RISK_SCORING_INPUTS_COUNT_MAX, p: RISK_SCORING_SUM_VALUE, risk_cap: RISK_SCORING_SUM_MAX, + global_identifier_type_weight: globalIdentifierTypeWeight || 1, }, - reduce_script: buildReduceScript({ globalIdentifierTypeWeight }), + reduce_script: scriptedMetricPainless.reduce, }, }, }, @@ -286,6 +210,12 @@ const processScores = async ({ }); }; +export const getGlobalWeightForIdentifierType = ( + identifierType: IdentifierType, + weights?: RiskScoreWeights +): number | undefined => + weights?.find((weight) => weight.type === RiskWeightTypes.global)?.[identifierType]; + export const calculateRiskScores = async ({ afterKeys: userAfterKeys, assetCriticalityService, @@ -307,6 +237,7 @@ export const calculateRiskScores = async ({ } & CalculateScoresParams): Promise => withSecuritySpan('calculateRiskScores', async () => { const now = new Date().toISOString(); + const scriptedMetricPainless = await getPainlessScripts(); const filter = [ filterFromRange(range), { bool: { must_not: { term: { [ALERT_WORKFLOW_STATUS]: 'closed' } } } }, @@ -345,6 +276,7 @@ export const calculateRiskScores = async ({ pageSize, weights, alertSampleSizePerShard, + scriptedMetricPainless, }); return aggs; }, {} as Record), diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts index 73f93d71b11f9..6a691eac42734 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts @@ -16,11 +16,6 @@ export const RISK_SCORING_SUM_VALUE = 1.5; */ export const RISK_SCORING_SUM_MAX = 261.2; -/** - * The risk scoring algorithm can only process a finite number of risk inputs per identity; this value represents the maximum number of inputs that will be processed. - */ -export const RISK_SCORING_INPUTS_COUNT_MAX = 999999; - /** * This value represents the maximum possible risk score after normalization. */ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts new file mode 100644 index 0000000000000..4c3b6f3c156b3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts @@ -0,0 +1,24 @@ +/* + * 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 { getPainlessScripts } from '.'; + +describe('getPainlessScripts', () => { + // to update snapshot run `yarn test:jest x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts -u` + test('Scripts should not have changed. If this change is intentional, ensure that Serverless scripted metric allowlists are updated', async () => { + const scripts = await getPainlessScripts(); + + expect(scripts).toMatchInlineSnapshot(` + Object { + "combine": "return state;", + "init": "state.inputs = []", + "map": "Map fields = new HashMap();fields.put('id', doc['kibana.alert.uuid'].value);fields.put('index', doc['_index'].value);fields.put('time', doc['@timestamp'].value);fields.put('rule_name', doc['kibana.alert.rule.name'].value);fields.put('category', doc['event.kind'].value);fields.put('score', doc['kibana.alert.risk_score'].value);state.inputs.add(fields); ", + "reduce": "Map results = new HashMap();results['notes'] = [];results['category_1_score'] = 0.0;results['category_1_count'] = 0;results['risk_inputs'] = [];results['score'] = 0.0;def inputs = states[0].inputs;for (int i = 0; i < inputs.length; i++) { double current_score = inputs[i].score / Math.pow(i + 1, params.p); if (i < 10) { inputs[i][\\"contribution\\"] = 100 * current_score / params.risk_cap; results['risk_inputs'].add(inputs[i]); } results['category_1_score'] += current_score; results['category_1_count'] += 1; results['score'] += current_score;}results['score'] *= params.global_identifier_type_weight;results['normalized_score'] = 100 * results['score'] / params.risk_cap;return results;", + } + `); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts new file mode 100644 index 0000000000000..896340262b21c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts @@ -0,0 +1,40 @@ +/* + * 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 fs from 'fs'; +import { flow } from 'lodash'; + +const PHASES = ['init', 'map', 'combine', 'reduce'] as const; + +type Phase = typeof PHASES[number]; +export type PainlessScripts = Record; + +const removeNewlines = (content: string) => content.replace(/\n/g, ''); +const condenseMultipleSpaces = (content: string) => content.replace(/\s+/g, ' '); +const removeComments = (content: string) => content.replace(/\/\/.*/g, ''); +const minifyContent = flow(removeComments, removeNewlines, condenseMultipleSpaces); + +const readScript = async (phase: Phase) => { + const content = await fs.promises.readFile(`${__dirname}/risk_scoring_${phase}.painless`, 'utf8'); + return minifyContent(content); +}; + +let cache: PainlessScripts | undefined; + +export const getPainlessScripts = async (): Promise => { + if (cache) { + return cache; + } + + const [init, map, combine, reduce] = await Promise.all(PHASES.map(readScript)); + + // The cache will only ever have one value, so we can safely update it + // un-atomicly without worrying about lost updates. + // eslint-disable-next-line require-atomic-updates + cache = { init, map, combine, reduce }; + return cache; +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless new file mode 100644 index 0000000000000..da2a75d569f18 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless @@ -0,0 +1 @@ +return state; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless new file mode 100644 index 0000000000000..5ee9376d701ad --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless @@ -0,0 +1 @@ +state.inputs = [] diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless new file mode 100644 index 0000000000000..3d79df51be0e8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless @@ -0,0 +1,8 @@ +Map fields = new HashMap(); +fields.put('id', doc['kibana.alert.uuid'].value); +fields.put('index', doc['_index'].value); +fields.put('time', doc['@timestamp'].value); +fields.put('rule_name', doc['kibana.alert.rule.name'].value); +fields.put('category', doc['event.kind'].value); +fields.put('score', doc['kibana.alert.risk_score'].value); +state.inputs.add(fields); \ No newline at end of file diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless new file mode 100644 index 0000000000000..88140b5ccd283 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless @@ -0,0 +1,38 @@ +Map results = new HashMap(); +results['notes'] = []; +results['category_1_score'] = 0.0; +results['category_1_count'] = 0; +results['risk_inputs'] = []; +results['score'] = 0.0; + +def inputs = states[0].inputs; + +// Currently the alerts index only has one shard so there will only be one state and we do not need to sort them +// If there are multiple shards we will need these lines +// List inputs = []; +// for (state in states) { +// inputs.addAll(state.inputs) +// } +// Collections.sort(inputs, (a, b) -> b.get('score').compareTo(a.get('score'))); + +for (int i = 0; i < inputs.length; i++) { + double current_score = inputs[i].score / Math.pow(i + 1, params.p); + + if (i < 10) { + inputs[i]["contribution"] = 100 * current_score / params.risk_cap; + results['risk_inputs'].add(inputs[i]); + } + +// every input is of type signal at the moment +// if (inputs[i].category == 'signal') { + results['category_1_score'] += current_score; + results['category_1_count'] += 1; +// } + + results['score'] += current_score; +} + +results['score'] *= params.global_identifier_type_weight; +results['normalized_score'] = 100 * results['score'] / params.risk_cap; + +return results; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts deleted file mode 100644 index 86bdc0d0e6be0..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts +++ /dev/null @@ -1,123 +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 { RiskWeightTypes, RiskCategories } from '../../../../common/entity_analytics/risk_engine'; -import { - buildCategoryAssignment, - buildCategoryWeights, - buildWeightingOfScoreByCategory, -} from './risk_weights'; - -describe('buildCategoryWeights', () => { - it('returns the default weights if nothing else is provided', () => { - const result = buildCategoryWeights(); - - expect(result).toEqual([ - { host: 1, type: RiskWeightTypes.riskCategory, user: 1, value: RiskCategories.category_1 }, - ]); - }); - - it('allows user weights to override defaults', () => { - const result = buildCategoryWeights([ - { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, - host: 0.1, - user: 0.2, - }, - ]); - - expect(result).toEqual([ - { - host: 0.1, - type: RiskWeightTypes.riskCategory, - user: 0.2, - value: RiskCategories.category_1, - }, - ]); - }); - - it('uses default category weights if unspecified in user-provided weight', () => { - const result = buildCategoryWeights([ - { type: RiskWeightTypes.riskCategory, value: RiskCategories.category_1, host: 0.1 }, - ]); - - expect(result).toEqual([ - { host: 0.1, type: RiskWeightTypes.riskCategory, user: 1, value: RiskCategories.category_1 }, - ]); - }); -}); - -describe('buildCategoryAssignment', () => { - it('builds the expected assignment statement', () => { - const result = buildCategoryAssignment(); - - expect(result).toMatchInlineSnapshot( - `"if (inputs[i].category == 'signal') { results['category_1_score'] += current_score; results['category_1_count'] += 1; }"` - ); - }); -}); - -describe('buildWeightingOfScoreByCategory', () => { - it('returns default weights if no user values provided', () => { - const result = buildWeightingOfScoreByCategory({ identifierType: 'user' }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` - ); - }); - - it('returns default weights if no weights provided', () => { - const result = buildWeightingOfScoreByCategory({ userWeights: [], identifierType: 'host' }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` - ); - }); - - it('returns default weights if only global weights provided', () => { - const result = buildWeightingOfScoreByCategory({ - userWeights: [{ type: RiskWeightTypes.global, host: 0.1 }], - identifierType: 'host', - }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` - ); - }); - - it('returns specified weight when a category weight is provided', () => { - const result = buildWeightingOfScoreByCategory({ - userWeights: [ - { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, - host: 0.1, - user: 0.2, - }, - ], - identifierType: 'host', - }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 0.1; } else { weighted_score = score; }"` - ); - }); - - it('returns a default weight when a category weight is provided but not the one being used', () => { - const result = buildWeightingOfScoreByCategory({ - userWeights: [ - { type: RiskWeightTypes.riskCategory, value: RiskCategories.category_1, host: 0.1 }, - ], - identifierType: 'user', - }); - - expect(result).toMatchInlineSnapshot( - `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts deleted file mode 100644 index d0c7486324e30..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts +++ /dev/null @@ -1,103 +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 { keyBy, merge } from 'lodash'; -import type { - RiskScoreWeight, - RiskScoreWeightCategory, - RiskScoreWeightGlobal, - RiskScoreWeights, -} from '../../../../common/api/entity_analytics/common'; -import type { IdentifierType } from '../../../../common/entity_analytics/risk_engine'; -import { RiskCategories, RiskWeightTypes } from '../../../../common/entity_analytics/risk_engine'; - -const RISK_CATEGORIES = Object.values(RiskCategories); - -const DEFAULT_CATEGORY_WEIGHTS: RiskScoreWeights = RISK_CATEGORIES.map((category) => ({ - type: RiskWeightTypes.riskCategory, - value: category, - host: 1, - user: 1, -})); - -/* - * This function and its use can be deleted once we've replaced our use of event.kind with a proper risk category field. - */ -const convertCategoryToEventKindValue = (category?: string): string | undefined => - category === 'category_1' ? 'signal' : category; - -const isGlobalIdentifierTypeWeight = (weight: RiskScoreWeight): weight is RiskScoreWeightGlobal => - weight.type === RiskWeightTypes.global; -const isRiskCategoryWeight = (weight: RiskScoreWeight): weight is RiskScoreWeightCategory => - weight.type === RiskWeightTypes.riskCategory; - -export const getGlobalWeightForIdentifierType = ({ - identifierType, - weights, -}: { - identifierType: IdentifierType; - weights?: RiskScoreWeights; -}): number | undefined => { - return weights?.find(isGlobalIdentifierTypeWeight)?.[identifierType]; -}; - -const getRiskCategoryWeights = (weights?: RiskScoreWeights): RiskScoreWeightCategory[] => - weights?.filter(isRiskCategoryWeight) ?? []; - -const getWeightForIdentifierType = ( - weight: RiskScoreWeight, - identifierType: IdentifierType -): number => { - const configuredWeight = weight[identifierType]; - return typeof configuredWeight === 'number' ? configuredWeight : 1; -}; - -export const buildCategoryScoreDeclarations = (): string => { - return RISK_CATEGORIES.map((riskCategory) => `results['${riskCategory}_score'] = 0.0;`).join(''); -}; - -export const buildCategoryCountDeclarations = (): string => { - return RISK_CATEGORIES.map((riskCategory) => `results['${riskCategory}_count'] = 0;`).join(''); -}; - -export const buildCategoryWeights = (userWeights?: RiskScoreWeights): RiskScoreWeightCategory[] => { - const categoryWeights = getRiskCategoryWeights(userWeights); - - return Object.values( - merge({}, keyBy(DEFAULT_CATEGORY_WEIGHTS, 'value'), keyBy(categoryWeights, 'value')) - ); -}; - -export const buildCategoryAssignment = (): string => { - return RISK_CATEGORIES.map( - (category) => - `if (inputs[i].category == '${convertCategoryToEventKindValue( - category - )}') { results['${category}_score'] += current_score; results['${category}_count'] += 1; }` - ).join(' else '); -}; - -export const buildWeightingOfScoreByCategory = ({ - userWeights, - identifierType, -}: { - userWeights?: RiskScoreWeights; - identifierType: IdentifierType; -}): string => { - const otherClause = `weighted_score = score;`; - const categoryWeights = buildCategoryWeights(userWeights); - - return categoryWeights - .map( - (weight) => - `if (category == '${convertCategoryToEventKindValue( - weight.value - )}') { weighted_score = score * ${getWeightForIdentifierType(weight, identifierType)}; }` - ) - .join(' else ') - .concat(` else { ${otherClause} }`); -}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts index a35f4978ebf2c..b5ff9c3487a07 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts @@ -8,10 +8,7 @@ import { loggerMock } from '@kbn/logging-mocks'; import { RISK_SCORE_PREVIEW_URL } from '../../../../../common/constants'; -import { - RiskCategories, - RiskWeightTypes, -} from '../../../../../common/entity_analytics/risk_engine'; +import { RiskWeightTypes } from '../../../../../common/entity_analytics/risk_engine'; import { serverMock, requestContextMock, @@ -171,8 +168,7 @@ describe('POST risk_engine/preview route', () => { const request = buildRequest({ weights: [ { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, + type: RiskWeightTypes.global, host: 0.1, user: 0.2, }, @@ -186,8 +182,7 @@ describe('POST risk_engine/preview route', () => { expect.objectContaining({ weights: [ { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, + type: RiskWeightTypes.global, host: 0.1, user: 0.2, }, @@ -200,8 +195,7 @@ describe('POST risk_engine/preview route', () => { const request = buildRequest({ weights: [ { - type: RiskWeightTypes.riskCategory, - value: RiskCategories.category_1, + type: RiskWeightTypes.global, host: 1.1, }, ], @@ -218,7 +212,6 @@ describe('POST risk_engine/preview route', () => { weights: [ { type: 'something new', - value: RiskCategories.category_1, host: 0.1, user: 0.2, }, diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts index 98ea7b172583c..28ebe8dae5f56 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts @@ -514,56 +514,6 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - context('with category weights', () => { - it('weights risk inputs from different categories according to the category weight', async () => { - const documentId = uuidv4(); - const userSignal = buildDocument( - { 'event.kind': 'signal', 'user.name': 'user-1' }, - documentId - ); - const hostSignal = buildDocument( - { 'event.kind': 'signal', 'host.name': 'host-1' }, - documentId - ); - await indexListOfDocuments(Array(50).fill(userSignal).concat(Array(50).fill(hostSignal))); - - await createAndSyncRuleAndAlerts({ - query: `id: ${documentId}`, - alerts: 100, - riskScore: 100, - }); - const { scores } = await previewRiskScores({ - body: { - weights: [{ type: 'risk_category', value: 'category_1', host: 0.4, user: 0.8 }], - }, - }); - - expect(sanitizeScores(scores.host!)).to.eql([ - { - calculated_level: 'Low', - calculated_score: 93.2375911647125, - calculated_score_norm: 35.695861854790394, - category_1_score: 35.69586185479039, - category_1_count: 50, - id_field: 'host.name', - id_value: 'host-1', - }, - ]); - - expect(sanitizeScores(scores.user!)).to.eql([ - { - calculated_level: 'High', - calculated_score: 186.475182329425, - calculated_score_norm: 71.39172370958079, - category_1_score: 71.39172370958077, - category_1_count: 50, - id_field: 'user.name', - id_value: 'user-1', - }, - ]); - }); - }); - describe('@skipInServerless with asset criticality data', () => { const assetCriticalityRoutes = assetCriticalityRouteHelpersFactory(supertest); From 4c56567ece34c93a152fbdbf2e425234d29c231c Mon Sep 17 00:00:00 2001 From: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Date: Fri, 17 May 2024 14:50:05 +0200 Subject: [PATCH 46/71] [DOCS] Update ESQL docs (#183617) Related PR: https://github.com/elastic/elasticsearch/pull/108715#issuecomment-2114866817 --- docs/concepts/esql.asciidoc | 31 +++++++++++++------ docs/concepts/images/esql-data-view-menu.png | Bin 78675 -> 84396 bytes docs/concepts/images/esql-in-app-help.png | Bin 370894 -> 585699 bytes docs/discover/try-esql.asciidoc | 21 +++++++------ 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/docs/concepts/esql.asciidoc b/docs/concepts/esql.asciidoc index 97f4b82586322..0390f9f6e2bc7 100644 --- a/docs/concepts/esql.asciidoc +++ b/docs/concepts/esql.asciidoc @@ -1,23 +1,36 @@ [[esql]] === {esql} -preview::["Do not use {esql} on production environments. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features."] +The Elasticsearch Query Language, {esql}, makes it faster and easier to explore your data. -The Elasticsearch Query Language, {esql}, has been created to make exploring your data faster and easier using the **Discover** application. From version 8.11 you can try this new feature, which is enabled by default. +{esql} is a piped language which allows you to chain together multiple commands to query your data. +Based on the query, Lens suggestions in Discover create a visualization of the query results. -[role="screenshot"] -image:images/esql-data-view-menu.png[An image of the Discover UI where users can access the {esql} feature, width=30%] +{esql} comes with its own dedicated {esql} Compute Engine for greater efficiency. With one query you can search, aggregate, calculate and perform data transformations without leaving **Discover**. Write your query directly in **Discover** or use the **Dev Tools** with the {ref}/esql-rest.html[{esql} API]. -This new piped language allows you to chain together multiple commands to query your data. Based on the query, Lens suggestions in Discover create a visualization of the query results. +Here's how to use {esql} in the data view selector in **Discover**: -{esql} comes with its own dedicated {esql} Compute Engine for greater efficiency. From one query you can search, aggregate, calculate and perform data transformations without leaving **Discover**. Write your query directly in **Discover** or use the **Dev Tools** with the {ref}/esql-rest.html[{esql} API]. +[role="screenshot"] +image:images/esql-data-view-menu.png[An image of the Discover UI where users can access the {esql} feature, width=30%, align="center"] {esql} also features in-app help, so you can get started faster and don't have to leave the application to check syntax. [role="screenshot"] image:images/esql-in-app-help.png[An image of the Discover UI where users can browse the in-app help] -For more detailed information about the {esql} language, refer to {ref}/esql-language.html[Learning {esql}]. +You can also use ES|QL queries to create panels on your dashboards, create enrich policies, and create alerting rules. + +For more detailed information about {esql} in Kibana, refer to {ref}/esql-kibana.html[Using {esql} in {kib}]. + +[NOTE] +==== +{esql} is enabled by default in {kib}. It can be +disabled using the `enableESQL` setting from the +{kibana-ref}/advanced-options.html[Advanced Settings]. + +This will hide the {esql} user interface from various applications. +However, users will be able to access existing {esql} artifacts like saved searches and visualizations. +==== [float] [[esql-observability]] @@ -35,6 +48,6 @@ Use {esql} to retrieve important information for investigation by using lookups. [[esql-whats-next]] ==== What's next? -Full documentation for this language is available in the {es} documentation, refer to {ref}/esql.html[{esql}]. +The main documentation for {esql} lives in the {ref}/esql.html[{es} docs]. -Alternatively, a short tutorial is available in the **Discover** section <>. \ No newline at end of file +We also have a short tutorial in the **Discover** docs: <>. \ No newline at end of file diff --git a/docs/concepts/images/esql-data-view-menu.png b/docs/concepts/images/esql-data-view-menu.png index fbbbdf44d315cf1f9c8a9182749861c19922fc6d..15e7365626ba8f01b869924693bad23e095a95f0 100644 GIT binary patch literal 84396 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 literal 78675 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

*Q(#IHx!pRza&9p`miIp@JA zuGcFU%Am}Y6N-kG6#W%);ShdMdx}CJ@~Vz{YC;|K+ZaOz1$8ghWOgZugN|&z_PGvVq9# z=+c<~mTce&*;GQmF7mO!0Pk&rD{v4%ho82(NeV`r#Xw+uv=&= z6rDtEcp!#ZFVetRI1qVOF^;5%-S#s!0th;6+<`(^Vo$>?HIEyTVt~1StawJ^+N{U> z>EYIDuF_Cf;MtE;zj-O~6$S*EL6rXlfu0Ws2DmUxG4_hg;Sq~Go>u3AERlJ0(GbV1*vw3BmU)uS0oBhfgtJZ)5X|>zote(D;F3dt9+3FWPC;dUPZ?>CvqZ!o|fEUf&Xg!*xbvdB5LMpq-p7{WQ%ZP8B4-L=U}z`g18VOcN=0T=0X#?#rZrfu zf$QESdW6b2G7yS-?ME~~&279L=KvVI6fjexu)TUPhCSfI8hYf(i_u)`6 zlIh$-v0Hq_*KINwCYrP!eRj|YFUl_0ayy*Rx0$&cLcinJ1GD|NNuhLACp%97Nw8B!ZTohEoJRNZ1e>A8=#cYYS{)PLg< zVQQ4cxlTCaroO(c9lbW}H%lV9QX4}d&gYgvHFx)Tb4Gr1FBk|OjNKEUM$9SK?SM#P z(hXLx&=*~(Eh6=4u0^Akw_f&96#vIYkX)dV2uloXA0+X+!g@ItwyWw$lgi7!HT>n7l%c)= z?E$(~H|FeNpKfo;GoQW>o`8}5i&Pf0QC}D=#F&21$8Xm;5}O;R%C|aQsIy3;dyK*% z)%s=xD-;8#fk?d(AKnHcGwJbhz66YA)cVu)s)PBNm6x8ySUJORxGZu%3O$WW5{dD2 zpl+qj5xPKk^>lJ!ZjQsd5A*wNW*z7}9LWPMT_?1dxXwYLpD0K_&$qvCd32#F)aWo| zrPpqM<@gi>l`ltFUK>COPw@`(-{9^iR(ri+W8iFp^q< zsHZ9yj#QytMArJpvf8r9dlFF%3ba6EGtM|_4ZKr!VOS8ZNYFU_09rQ|mNXkf7DK3k z2ot&2W&A8pCX_gS4ygv^03N3=h|lvPw5>B4p2x)+_tp4(#wIX?v)Q^V2;uhUPCz=h z(;Rdndk}xkYZ>KmLZoGDi%1KOO$pPucbdtVN5WOaB4CoIbMEclJm*1Z4vE_otP>Rl)%t8@%#n3-$<-u;ot zSKk#)Ugvb`PzEd!jt7yd`VC6lY0)oNWiW>YGyOq_?Y+6YBr4_FQDCGXwOx>COWr-D z)KB+u7jxa?VzIC^{4+zl>y4HwtnbkA-3W9H;H}XNxF}-5QH2K6kyXV!Y3$3QZNMJ` zV_`W!K?n*83PmssF?-+GRT}joW^T(ef3e=(KU=GhaX6e~!DF{%>sKm}w<&y=8&W_X z+yCKMcQm8su{`)VO*)CO=OD-x37?d7!2SFOAz$mWtR8vfYcrh6`f_givakpo5RU93 z^1YjM@y$aoNc3DD+h!E~byyOFUA9+E(a$1@5lf`1WP23__vG{kV1Va}iYH=m_e72uGG5F9pp;U3B_ zoWg)N!?0Vb%(dU+iG^>qi74yig4bYO3c{WT4j4GPSwuBZJ9(%pZq9l9Z29PBQKSrE z5kOle_Ou&CIG~`I?#xTXcgM1|8-Tqe=8v z-z$t8mcj#dTdsr}h(Fu~EcSVZDU1vubVQ&gRThdN?;{pN}iB&@d=kC)VM z(?Esu#%+Pfwc#v|GaaWFy#%_uU!@bm%|e>DotWunggF}%UIQmTfkmj=g{yF%ziZ9f zI+O*J+1ib144cUV+V;K;wHshFu8)_QZ5EvKK_k#UcNeCpWqJhXQ=7pVd&^)e( z(8~F51z!N(6VH~*!3;~4s_IHt4C#Brwa$QYw;c!uVm|lI70(!YuTLc8Hw<8NT}Zg( z7$X5p6$ATeldz6eWsNp_PEY*+%V8dgZVHE5`R%LLql2jKDkxl1g>_lQVkM>~RB2 zel6L}ONT?zf``v7W%J=rPpF1{!`!FWsh6p``{-buR(Jd#->u#>5OZGnDK;wlKDDj& zjc79)ZE--DlX4|vB6eo8nJiVRoj&)1R4Qj(84a_rW!D5?@V;RfRTmEv)({zl0*8WBZmSS@R(rxMd{K znSt%PR2dRrVyVMnhy}a@k!FdzX{lzoyzlCDsD+oznaj;6;R`(P^`l8Pm+uwEy}GH~ zZ?+Pa^drqbWxuYm{1g?&r+9jHGjBEL_dxZU-%GOm{imJjbCIIJee0j@?cGZ72%x~V zvjJo~TySj>e_{)rFyv=`6p~oEle=Ra%^LWEM!qOjJ23%(ZzDS3KVj zODU50=`{&Pwt8K^e4}25MDx^nI(Bu5#-yi1RN(iIXgXkAu2WjLFRnk@;38DG(&wO$ zL~#TWRN2E_li$6AVwLUPYdQX)e!^jOQWA(<^BK{^%WDQXP)7&H6xdLxUOe+WVFptK z`A;tpKok+d$2h(k?t^JRfXL#78a&8$sS$zQiZf=`XkhJ0u}XxS`*Hgl?v2%%QZkp6 zXV@3}97tx5Nvh5-lrF$WZYORb20^%UsU@XoSZdLkbw#nC>%?3QQWNbKcg0|=_jd8yGm>ozF5^9OYRUe)I}5jGpdDbaQQ zyLRqGSI_&KW{vhgsDSVsX3|8SDjH_>N1Nw5;|ZNy9~h0&QY_84^ewQs?3NFx!~&;N zIkzLpEXE7zJR~G9^x6=yT<5W>#R8!b@HS6>SaX+A4sJ>=V5q@^3C(EdU51r4)Yg+4b%xkOq54_e)8V()Ct1E8?6n)z*S;BGnG49i6 zp5*;k8BTd&VIPjs)>}^Pk2ZAOhf4?ZMq{8cC=F!YlPd2|spl_+r ze7+eFSgeC=R*WE=J=>i>FQ~Y2gS38*y=4D<#9^IKTHzv5%;~WPOhjH-WvrKGJ1%i@f%cgH=Rv53zAQW)HG!>$5eed7b@%C3V*kOP*$Okkk z?O9|~>BAU!YOPR!UfD^Vz!@9f?up!f_xS)HUQ#2mMgB+tin(Wl@|$~;)^}2#rAh}@ z~N8qe~zKKp2-Cg zW&C59yA%-&$4^}$yFP=*amOPC#UIv~b!(kpn$I+7vaY6(h~C2Ty)+$+?Nw~B z)%DQ9vzRVse>juO-Up?LB--y_h%gM`aXUhPFH-&$Mq5l_H=}9Pnv<@6Z=hAAczAd) zn5$f8cG{5@FURUd8GU-Z7rwtbY9bNRHQXD|VKyH($UN%z9z0%bKy7wC>J!HvYE~^) z2>=w=-taw0|La+^Mw=)5#o>HJ1c_KX*9Kq6OG3!opWB$4w!DB^eQNrI_m$Iqnz{?c z3#|Jk)JMOm{HOJNHTG~Sdqft-vIdDZx;wSQ_bZ0v&AdQioqwwAYc=pjg>YZ$P+AY!#_QYs~{T z7)_c~9dzPgQC@YI>$X@oi*=2qbJyfp&u4kG^9zTh9AE}RHaRW&9xoz`kUMM-nY>No zu!+IIVv2L`_!5u|uC-Rglf*TPx&BHyql!tiz3;~bV6&W*cd*=w?AX3L8)uWKAoB5Tm`qge(Ao2s>;{ocR z=X*y1vg3gx1Vp+Clvg3?w^LoW$_;Ceyi$>&9ycDDkv{qo*VIFb4dS#nMc#}iIL_A0 z6ZvIwq%sff#RWXysFsWS%+qRX#EwiUv%%uV*ZDO9RS(%< zw04vn@xC!nT~pmvJfmL^Fy+}#TzkcZi7Se7O zSP9=AsZy_F;(n*%DA!>&%khDUf_AZZ6PHmZ5O^yKYm!mJI*eBl1);HSrI0U;UMrqg zW;0y#u)N$emJy{JrWt10DTl^?c~A!Gwgm@N3X``BOezR^}9(h z>HD@%1sLG(bk9&KceHyFzsjw= z)J+|Jr3BR-9JfN&FROqCrkQ@nMMY;zb~$1xp2fwX01u>&FOz+QV*-pm1X5PRqDzD} z%~)!Yg#shSHnOkkA#HWNNQy0pkg&!e#uG>aC!XCt>j;_r-eZ+g4IcG*b( zrWy|2vG8EJ@ZEd6H6$dw&pNaNogc8 z)U^IMCcPe4_!C?>w=ZB;y;QA#>Y{^zFdwQ|y<+|JuKU0U$mlL%gpVLFNN0G=?A;U~GgfC)}+5-7s}XH!N_=6y3q#EW<8zJJ`CAYYOW0#uXdp;Jpm%{vL*s_Fgy~$oI*jAO}0l(w%yd>QWL=)>g&FT#*X!Z5{-4U z8=D)UGG%3=f7DdahTt`52a=gxxv(Cqu(k&2Y`p1H1-~f)?!`mO{B_FgVS|j$Tov_r zan-;xsTtAUTplNzoet~38?`b$IL~W>KKji0_IHuks6=y6P@Zn41S$%FwFlqpP}wYw zA2|JDLgYc-?_x+f+6ZkVa%E7k&vd&D6nN$ygQBruDT+F&`WB{5X^!psmVC#py zQ9r(Ak&s-gDMUukV~DohuDVB3UC5+a2CkW9WqR;U6>f{fN1`#kJ6;1^yPlBCY?*?q z)a^!()r>H>ptr)h`vn_<-N_RjgZD4Z^B~Jq=a9^++)s{=uEKRkiTO?WfUID6gL(_W zyx$s{^Wpc8NjbqNwY7#!vMC&K{L%UY5fhhTTBQaR83U4Zt)d|x4VWM;I|`MHj68&7 zvfA#D*W_w}x(?7kK=n|lS3`8dDj9Xi?3P#h*!p)YfGcE{FC>zD*HMJL9#83-tjR7* zhmM4~2MnCfEE~XCu>Dl=33qqetJTt)v~Jhxm$E7|efk#rL{SoPH)2*E z)J?;Be^IniRDq;xVTJ>e7+ydyV-#HT@yTL)`7m>9ru&#_%7`y98jGbfXJDYc!?=A9 z?Wn!|T4$TY&6MnOd(-fetlE`{k+iLq<*#xh1s4py`|-V7mi;#@JTpH}1C3chmLa*A zYohs>{)FeRq4?9~E$D`%qMF3ws43M2TLg4=tF0fr8i<7xJjF?E^1(xv%w`*~T-Dcn zV@Th`r0P=4$;qv*pN`;ePCCVH*+toe4qllfh(hO)K$SVMov3_j=ntp6GLbC|NVerILHPCgzwN|rampLW$aLEpX9p``xrpT&{(CUSZ2V_RW(+!7z4O~*1b zrg>>+-ywlsv0Fj|FD2iyWdOva@9u~^0@MEd>F2hLECrsttsRq*N=$&Q7m)HKui!^? z1p=|*<-E0K*Mk||`m1*(Y7C4wRc^Mdw?ph*2XGY*&Jgc$UL`W>_Kmja9P7!%7+&3p zS#h}AgjKp6sF^xQ+_j9?+h~u9#fOnME4`~g(E8kUj{+60F<$*PM<>Bst^v#GTZ?{22XC7`B;0I+HJ$kG@qvyZ#}eC0aO%Lo@{0BoIUDA^e=r>k8Q~ zPT01S%-yCpn)rqvk-^A$Z=9&wbYw^qm5@jFNjv-q&=$3&lOYzvC2bgdnqn@mjgS%N za%LGl>X-WK9um;_a=h^$+!2f%A-3DJqusq8TJ1&z(#S#T=12v00sW|yoa!CUMbI7* z;Mg%Q`&mu*^Sie6zJa0J=b^0aQF`zi-}py-SZ6QnUI2Um?es`qTSW{KF(kxW_oz&z$xxTV z|7cHehl8Ml!T*5|*FYlLNGLWDqGmo@1Xp`A?J9b_aa*I<;R=)XV=lTnn&YUYtD@}4 zu6>?uCIzStgj9O3zXj)De$8~x_ zBDJnw9;k_6>~w1qjw4!}VVHQhiqxM}8a}?6*9xUOC_7#?Us!~}mnb#HGr>&1{CGST zOY*)f3R@(|L|eWKs7K6~x~!)cWv`*qHti?8MekXR=a%Ne1ZR6;)eZ&Zn5G;mw$)xp z&cDmjxH_A{HJ>yq(kZiP6H%m4f5UA91J}eFhh7)coj{S2FLVk$m}_=vhmgJp__ff<8~rwOX>TZ z`3Mv;_~xSo2ZSq5L_6{eooU%HQ4Z$|-+T`e0wep5hJ@EK=H1(Fr#N~7GGcjjnwnUB zwk)|^F5Xm3xj7rZO`$@M);^0)BryhFw=OHDsCn%Bz3|iUql5x?8MP6(M|-~-`d$KE z6nI6wwfy@;2t-^)GP}@{Ws;lwd(qEglemOSH{YxEs+{+g3e_tFPSma9)M*EVB9P!- z9`2@n9twE4oYh|{d$FTxSS~YDdaJF8oWbJ~G8jjF-WrUATiIMsMp}#PUk*2#B{+6a zxNA6-681F^1__u2&2NQcz=bo1eOZ>39RY>B_Yzv1o-BF02&f(1Cw*OQ5Bl7KR9o#2 zv3e>T01vzLy>G;3iwKRx#@?tMNElQZOD3~fkePm4@B2tFjHM&28DC{NEp1ws-K=BF zWVChnRUnna2KIb+Y;0DvCvj;Kr{Nh+I;kkSCK%DhkWmH6f%sH5fyhJN)DB(n_Z@+uHr{&gE?! z6~kC})7`xcq>>4t+t-gkpk}~kx56iY)ha+OpKd^2>3R7BJDy2j7)NC|nT1fR!PbV= zIgcw(r^#`6dE!kd7HzB!-1H+hotjZ%z!y$Dz*w1{xZQ%oLmLo5SjsWEH%;ML6?hkj zpr2Sn@Exvk0&vmCEiY=QOLvcus>Tke4xI?zPrj0zzU$4_LFr#5Zsy1!YY}sz8d#&g zxk5z*dDbTPDI>&v4aiaUeTM_*wG;{zGk>@+JI}opkjNw11d<`+ZDxL49zWTbB3`kF zTa!YqzD#-E9X80q&4~F&H_j;^WIM%xP%}+Fo`53;7L5wlX+w9dRIcHOrIdTbwxoit zv61Wk{7LSIC&da=?sdQOTk`T1OspXUM$#&qRpO%IoaIoP zFiJ>##CwSk`Vz;v=5S8LdB$RJ+ssbquJgcZdI+u2Y)z@~l7I(o2y{)g5nSN!|H6Es zG>@KMP8?DW!T% zL2Y2%8+v8_DD4JF2Dw_>OQeUph@&U}=5|n!VN+tNlpI8e02=R-eV-X%m()=vEu*ZQR5aQsPY0G(Kyp=ExqSdKbzWzt`0%o9&ZT6Kp3O5g@5+E+DhfVS2UnghgX9 z+JSKlWrGa2lNI5uf!JUSEMJ{wXY`tF|L#v-*T-khKn8~dNIUqIz`cC^mE$6hDYWrc z3wTLWQ=951d{)x|9O1*V^-tuI$!VxkY$`b-9#q!wOR{X=07ptuYgH(T*t`t7=L71A zi50}q!Rf&)69^XYDuZ9q%qf1LRnC8*XNPyFsqn6W1xWD*W3%x_K$I!10VU;P)9s;z zVNXdLy|GZP0M7BV=-8q}7o5Y5-UwW@h+ERWY|4SkyE|vqm@2PpTlf1bhTTvc(%L}B zBjV~0pUyTH!&hn-l1Es{1{C#h-B)(UvV;NC@g5n1;bBdO+5B+?&4_}&fqHAc-i9Ea z;ht4(q84zse0K>;o)CaqI8b1AI#3PWzB3WTo0jwpRr)MqTR7E9QQ-3QT`rkTrCM6K zN{zHor6Dx@vrQ~Cer#}Lu4J@m0VBT&gv70*Nw)EanNueZDOn)STRaF+`g@{QgcU*` z6D*s~0~e0XM9H*YqRy0~5zvXd#bLdSDiuq$x$Re>h1dgox#8e!rqrTm+(am0tJ8tV z{zhh11__tprLyT;CbT&fN0xP~Kx9(1%4?2x$2bj=8g=z}pgS|Bk{@xVi&mwIFyIjj z0aS_!CNbIe$(4*ZtZjcx_^!SI7=aS?iZ0U~9w-KmNFw&vN)!r2;8RR0xnS{#FLk%q zSqy085^GLllO}YI>XhaahV1vUif~xY^u~c7fhtf}bZ@x*L{@baFEsx3iJ-sh5pPey zSl0Zxo#6KV3m7bhRL)N@)SuDB5oy#qPfrBX=3nQToqyKx__~`)DV?yZIbEU};OleQ zL829*x!>r!NoB_yj-V*bLK@EVn#UO$fIA~IEBWP#$Z}sf?@xA5_6iFoO}Io&mWtA3+0iW*8KrL}{R z_|h){Z?2Q2iEId-!3cTF_#&jAV`vSC&uhyA)Hp7Jo-cJ?G?eqNseVwfLO8?howN?2 z`nD@K|MgpZ8dv6<0U>meT!{d|pLO&Q=JG1!Qv)FlN-;UEnmLDeodHlw(y%)@UO^U9 z`M-)tS@XD}&M<4cyZ!C00tw4Y@r=45vr`zpG%)zT5(*2nP8zSTShNnm=~y4a8EGSYz)VEpk@FAlD{i z(#Bpi%>?c0A2c$Coi8+XdYx^*!V6?Sf>+dr^nGeOG7)pl9OiUqDN zruh4m=lgnzyMW~PiK73#Un3{eALyZpN^gY)Qbi4#v$h#p)hNYfe0Ily38$jvt$YIB z_I90|VJwII&M!Y(wjNFt+L~~=U13!?JsdLl)@~33DvD)A=TN&_EZp>t{st(=81S1t zhPRjUcs2(kE8rNfy>d?@5!KP(xI>?>5ec)|8%ljnAE9eAQAeA`d>$BFWwi6_}H8W;#+af{VZ z??mXd)`eJL5z)Wr$joL|nw|J&`dpl@kOZQb_|)#Db9P)H*K164GaI&Ev1*tQM6R|w z!4<0}Wme$QDursdE6#SYY2n(=H3lC_NH7eHTjq{B)tUlH4};@jx1UrcYV;v0MM^^> zK#~lWrn%GqAo8FL*>qNWK53;9aF~8v$3&vg6On6*%<}4+UD^}li+%*nr>oZ0F8$V6 zjpqw=evDbpVS+>Ji3NQ^;&30<){3<4e{T4+Y(zf0Fv1nZO562>*SkAY4BQNh5r6cQ z(wC^Ry}t|2Q5@|Qe3`sUL$h1#>h_ub91Cb{>)V}szo>&NK&ZHDW`;VUXN%${i4<{bRo8 zf-#YD-0yB2+|w2sc4!9%~Y2RRhTH&$$@kOYQ7Q~qV`}KKYRj<{*oQY zmGrph=d4+kNBiK|lzTrDgsUuBa#u`Ef&oxrSq-$FYEa43Cjs`r6ZkCxu}{7=0B|}L zanRrRc5hEc;L2<+h4FTEjIJyn)9PxU?#VeipVqHN(95_#DpELjJV_O(HYuogH;i2R zTn2v3_Msn}pg(5NL^(j>M+F^KPgpvX)9%&do{a+lnyz-#m*bQ`mi7v-8~8Xp=)u)b z-YB;78(kXWhi4uLaSWQ;3?JDo>G3#zypyB0G{ME|Vsh?tRI23J=X~8yAfG{AW|>SY z=TExC_sMUk2?+awq?KIG01USDj6M!ap<0dcGSo+txUix0#sV$q*?@Fz3=|iKJx`j~ zA8|jJiqU7!)jRcSj7L0?kf*tOhC<9D@I@tp2irA$A6odmeL=$3hN$3X07^FULtbwb z3X>cnu_T42W=5nq`j>IkKJX_#FCS7#lIo}dIX~820eD4Km0e8diBu}1pBPztX!i%Y z7Gf+$cY@Zpbr&q+sa5(keh7K#w2}OI1g|)AftM@%F#sc4*?yTOz1r%G763}%?MWI? zA&cg|+Ube}!(`RP!1K`gPzb_}oAByY%*eF8tVwq*3F-M^zwBskN-uP@>6G!d#g_aX z#j68KK{!;|a2kqoAo#&Q1z*?*Td({x*{5d|qL--DcVo(dZPd5-h|H5OAMM{>x}D(& zih^a*+MDCu4t|53C9lk;_G&W`90$ONaAv; zMzlD=Y`Zy%Pc0KW=XicLpCViDXq8%IYxu2HUy@oDv7D%x-t|f>oD}*?V$FQaS{nx< z3xJI^Qj1K180^Nsf5^jgB~!ATQKcLmg@g@4hgR*H$Iw6mbSZ@?evM;f@4Kv|C7T+G z{3{4UNP!XhLJB><(4X=&Cga6?;}(CwRloJe41a`Pf`Sa$;L zr({$H)I-DvqNAB8rVQpx^^4INq8p|y=Bc7iw+lgppjBcSDbzb;Ae6dgnthKmD1qLE z2^oKin#EkB293g5Qj>#aO&m4_Zv2NC09SkEoGBJ*JVl06vun@-S(hTxS^*23Q1UNHjrJM?&wCZkQOwXIo+ z*|b6uhL)%A+Y3D%a~P_K-J8>K!-{O$=_^f!e0v0Kdf(~VIcWsZGT@NV%jE#Ur2EbIHXPRRe%30&ok zu$7`!!sJ4i+T(=e#`uNu4;Bx_BU+xwI4~g6Fcssr!`WJzv$$mTVwNoKZs?$>-8Tw# z3HeuxweymFbCgQAL=?81d%Sdg?>x^Bm`&OHZn2vOX-^V?gShz=cdof$+QG3=CBiFV z-PrNAwPRcPhF~(vzZ7fcSPatp+jD;!G@u?FxV0Ms4#ao>OpQE}D~AKa%`Ey1WH# zW_$-HOiQJDB1L|dN1JYQ=KCLUA~=-Q`k&9<4Zc%iazkdMm#1YMHNM0Oclk`Jd%mwd zRHVs2QDs3g{fdEl@ZC~7;`W0*X=@dkb`WG}06^*8b(@-5^pD)Vi4uzR2|-@~S7%xD z(ffw-g6f&UE8G`sVh+dv|3*uvnpJJLMg38ntVZcIDmf(#e$P=>1?-Ht7&}qxoA;Lw zJj(8!o`Z!JXaiU(#I#HeMKaMs8En*aPYB^S26e6Lb1R-lGj|Iopvm7eMI5C@+}ri+8Q}(e*Hb%G6vP_rBk*hyAX1YQyCL^U3ZQ;FilDSXQ~qSE?Zp z4U0;x3wv}zSOt+OFOyPbH;uYJZGk?xJDm$Znx`SJ{zCe$!Vy0tjVU2_%o~M%{1q)7 zh5}#g6LnPGE~%xpWIi+Ul|jS0IC`{Q)%KVZg|m#uQr;l~FL#S;F(&GkCF2A{?nx96u={y2ozNamJgSsyQtmy$R14pDUuhop0tn3qEX?3XEjw<2=&W-Z<~tj?(pcy z5y1ROJxZSN>;iw2zsY5$0Up&F(2opd2#fJ)T#LOk&>XNuXD(eVn28pNgJm~Y`|etn z^=`y~$>Y}5eVkT~TKMYJ%VL&p7QQcq8$R*5367raU@EV!rZm+-$oP+3Wn}zweOgz? zce?nq4Chyp9$911uWOqv#9FSz+ zKot}fXhs&mW!+>lpW!e_NrxY_uViMJBBYS)u`KJpEYVQCSS3ma{GQuK{O%|R%aRrc>8l^yu1CO_1sH z=IKp^eT1F*V^Guc#*(*Z%PG?@1f*pH@Snxs1Tom!evL0ghDq+@u7h3 zl8R56*d|0gj-Dm7T5@5C+56ctX?*)lcQ*!dUE4S2Ba>CW);_MFO~|Cw5P)-8DCjIDGWq z^|^COFVKQBQTp!b@eVEThfi+`*iZ=AIt@ZCK;A2KOWyzOe6Aml6S6?Axtm!}KPBj+ z@_Lnw__RHzV$OMYW=5e%+ft>wIn)lNpH5E>%_p}(D#XhN#4s+vRYQh_l?8nkWp^Y( zBZrejDv20oGgc_?KqS~eqgGCtrtFxr|tj;)BJ3~iywuU z&zo2GLu%54`&4*b*=!mUeRngitL(aG)7J2qT}h_jSX;3KyU{2SgGRK*3ZJ!6`1SPN zKuVo#g)^}RZh>xk42laR_$M8*Epe=u)ag*~^z+mT3Aa0xN@dh3;^z1#MBZA55?V!S zxCi(H`R0;?3DH24zvnxL*3KMCBy*H#gM|N3KtJ-N76Jc!MQSijS6mc! zdJ8j~XG(p!)sC;+c;)Pr#A+0YFkRX}m#{=cOYBHKx^`C!J(EMlF+6rrSfqNda8SD! z+GUI)N)Q!SxY}dG7{*u(0)-x`T;`=$5ngt(YE~z)<;o!&u@y(c2lelmdfG~C9JFWZ zdC3tY$webz3F0}^rDz$xMbH-qD5Lf?&JcGInw^}MsU>@@3Ec^dNg7u4?M2jeS;*hr z1B;x5vp{XcXKkoymV_l}w{1(f_En`OdUR&+ecf(-(rhxPx*Rd%i5${%wn7ewt=Qe= z8~b%dtkNG*GdB6jgKARwA8z@MuL~eQevN<2S2ZaP0^|h3;GKFxkVJk4iti}FI8T@K zt1;Yu^u4QOPKnA*xyhR0sjT`+b193&!elX&F31Ex%dh*wUvA;WTirF1P`->7FD%n) zfin8`kyb|Wi$Z9EwJKGd_6qEgjApL1`b4cA{h-)lR|{=2W2L369fd zDp6d{JYUDHLLaHGjQsjo4soKw1b5FEKzN7Jy(sHtPkHrwuy<>rj{vlwWybnAD!Bj9 z_C2Yv*+W=(myK^!jgwEGxUD}##joKz;2Hb!-LvT~c1Gf@b|F;+ZIF62I6_@K6^Ms; z`$4qD`c#Q#i!c-7fxLt}MW6aFr2bO0y(fm%xIbX%Ei=`prf%+sr~Cr^fJadV}TI@|S$9Z6_9f%}Se zm(lxdf*&L--jjGdL9M?1v*0#YvPpue=NLu`9R~+M0)5R)MnX2?QRI)p-?#GvhyX_; zv+m=T%qB{k6gOuFpISTP36!hb8+h3)1z}JKeBi<1mHEKXL(G&b9p*1&(?S`HP#Nj+ z$=3Iw+84a{X*$h$oeoLC2DtwCik3Xfi^CV^HmA1L4vNk>!ylbY)<-MC^}l<83?O=l z2Ba6d9zwi|#Tha^go=J<3U^owZ8thmIm}~c%DNVpNoi_vRu{wjQSeiIUsaWOZF|HvGxG!C2H zEIKtK=%{>eB8In%r|*L#?%;cn@D3us$kPL$HTQTfr+T#?PcpIP(*455=;MlU$G!R3 zTh{RPd*x&6K(9pfnuC7-inOv`OKS(yyF=F0=avE_cwDA1;p`UUcvBT-+4()o3i%EZ z(suWqKEjTI#jPV=#bI7oZ8Y5oyIMcdg^st+U&S*qgnv+rg_zMMtZ(Qvi^9)5&k*(z zil7k2JgqEfk8NN=GAx`~Ykh#NA23v(KuTOjpG99EoRAmkjNc18! ziVYBYJ&{3X@vgqQ@Qke)>T4nx*Wa=+SDs?7lk}^?;GaW~YBpY*wjJ}NN&cAtndwX9 z;lvS)Lj~nwS%a3(@?RFutpa6$F(9?uZ0>)1UB5nFKrfwhA8XKjHX6?4)S3i^ zDl<02v2mi}Td>WfZkH%{s~G@)^h%ce;uF8aE)hTvtJ@EfZGtxW7a|wKNP?oEq^}K9 zPxbsWtRzh7{8v#4s6#RBK@aJ*0p5{qEtyH1$98vSl6o+x{adzVhDL*cAB~D!o<;+K zOD`{VGLKVKjXY;_L3)4vN$&K!kU+{HKWl@Ma9QcA|E?c#L6gY>-5s|1#V2R7xGOyIdM2(EUj7Kf@VG;12= zBY|5};v$JjxOg)5MN!5;f}DT4+qHk@xGx%3%#rV@p#RwS$3c&W)I-qQ(*rVW)}&`% zErD-L2-rfg@pv1Jh~yrPk$w^k(n7svL&hQigkED8Sm&_UO5)KM)d8Owd3lL_VGw2{ z2rgn1_`)>tJs1~~$@$o5?rG4ADG+1Z1OYHYs|^1Qnu{{`o;rBeO)Nvxa}-&7cZ z#!pft@m7Si=wLm!TFcShUoAVX@iFgF#lPwimnAg(B?|h7+0f##{vovW$1^v`7 zy^fWqYc>gu46~PBeX>%w|4iSXY*BhGtAra9Z?bQ_V?;w_f>Y`wVy2)G82o*6P4SZH*Wt9BWA zF(R?}PGjmVii?x66~I2TQzz?ewtiaTZL}7a^%wy(WQ2Ha33`W6U6-QlZjBS4d8hIT z0};ltMaGFuC3#5u2ElS!llVS!ue;pT-jRm$qJib?Jp=332&I*RKB7WkR&qRLZ$IvALyim!F@nD>c^0hjT_$X zDQoNPrY+FYS?}Z_;C+7Zf|tB&_iI$5;@I)SG zUk>=-qVI2}NS%R%lm6~Y_mw3?eXmC#+d6+VZx>&)9Z~v;NKv&N9d`b4Xj_a>B*hDK z<-=i`_pO|Af_0aEE2r3q?)Ov~CkVn{NSAqS)}A>45Tp}jo|eSdJlB42+3eH;$FIQP zaD;;zsSq$VtNlVxaiO28Rz}VDC=SfNzTOyljLvOus__CKm8A?ym_Lm@Vo&&9pxVps zI<^X&`!$R4FGoAsV-~abO*dy}hPMzl-AVl_YYGDx9dzdk4ctO|b8Zv<`!u=g;UxU1 zwYG@HJ^d&$AVs?^1;;*EWiby2s_H0jI<`+}xkTunt}nd^Koe=a3^2L8+I!}6E%5rg z4b{%t8hi|$d~ApGCSUaT*jUmw;_qv~BE`$*ggMh#6BIqI?~K+4jk6$^nqDlZOuf`o zQ{5KTSD;!&Do;1v?`CE0B8JVDQHQ60pG6qWdp)vf=cQ8#OyWevVTs}3qdcXOsJ14z zrDOo#dGgGqDfqU78Dt0QDw92_$|{4&*16y?coNs!K$h@ZreA7cdl(_H=IT%ZQ9X{ivG%7o5&bVfzOE*a)kbl)Ze08 z=922-y9hgs-oaUi-N1)>ZdPn=>2OthI4D&az!udxbqR{c(;$Y&(ZvP_zDg7LrbnxF z6Wd^|d3yN5Z$qxLb$dQ{e`VXLmrODBIx~QTglQ{Xjyg=QaDIGhB`Z7RGE_x?qSEW( zQx)4JmNG<5a0+2HWTLoSgD%wy*)|PA`XiufWIxiLxk2q-cJ$Q1ifPVK17NEUXvsEe z)S{mXbQftXdQ^a;mpA3cfM6T-kz*WtD;rT`*@oCfF87=twQxebtcY9B=D zA4=lTRL1jLD4=g)D^*#IXzIsND;2$2MT!XPUqjl`y{Qc?#iP*eL@D+>$-r^%_9@1C z9ZWjJ`*N~0dlFeDVR5)ktk`wc_{aG4soxvm>C`Dlr}fe2C~CqPkXKxO z2Xweh_i@4;L$_=Ytr~`YyxAU1(XI}lgQ4u!G73e)uzY@5L5HaP?WIRL^UX!CV*KTI zcMjL7@esb)cYZ+~&jeGf`30&ia9E@503NHjf1~$Id&%34ZC5J4)U6!I)S53s)g7F9Kp;-wlG{yGTT#qE!3GT+gFYY^ z8%$YlHXYWG!Y?en6S(0%BL!aeMCj0nL-zSwRvDt6q?P3aT5uN|I z0nT>1Sa_P%{=8fAo1rB&+oxUz3Qp(5cD=dPnPQIDm?hul(8|A3vNEvjNamLyNsp{ zLhE_Qi{VPhFYGU{McTs#z&i`%_gBZ#l{wV|FLS09B)+F|s{8g$M{=I_C3AjA6QgYu ztYYqsV_drcRi21h?(1tC&dFMzsCO;iehWin6089XTD1;)kefB>&tt%FlhwtqrN_5V zK5Zkb%N_$Ph*@C~N-3l!sNcBnC=?Q%Q&$@Ww^G{{=}@Mx1_J~u>^tvRVlIc>R!+JO zNuxlW-5JhpcojtV8(rtY)LFr)q#8aS+ta7e>V#y5tB%D8d(#*cxA1^#2Kn1@cO=EN z`%IJOr4=1rdIZeVkaSq=@I%^*Httsep%|Te6Fa;W8wu`{7VI5eN!e-eT zbBsJ$dReWRnqf-Es~^0KBS$jHj?7x^dj-S_4YsFj*PKqe5qpFa2u+L&XZ{NaAP=LP zBc9D}5rp%!y)Pha%x)@)-J-4g_449E1_j%RT+S=V(1fv&#OI$UR^+_@O4XA>?PBXW z63(()X!zXuT=kitO3I%E4UJg$tXRqBr62d5&D0zX)tWtizhm>G zuO(kPZAl##gyGKK2R;0_hTG$uE1l-`6A;dTQyFqUg7?%K9n728vGd#wN0BfGi=Me|GVsL;|D}R}$F%D6S>ls-{ z)=-1$V?1ti%%V&jy?FI!t4{1@b4bu|s4-QYAvy_SP5pAPQ} z2y`|NebY_Xg5ujS6tndI$a^4EJu2l2I6*R8fUlx|uptMV_ygmFQNsg)zA6RMHY)?w zh?zpY$MGI&EWqs3pd}LFZR3bKJDSYsNH@71fckY}uwsSgF6u^|gI&llikT|+!Il{vd(Nwl>xq|56RWx~m zi^#APh@^<@^-tZq284wo7A@brrNI(_TptmGRFfs3P4#kbAw}D3qVE^|c86@bTQmVfxH1dcZWfH*>Gd$L6LOb6f@k|p>aI3q^FcSPbeB^2+4kJsgC z$DsCsZ(7lezwKy0EdrpH@Ivl!`p}}$9)zbgEiy=ex-=|vCYO8L89_f5odjbFgi}?+VaaRN~ z<)&V($mKzqs79Z1L;|CVO};Z|faVjye&c%_xNN6<}Z&kz6*= zVG+++-st=ohtVV-CDf>Qgj|nWo-A9-cf-7Giae!3bzHbZ*C~tps?)_*mG+*|akkp( zLpZ#64NS?zEUSw=-@q+rw@ds?7oy?8(Y`II6( z22K;D5oF-$aX{hWdAZ0s)7w`>WwUZP?LPr$ zJo>BH%a$OGI?VgRE#Rlf+5N?H`32!Th_sFF(P;P{sA)McV$+{y@s!+mj|SsWkpgZF zaCb(VI%8lfF?Z!NWG<=~-wN())(;u&r2mTC(Eo26|fe5Xn~`*j-gcznJ~*x zfNcbmODeoG{=&q2j(U9mEkJWo$ufCmrsb?w6cigBd{27shWwp-uF}AHEl-|NW7|k& zGFlAA+wZ;$EdzMr7d4pF;t(d^pS(xFAcyI#2~BRt1#L4w_Gs_1tNs_)7e|T|)YhX| zH+w%M?QpWC?m0{)JTvlYfMSLE{~{J`rUTnh97K7t{1s+M{x!(anziHh9EgTLA~xuY zM}0uRDr->p9Mnjwma+%_CPD+jl`Y_~^G6jo&szdybuw5wd2t;`+M;BKUKa zHG810rl+1?up$k!yo-|2!3D~}2zB=?`e0m0^kV(cq*}m(OZE|-57FoJj`U>>I1EZkfKJZzd zUIZ^sqev!9B@_pM>Dj<~oX*B*iL;ppEtL1;sQM-S2YC7_Ye^i0rixN>+sMV4A znB6iX=b50W5xS0%whi?F*kzpOH%^q@LxVlI#!;kgxC)U!7R>Lh1iQA|4dHCsgSI|8 zj|Tv^+&K5#II0ZsG^81KNJYWw7WE!Y#t(kSiN^xM-GZDMG#EF3*w1xWw0nAgax zU`;~em#sm;{>QROHsBqsc$j@reXq4Mf@6+pKoGwQ`CE04dLuZZ+;5Ka!vCh7-Q#!l zPI5?%*upF$O(_>+7X7?pMYiOym@>+$`2+_4;A>(TL%dKtz}Bc&Zz}*YTy<7HWpMnY zsVO8zcYtluW|qaR`-3PMS5WR5`2ylkqGaa(MUaNuv?^MTm0pG0f z#zSI2kxL&N2#4Pao(H29d4K2lUMT@~b@fXEnv1pa5n*SWwOrFSA>_JB(5R1Ci%!o(6VdrXnNK2^HWd z96MF;X49yUfmJYPMIv9oyx)XN%HIb(!$jDC57|v2KKae~-XAnYn8hj)iyKX$`Izj$ z=(gb&@dDD>rO#7oSRgr8mVrr53P`ww$+gGU$`_1}jeZhHiz3RokuM1TT=b0J7kx&! zd4zW0i%bjn&^o5#gtSa7eT}Cd1%&COFw3193gt9G>v; zfpCY@sJ`?}>)CF6gs+C6#;nMyD!>})5P#&VOb+l4B@0F^BYAc>1@)rF}~;o(4oHs#W z@7!6KE5mzp{*ss;ghg&Cb*=!3Ue($fTvUA0?gS1%EJeT06$uzV*J~I>3qJ2+?KXjN z6p2?4b|wnq`VM10F}T*2WDV*XKpfpF*J zbGl9RC=^-qz+Fxt*vy8vSXkxWo1b@BJ(_%I*>fZkj*ZGf$x_L0q7q?s*8yLBw%tIl zo682oHl8$s)m}uLXe2pcX>l2%dhL#AsXYjZ_QO(~N~yiBjzk{qAEwL^AJTzu-NtZU z!#}}a^4|g$ht)jKZpd(XpxhpA(kD`tIxp(H8DIIOaD=Pax~*fbbmJ4n6rb}U3j#aE z&I|sGgY&~xZpX7*EXoiPXw3F!hO|?6uOZI)-4Q|M#&dm`+)p%FgV_+wv4drpR5+ly z?u=ydwhedxbXH6pkCE$c+Q@*Wv9@V%U{GneivFrB2Nr*i&)uu+Oe0Cz#uz-GCo)Va zbtqCKxvyP@z82OswENnqgBHDwA=H+ftT4 z3SopVSrK2UR)F?IXtlZ%yhbJ~jGCthGhh6G^IXTqS{gyMe{qD1pf$d`ch%%syj?~w z)#HfHSz3czIq#vP(_2?0WUtd=X)l>$sw|a=4DpTwiiC%A`5*IBBv)&Eps+wefak zv-%S;*daK?_CSJ1Z@R)wi(~9hRL@eIWF@sl&S zFW;iJ%-+xszOeeYvfamhp!Czwq?$ago3s+vstB*(rYjoWGb1Z*(54A)0K{e7Y2qP( zOEyhaq8w_j3E;)e0%X`kLUQ;jT>^1b%voaoD-yb@EGN@vsU=&>4@82*6 z;u~dENlE9+a*+r@1r{}RHe)duH3}_1I}|H}YaN%{AU$g)iB_eV(MXysieCq>#p8wJ zmb4bc#GOG?7oWE5&HvhHIhb|}JWwBR2zx>3vKsb8ivv?L?@BI+StQ}vYyZ6WVr5Vd zcnO(6oh$XS=fH3Le7cGRlAVP4qomm!m;XY50*m>a8&VpdhriKl3ac}!%#g5DLBNrp zT7t@oG)U%|mb;$0g6Q(;@?lv72A>*-c+8WqcUyQm{zg>GGYyJie6cU&2e}im&8rU+ zLUQ{OSrc>YPz-Mp?3KwC7KVGnEw{dMM}EAYkvVw;i#cTg39ol{1Ha2bq8q+j>s7P} zKC{wH3Crv^NLj;QvzrlU-VhdmUbC%WRGh}gBao;1|46jJo&^r*O;vZBDDhr=<*A*E z->zf+(pMklWF@W4FvSQ_kT`i~wputn16wbzYnf)pRJZH3ss#gBnkdR3SSuZgahd$Y zkq?GNErWNQdtoB@3a={)0sKd)E6VxM9csOHW2X-7p7)obvl)N!R()pT_0trh-M#r! zzZ*A0l0*AE{EK?KO8%`p#@_pq>lGtkbAw@&_3%JFO%gDgSTIqYVF-b`iATu#d&jro zP|bT8$NJc_ag-9+NuPFFOE)R|in~G%|FFBwHi+xar0aU;qE}2R^ItbsbsSFU(>%$4 zt;yeE&tn*0QVNY^BWYuI`pVpoS{tog5cN6E4 zhn-sVA+hei4Nq%aG7KSMuifm6ev8>0+Qr^S*I9R#=4kKj$V@88lkz>Wl!DmG74As<;Uu<5v%pq`BQv zhlDfAsghWF%FBzqF;y8>-_sDMFKk#pkdG$+Z%15KuiKU3lfB z-$EVb<;-l9yDrFklcwR@8nQ?3``&V~Es{g{0X-f_9bKxzm7l3GYaabc*f@<;c%%^W8%ldi>C)t8+j4cd3oBqamWxARrKc zf%<6*quPTIBFWP$kj~)W0`in&s8p7;x7fet???#gg0^$q*QTU;gY%JyCvNoF6Lq_g zP<6$)w}JmTwMnrMaM|`JKbFmNpn9ZDyff7-$2gy+SI(@CptLe|dgV-hcAeVFdzV}P zIm->bE{PkJ&64Qk{o}W671K?r624JfC7ELW=LcEB?R%~8y`W|Druj*Hy}zfT#bR*E z7K;*`&||WDJvBmaj{6AO$xsfKMD$}_XLLC=-KNgc8of!5d=HL8DdAqn)kWE#DsQ|@ z+hp_S(~Th+*bIBSQ9axUT^@qLTn}B5^Ep)f^O{+WABq(@PbSmyWRlGO;q2WcHyQ`6 zaaB0~oA5b7fzQ~2pRr#H1ktcPDGE}D`Sb>6BjDvj*p2kJB?pSpI>N8f1qCDKBI%@@ z_jVTgKTz2u)Fez!PS@z3o%^9Bt$h9P>Ib(AyUT}pK8&E$V8!()*)viv2!v`Z@svYq z&~XQ=A0+W&@>}jp7BYW3g|!zPA3x*{1fqRh%ck3llBF**#z%w^fVh8=$9QfDH<5lg zo%?>H5g%d5F&9p}LOFy2MGEoTOB>Vj@`_SFC_0RS`y`T>=vMrYym<&Mo?phkCylYr z{7`MKZVzu~(xZKNzSgKUEG(Ft0h!Z&InPI>%LZT6PU6&w4kMJD+KlccV+#HPku+BEoWt^kvU$6j6jf!>5@VO4 zbX-uWq4-ljJVee)uGl&D*MN@5vs>`z?EhE;0a9kfYCZ^C&f@RxE!W-^UuYdq3Xu8kGATF{UX`u03~MO6Z_sRjx#e|C?Myi`SdqIwY4Q4RNJtPW&*(7REYn?Hi4W z7aFFQ0;imeIO7#!ApP@W8awsxx0O`;9f+^p>0$xI0 z`qauwlPWc}n^z4ypE$_B&HMB3|M(X0(^C@$^YSb}mA{USn=s8MCB+Qj86K#HWZ;$1 zECafdFd?K0qs(j0Gh;co?Q8iY^+%tZX7P%FkoJ#2YZX&>_EQ~vR+;AeFSqT>a3=hvu}sCUP^3PQW=jnT%;Q*z}zzL0GGOEyUt;JqyG;HXk>D&YyS0rCr5`_^xbA*HG@r#zc;9oQKRU zUpF1~H(ddbmw~>2=@5{Vrk;dLc-8(fRDzY+f2lb$GsL4L3sRQ3iq>E5hk6O$CKW%xBt?wB7?;2U(50` zI%6|g6<0ssj+^8$;q-|fd+b~o%TF6rbmpkwoRsH} znYRTTAzw676!QBvtH=%A_T$lQhgqPKGnr6T$QSIUqVM zO#RGNvg`Kt;zWF)x(9pR0ETMpzs1N1YEv-fy9C$8^dy$?(Ny@F>DrbpL&iB*C?xlv z)4bP-pDzv!@%4Ek^PBCde6~90+IU+DhE7>ErD&_&>l#Por}oHCrf!G#h)JX>e$0Ln63z zf`Q^sWALX*5Wy-2zCc%LdUkX1*p3j_?6;kUfMREZ zWb`nZxNt0?B?$|$YUse*q${KGq0qNHEQQcrTOL(J-DD~>7XGClUn4z7Zoc)_z-^g) zH?OD}o1GfH&gV0&Dyy05fKU5kEbzN1p??C%08_i4lQ*}kVl%+WpB?Dxnd0)V;2o`< zU-ol${bg8xZJ-FdmzIYdpl>TFk-4UFq;Vx8h~eCi@l zTn?wn7_`DtL*~OBU;52Z#{7Q`l^NqXd&87$gUFVuUN@HW{e)#uvS@2nc53uO`HFke zqSkt~NB*(l7$WCygQ&>>qkWuzWOZ0lw#C-ar@LvFTe{mdeV6?zA&O&daO~XFtbyfs z8}e)2{?q`0C9H+DnrlrXQW`ckg&(eOx1Oms?7po$9l04zn3!BT!NO{dr3%8c{)b)h z3ZPgxS^k`&$&>^xj)SMqeHqh(t|LOk`U>GsS^V>u{pXPj0b_0n^)lY-;!s~g2v^pT z*5>hJem@0^sxs|f%oni#oR~m6#?T84^ABZ5$OdJVTXuD&rB;+7fQ`F10PivxhfzZa z8HXXKVrzV)Fr{weJP^wtO7%?mmmd5zwz&vYfeLGO>{HFq&vGQs49MMB?SQAIXe)#0AXHRTRit*bb(Xr3+&$YF*I{~^TWUb<*NQSk&JsrGBS*1W_>>Kn*;Rw z&VXqYVtFAw?0Nm2fjH0h({u>_dL{}R9kYLrU6RK5v>kUGr}A{n4mC#V@@Ms|Z9m+9 zNCR*Pq+x8CeMhjjUGv$hYK`SMNyK7Kk~{F2nVCCT-7oA=?Y3^iGIH1mWdHIZ{=dHj z;6YuRo;5%=e7e0k3E@oP_t4;*mghn=83;3HdmeFU^|{wd!D7Zvep@Be^>@Q5Ac8LI zjZpP<;wqkhT4U^UZ4=#>ypefT52pNjFaB)?ag=AKMsp~(SKtvtXF z82#9RVF~R@{9ktNUvC421z^(Cq2bYn0dx5~aO1@B?AbFjG}ir_Mt_>+m5s7;_Yw0A z+{d4*zrI0Z=t1X>QhLJ!JzH)*;-Sc>QEO@?^6aOk!0nAYm0`ZtAxQ}2T*E%1#26k3dx z0VZovDdFbG3Y8{`Y`ijY3@I2JnXi{|%8kbeMnq~IcEktyuA;yCBmHKE{yI$m{H6JX zVt-6g?p^bhj}%s(YzC1V*Sq|3E4@&gwSkW5M3KN}ZwCfG=UdPIP>3~K5>Pw|GZlgQ z+_R;ZdetH;O>EPpOlO=Z!4859^8IdXoPv@v=&E0{wo z7^2&_%zF1fbcJDGt4fC9iKBqWrv%s9pxPv)+f91Wx=*~EU!@164MCf`5hzIinrhfS*%(3o-%le_{9%{ri2E6o?d*09{2-;{m4e@TMKsv@*Yk zq?5&fZfOS8zx~tS1R9+a=5&W&0FCFo`kjz2KY=tbdNISy^`@$szPRJb@bYZAEHL=g z$9aDp5}2N(6jm2H$LrMTFqr0s+H1PtFqBu|3@_+$E{wBFL&0b1MjItTc_f(TagfdY zT%?OhHvRX2^0y~|fWnY!CZJl*#!btTCZ7Ik*kL%39BHH2ip2Z9cl+a}^W*?EeEkoP z5r9)D+?4)|@z?p_c?UzZ7MB`V_3oI0cEzZXccay?1u7Mt z@lz2k(Cc%x6`@>6{~G&WZzClZs%nnbL&Ii6zsl#yhViAaDvZmdaOopFjW+kTY3K`+ zwrl7wL=l9O`+G;o?{~zjQ#&)t6H}yGYHmQRYBDCpw5M>*IbkgW2H$RzPed%7_*sts z&Px<*nt$uaKaXJn;E9>5M)9h$UApbcSPwkuUigU2a9nC!HWjTtxYILH>ig+F+Q0t} z0Za_A6@dcFOs^#b0?Cr8Er4J3skX=IiM)O930ni83N_$N35xmrA>fJOE3?%@zi8n4 zWracgpBgKmhq+)_cO9Zg6PKEO-xg7Ds|DhCA3&$a6 zpke>bs}^r0^|u*u7y0=+JvjqP7hPq+@B0;Fu>NvCyxOV2!Y>_mtPe|(PISGgv!BiB zp)(}-Y#O)qQ)-8AQd_tGud~h*5e7@II9%Q~Lj*^m?r_YeA^%b$31QGmp1m03uY;Y` z3PB(ul0%Kz_>eSR1Vh?T*X;XNOV3Q9>+wuMpBd<&iW#xGEZ${R{2vD^`FqciXLEqN zl~4#JJE<6n$(B1UvO+y2`%sRL1s5$5gG?Ah=$E@3_zJ>1LqHidEWN#NjRl`PV6%9eLuMTfa_OAg)|ZoEM~VSv+L;3F{cW@@AUK-fE_cL zRC!NIVLT76!}`Aj6c45z+=Zu%_|#V0Pfqz_dbP*?gX20-$Pt16n#d^6X|XqTY_kT6 z3T(U%^s3WuiM>PeT)DNs{p<06d#gphK;5k+_L$871DWaS-(W<1xD9 ztM2MBmavtq6al_YE}ZoL|DPh@YQr1GbS#3ch9^8>9YQnEXm`O(!0#R}m&Pf-$EcGH zhy<`vddMCv%zhhc6e;CpCe{`K4qwSYc@)92;`J_Qm(-s$Tz;=o8lKuCSwaH6toQPy2t zacGMec5z+-IFV^pKZ1WjF8__L|M8dXpzptlx1xG2shx{s4XmkHjmNNsHJS5r5))ZW z6azK5nL~gz)4y#Ti`RgZ>&Xi2#i|a_XKY2S1+T+%d2EL33yaNgEY=uhrZ}1cli6Zc z#~4el`=1E7edaW%B;uH%bunE&{T)91a!LNMPg!YBE3fJ#QRm$g9E(fI2*R9p_~?*? z#{l~>iMvi3)ri_`{_)x!f!buzVS9k2VE6jliuI})DrxqQt_P2{1ownTsjJie;wnxP{tBsvbGNQ#W4Ud;R z!-2_WdAhEFI5KqOk82aV)fw|NB#y#F0BbV=|6JppaUA ze#KXjkpH)W2jiP}LnTGcL&}K5`#RHn?$BU%9Ft?5PHx!Qf~I=1Jqo0`?$TWi4!`3L@Yn-*5a-dtJh`vY&4MK&yy^zO*$D#vj& zASNJPu#rPw?X7Pue>R`@72gKFRPQQNYmiK%`E)&G5296X*mnL*k$D$DQa#a&UHqJ}z`8XMQu7nj|0GqVMOSmt zh=GtRhjS*MksZg34foP(~c}X;rl1?Kg0bBB~f|} zw6u78HpVS3#?y{~DL~6A_J5<6|5et%RrXkZKMmTVE~;B zG+}Ox{D8(u9f}(w8vDHc_g5t4KkN9WtKua0`5k$p9p0bAoOxBv9UkPrud9oy>(HzQ zAaK+rsQniP@_%CBH)!jG3I|xg*C>!8S^_(K202_V>2}6{M>y;Z7+osAsFYhWZgXss zIBB3U*Prr80OL>vq|gpZb0Y^29PBz=4C~;Qr_aVi*uuRCJIF`RBm;1WL|g#?XAV4D zQby^4-=QpT=|aS3)mPX@I5U6|s?`gr?s5OEj*$-?F1Asxucsy|;Bh_uW6BK#miF&| z06qJEiYy^gZ}_J(09F?Edh*B=CK-V)$As>m!sbm6@iYUEOdC?3VC8PU;Q7Jme zS?2@DWjPoWSLpgL^#m3a>2KgN#<=SWrK#q=#dY$_t5>|k zr+2TfsOOhVGj9)dk$xW3So(T}Q&_DHq-Xwl+?@YZS^T;Agc?a4Fi-ZnSf61qm7lHD zd&wF4StGSma@;obAG~lVRK`>B`<9*VV}v}!P(O4Y85LI}$zk9}pB8|h{BUMKHTn^V zk|sz@`WE0~63DkDMU&qSrk>0l5FRe^APBrj{s7^@$lA4-wncZ0Z3QUV*xJ&D-`+XN z|MQ)TN27V}Ae61OMk(dDh77H!{h@0_C56}h-pO+PD7z_G^Yr_i_*t!djhvTeRqu1K z>7~tW7$g+V;FGt1O$*Z-wB#xG1fJM)^O?xg?7N`-cK=SkpIV@_L!cV7F}6_#CuP!< zJcr)fl??%I%rVhve5K-d7=Qj_nXukR{8p6!=Fv(5oumlJMx9Ns83%UWqx|5IR4UX$ zQicyO4b((!Rtr6ldnLt!9zXB>1Ec@?+`$1nq)j~3Q({zT0BHyNhi!igMKyB!?nI#T z+zi$WDq1@aGU^nrH2SY-q*Y%+%inB3V^@phO9UYfyTyNl#!n$&(jK$=?K*Udn*;%JCJ zIUE|N4+6qBLT?=bhg-DE*^)IjV9z8BGo@kyxIXT_In+sUTT-0cDGA0fD&x+Puvr1+E^_6d)!Ap2#;}oSsNcCr-9_1H&y$g%jR3?FWhk<{jBs8e^%d zarLj`3}S4^Pap`25K<>h0YL-2%}Y zh+;?(5QD#f2Y11E)GM8EaJfT!f#+45D;4KIl`FRc3LXUPIG-wPZPAfFLcAl7v^z2Y zj!*G1xhc_2MpVI&o&y~7a=C&I9u$r}ox*>UY5yDQ2J=A7)kV+JVhu_Xa@hSrRit7H z)NS9*0RJG0!4iZ)88IBt;R%imrkCc!1?CX?3rV3q;CBkalP9xT(vz7~di$d)P$?ip z^Ms-5UE97ZVG`ay-3R2|WVkHf|Gby~%OB}Vd;K|3x@VH<7pZ-0I8dZ`bE-IC7!^J- zu6ZUf>YF$3(b&y-+6x`W_<&XSe()T?{qtNDAIBeA>!^Vr=u{V}v9gU19Ir;%rKt|T z6q3eHjAs}7It-HxNZ6=<+=KkT2Iax9H>d}Atw>*p0FH|~fj9t}qLNk1DFg$G{Vp6Q z+vEmEFjt(YF9ZcG5T*64;ji51E_b-GFrf;BEZ73Oz8FXy&xKaUtL86uKyMktPcHj{ zKq6?V2kNg`yFL*)k$Cj}NUyh7)jPD?wd$oBcuLGX_yYEcAw72 zcgt#lo!F1IXMAXW#6P7HXar}AVOFJqq?u2^eW!2$3RMP-wh8(h6>*6N;B?F*Lpvv# z%EdRSaPaBLQk`<(m=j~Bc^6PTb60)4-EA{%yWlF@lrDu??`v)cvf3zsLd^=fM%G9iaVQjJkQuv}LuZRt*7qbslt8Iu2hJqJG# z3cC#@x>4;WdBQxG-qu()=SxpQpX(WDt@eY@Wlwk5X*`}W4#pu_ov@`diSZGNiJon1(}v7N70+QVqfB7_bsvg z)US6ts($~#W&7d=Pau4@hZWQl9?j&kEP~7aYG}A5^&R*RNQWX_-qJ47n;t`n@6FVT_3V*)Eked$HCklnO`CnbRimc zi$!3~^TiLv(Tm<3&B{&K@qo$1BNfYaIQQeQ@F>i}6}SKrF4LJe+pzFxTRb*=n_-~R zW((t3FI537(FQKQL%7T(`_nNUK>mLY;7NDr+#57sjMj&$JS2w~8wWHKAu6ada>ih%2Z)l)T7mVY=R z7Q;lTX2--hKUk>6?c?eOY+haNcn200~mQry8)**CDc>UAJT=82buO+An09(OHO0{vq_SjN+qT*Cc}DJHtryWnacn@iZ1o9<9kk z6_4U86u2%>!D2G{@=ty~FV^imH+;6v`CjfV zXm(77pZThp;7+u(eB{Ia`5B>AW3E*ei&JP3WScXu%`Q7(M$mklty~}{Jvo%=x+XP& zo#o_DOeS!&{2=YXG(!tan2$}e>&s&z{*X4C?Befj2&hqrqnX_wYbl=2iQ=W64bki0 z#4~PAoY7mce)-ix93a1RuzFOGc6O0Udh$w38JO^i{LkIpS4luFuq;3OH&X=vKosI1 z4tRhnfLu-jHp1p#Wx5N@_a{&`7b)dme{s1UV2<0c*4nIuHoF`c(MC!DPU1tc6$fPv zo|6aDrlmp5vw-n|U;r#i_S4~tWp#;Eznt#=Fk?Ys@yOk4!?a3N{E%M&Y>~<6QlyN@ z%QG3!6JXPlgS~?KH%7-Vn6&cfkGV(YFrA@X1&@M%GmrXMAveao_9^Sq^Vi@H7O|tk zTU@ib3kxMG^@A+R4Ru8P)Y)Q`&(ah5o#l=@10h!`H7il|HTSLJ*2OB!1=iYyeS}*H zQw@j^zWp`WIqF*ipKJ*G%jjpe79O<6FIryjlrOZIK}}tl&`=s0HTHYA{6WH5Reu<}E-#BLDA*vjf?E#;yz#%CbK^XClbt>%C zboWE1<^m7Zs?X_4Hn5$b-}t;PsjG)yg723w7}b6>w@+&6rdI;RWtW(nE-Z)VY4+Y0 z5vM~U$JB80?5+fz#b204t?XtTd9o>LN&dSB(|PcRTf5QA&^TUa&gde|D=C!WMAn&z zn3}?zVoNXTqloTPXy=z`D!{4XkydROPP4K?x$$+tG)m?7OTlMZP{5%Puv5@+SIbF~61BHPd^q=BJCnZ$FZwimfGx4l9nZdaR} z8HP!rSfc-K68xWY;R(Tb`;?|;#Yu`+V=5`>|VZAS8_~%YrB@f0G zEB2;6qb!%v7ep~UK0_~c#uAE-j!lqVdp`V&cjnu*f#Ge`vTF39>X3NbcPj%NeIeBpng##fZH~tT<+Qwo#)}u_fQPPy@p! z0DWb|a#$Rl>dRl~SUa5X>=+zx!(%irQkFqwZ7ENaGX-0?RpgA&0foa;{=v4edZbtQ zAQ%`u)jn6u{7B3f^5auSpJIQcilA6ny!Fn<{z#mn{6o&YcDf;rc9zqCpL=6`oQIRx z%q9}@)>>>?1`U{O5o4R*H*zQ@sYC02uK|D}NnY)G(5JK(0k~53uAJOfzvD!ad1r$8R7txdi?-+>$or&5;YE+zC zmIwQ86~L6Hf_=aW1oQLgZzTHHSbA=eFz>y_XBa;U_Q4V%WswL6r966;o!dX$dq~Oo zhntLQPmLtm$MoIva288O9L^p9?IM+CC+e3O#s4WG{0HELi}^EuRj*tjNgJ-05h8&S z7a-!X&*cLDa<0CdAeIpB9?qN}7S`ADO?(~iArH}%wNDl~@HL|%dvLzqZSXfQ!3*E@ zh6pD;`(&VY3ej&r;%=1Cm@i^LiJ2(l@ba?mGOLgCIYT4%TLRyKURd#J{BSw7q?HA) z2!TwQ^0J*jlJh1~myMvf-_*;;S_OxBxCD3 zP+1}74cuJN3?yF8V$I}O!n%{P?~PK{|9OaW*h9AJtZ^Bo@o4Sz?vs=QpJLJIccvWT z11B`4=xZC%(u!wOfWFJoh0JeBf!>NITL|O`hHoDfvf&%PIc0n`o*iq_2Aa#GIik@Q zlR;!fD%5wqk0VK6LdKQYrMIyd^>VvJL;JhIhb0k!P~&dUnu*PJRmk_b>AQx&H(9*7 ziu_W|EkYJ|3?n+goPyAIllsUm7VyupSgM7OYieWB#YAJ7E7ujz5%V=`aqcI&+Ec1B zXuvi6L@C&(EYBt&@!pKt5Zj`G35*e@w+a${4@@`!bC!wC zX(*CSOy;ovgZ=m97q*FgTlNLyF2Funi-Ww)_NZT4f1V1*!dqxm_27LMn$fw&Tp_lO zx=T_R^jOIdm9~)Ag(Zo|I|a(q+H=F38o9e1CiL7Q)#3a(3T55}4hmBgj1G=_U~`jp zoZCu%&%wvI!YvOg-7ZA(Q7zYa<>7c%#P=R|^abWpPct}Q4l+iyjhlp1OkxE^W-2N4 zCiI)VSj)6(B9ZZV=u&J4Gt@;^sDxNgi$AFJbiQCQY6^m_mZY9g@N>VIj-!o7^upiG z6jBV&8D6@}yjpOjVQIU?GN}3&t2PD9m4YN_)ym?oWPek0F& zY{&AijjEKIeWUU8q#jD=wRtCUQ-i=w21xjW(J6)z3?%K+Sp6 zT3<*KRDYJc2G*czIe&1JO5?&%E!L2Wf7od%AW9gyxoWGEAaUpEmZbf(t!0<7YYkDl zOq!L7PO`8!3JznlU6Bil#Lr_XQdT|yNh#3V103Iw;NRGV)ITWUE{<&n3JbhQxUflX zS}y}n*j=`jn`CJ|HR5c;kPgD-*ZB$jnXB=oiqwO`@rpR>xU+e5^&}oyyZhps0~+pW z8^3o`?s)yx>|#TS@-mPkKXD$j#Ul&pkK`$H=wr5)?55`2fpP~2FcaNru#Z7<-_Ni* zf1RS2mM(F+(w4YXG=2E2;j8xSfdXszb)L_93CRLf-d1G3J91WN0KEq*L1C)+aJS}L z! z_xCbP(QKap%Tr2pM7|lo+RS{8u_M$>zU5#&OL((Joq zna>c0fX}NZ(Yg=Nb3N=qpa}x2%_Ji5*qN79NM;P}`BxcChU5S2nUp8cY5J#o*OSoe zHp`^WGa9@O7Xt35EMEoqZP-4DAKc%zoSCmQ-_&~E5t;)+q^I!S~c`+jcPicYt9&}cwfTgMjPiMZltd{dr>Ac{O&rQ@tLcIt| z5-RmpI77d=8?;>)<56t{VFMxmwF4}mf@=2VnK8M=pd!`sXQE2&eRn^V@z9^mq#Gy` z#-}JfqNah*Q70M!7V(H)i+&Lu^lQ??Y|!oSdi7>59)()(y44v|Ien8%LQg)lemRL~u{m`~`rWqGx6xXP*aY+A2yU*eS6WFt1P8Ga`Y z%%NnQ28h~>&9(K;x({Pn~Gd3Dss+576e!Q(xx3zQ9 zMvb93$fY)v#&4tbIoDTfEqU^%RtJR_0pvzW`q!2!0{CYS9t~6FkOk(Gj{&uIEAJlk zO7$pER_I^%(7ZW&`}Fdhxg#fkMZlWZVROeV%oP}Qmd;eVpbf>4i#>9*{dm;dvoGCS zd}wkokfm3%Vsz3N*@))pc=mS=fzaF!!42Gep*wH-60nnkGTm%>_Jy!P@uiWG%-Jj7 zs@2*>F0D=E`AfS82yKb7{D9nk^}!*x;IhG_0QQVd;5mJ6+M{z@`~o1aj`2r7Xg?NS z3(RUGxx7F6m`CakX(^cX>~n8k#SwHOzQY4$vw2lUx2k@pIwo(fl&28Hyh8-84M8U0 zXS5z)@uc0sIIMr6F-n@=u|9ooUZ5xOxZf$2!F z)e!vYhE4En-WXS^b_WE0-tFBWkemUM@m@EG7fStPF8XlEDI(j4ryEG)ehjfT^(V@qA#Jt76;gZ#k9+9q0lx-A59r}?)2oJpQ6a=Y^dvbi*kEc; z8UM+#fP#@<+Ex8aiHI|rpfkxMz$CP z4*8*n$Y@xzU_6s3+4m%tL0NM&QI8YxrR4RZmq!s-hX)k z+^rtjR@?6YJUol_V*|w#84AA$G-4mG(Ix4?x;@RR;w%d?B62)~rif;BA`v!q0bsj0 zsq5Mg9;voI;s;hcu9FA?U2+a7@fX;U4ULX8rZ}y(x%k@?#9(^&bi;lhiT8x?_i23I zKlMBwFaX=vKR$VfJlCyaH!bGd1K(mP<%u8B8{()mm)LNFS1W`9FR!OZ&fY5vMfF0m z*i41{p!uW4X;3K@xL6-f)wM87D)wY|ys{jw!IQvbv}ogU8KLrC$q4@rBM>KSkYDpr zdj96nu7<_i z8s>GHKe_EXMb5#RW}L^=(`>EU?-rL0Yu1B;#Bc7nnF#DboOL;vdrmqCP;*t1tXe4j zz<57r&abR$Zq(9puqtIw3S*PV2G;jD+S>Uke~bf~l5d%gEK!fG}BIFq9k zSdHs!=?9I!lPuGaEbF{m>14H@1c6Afnp!DS-(qb!WpEDh=s4sqY4x-ZcN zX=N$=oeo0lERrp!VS-y&5?=)EwDQ>qQ*s&g2W2=dZ$+SF2 zI-4!@0-2Ht2Bw zNi4yyb~|Eju94Crqy7Ho)d^ZQsYTZ{OkI!Z6&@W_v1^mVmr)+RSAh4kzFh1ka6yP1 zQ}#1n!?pdqXWP8+cmFMIsRL4p`kSf9Nf z?uXXO>8v-uIf=kCYsgeNEh1ddmvEbP-B0p}C&exZ&6c5bsKRqW+FU1+#2EBcl9*OPn0Am!<{^rmQk!X#$#Inb2)-B&6x zy?3b64roPhd|ZQrL(v*c731lW00j|JtttKbeh& zVimngn1eKsI7JG^Q&mkoc8kOV3pWXK$`P5FL-&b~hx-Ry9SNHy2M%6AEo&@bJzJMg zk;t3m_9`)p9ndy#AlKtxy7xK=!L}j}QuL$>wVq}0c^HZu*H?AuMn4?s{Ub(6#SBK(q*66o0UNG+>7a&SL*t?YrD^+ zARn+HHK6xjn3HE0iq4u2rhW3}564?<-@dy5s@0`&%(pNtH|3HKP?33+ziUPd9 z-HiWKLrQO&SKHIwnY29rO>VGWG)eC88Bi$gBq#^>qI(=JH<3A;5e0kXf^HldY;31Y zGkmjB_QS^c^O~J4rL~n1n2S}4=yD_mAoV>J{gEQ$FD{28%>9DzQR#L@l7yH0eK0aO z9i`?Kpm8?v!%)mHjAE%2Q{BRFdF^8n$OqO7DezZ%qz~unz5q_deRjYDVexvTetb+R z`WdCW(ZE@=Y2A%J#IsayEvqYf{o!UYDhiE(y~Vu?=3jdzsNPVeplFs--B=IS$PW5T zb2Q#8i{Z0y^le*^tM}@2gHt86dZgUB*IHAg0g|DeVj}DLyC&>%E4q&hm0OLaOV(wV zKT*T>*QOd6dK~-h`Co;*+chcs*8^Ah#dw6Mb%Z$I>N%OJ&nuh*o1Ef}3*P(T+ue*V zz!3bzSB|BRZ(LjsNWEZ&Z4$}t2x}FBLSxgCqX=j+F$bs?qLvc}tU+S+u<^)k5=Z`U zFg?&I--IDFyoE(kYOXc8kmNXXK5|#1N%|ch28SXSviFCd5oa2D4zUf6SxxqtpKMCx zvwgVFvZN4_eL?VFor7V-TaF~*>kp0yw@=@uPtk?kE?c&4q631l_}0vYjJmTac@O-< zf!;z=cEG+*@w0P{tBM}4F>2J2CZ5Q(Izq@ zopZ)}uvHcbSsyM1CN7KHy-%@L6NK@HZ;@@Ee8N!hSXYBN>{}Mtw&N1gnd-5=`Ta2k zKlKG`DOL*jc(i!J8k(K1-ITqG)rM$?VP^L+@{!$sjZ*&W5bcv2UX8Mnp7HhZRorz zzkUfZ*|OyZ8=HPVgY7M=2<^^L?62%No+e24Aw+}W#BZdR1My{GTt|Sp5QzB%&FCLK z&YvRDwt7gh_vP#QXN&t0#Cpf07Z=yZ<7xP6>!BHlY_70Et92Rg0!`Q<_l*`rvu zK(AHQZT5B9aa^Iy(?SW5yZ4x;r^GEX-H{GpvzUE;Pr%m2(pQ?{8w-+2438f==Ij7m zP-SJ*BDXuycsa-c_aK%KOAJgSo_hgri^KR^fv+j7)PjA#$fXZ1DATLihVRgA5gWWj zpKX6XpLaK?97KjzN2L*yt$tO{ko5mG*+Fp`ubsLZ}ZT~M*y8tl~ z%m;BMqb0XCg2+R=Yx}~8|58|`Q8L!oe9yI$*6g4S570Rve_HV-x62dGKK2Foi;NEP zF%q!mM<##--3&B}bIXdwl2LtB_;E+XY8X)Ju%_Qb+9=hUCsH@x9@Y@u^3@2k$*tsB z&%-sns`5oUAEG2RB1{`^-ZnzYI2uq>~DGd_ks>k+n?=Xw$qTb=1 z`#L9d799h{o@$C)G5t!n`2cn>jtJX5xqb3$Am3zC`GJ^zc4YvH z;lj9@>>XC$=IKKejFZAAF}zHvlLgi7`y<_M9ra~b0a$<-GuHd|2va#1eKp=hXzxD8 zY?dkYQX%l8ivbcL_l$k&E_$Rc(6eHSmHQ5jLsz)Yc)JgQXt{_>63{>SCTbYg;W0yg zWl7~F_@$3Xg77^BJD?YHNdBFY0aDOG(g?kFt5lg5TO8XF2Sq=W%Jb%W0&(}awU|aU zlg)GgcKYDbRJFShAX#FI^EJ<9pPY3q$BAC(s@) z{P?rGuIoFB*i`)qw{h9@5q)p>l|l6gKPjzxMGimKt89N{TIOjQXg~sW?vxInn31$&cj*NXq>nJLoxF)@-0AR)zpUazCL>5ct0PgUpjHnz=(5Ii79Wq z@_M3t^lXBSjhnN?ph2v71zVx~U>#4Q|2@wzI5i$dY`E^}ZGJ57*V}B5-jV5fG}~az zwe` z(BI0R0-hx(oX+x(;;jp<)aR(;59NAoKMB1G{ffDETQgrqYTg(5ygX4mQapJkVF~u( z5pYkd>w8;sjUB2EM^K^V!<1Ej>lQ0^U4u*(75Q{@&gFb~{j-+5jyNC?A&skDP}G)Q zs6UFJfK)VWqS^VV$lb9%#rh`zq|O_rZ!RZs!Wq?juj$$o5VMaq0IR z1+t*DWp`86WTqFqS-LAC)7Ou;Re(xZHn~*@CHN?n!P|q&V;gS8RiC$5u?E?Ct$igC z5>e!p{lSq&dDq1p)a;zHoFCYbpm2o!Q&PnzmB>3W&s zOrx84JSfbwxEL}no?DY;mvk#|J=j}C2#%aX7i~Q8)O(Av>3+;v@Ay@;71nJ-N1m6G zA?LvN__K`14=6pqvZp>7L!?N7i&>DfOxWS^)#Z1Njc|sMDg=yYRHIst(vNj79e>uY zc@C5fHeTiEKOlklPV}S^f*MCcpi!K;M_5D<=;$oyMUZF$pY$5iDIJq1D+u#s)07B= z#%jtJ`ou-=*2_fPq|ta^{@J-ozUQhE7)-uzmQQbsb1R7^c#=;lZ@MJ7-@$z}nlsVd zIV!K;S3L1V$;k5{@Ku_1o3vI50G+|E9tQdPs8lnXwy;(Y9m%iC(f7oEq0xlKY2A^D zY27t-eRL6Y*~{*87#2oT;s{}K^IA;~gf@SEZPI&dtZ&h_b-t$@P@o(m1}Yvbb1H?Q zGk{Ki({+WWSo)9w>b_-@p79fT9mZ(st#t71WP-l0bV|Kd&CRAYZ@PQ+yA{nH6j<)Ho_cV54?is8*{RT*w#&#A|ea+xit>QuD)w{NDn<){Vxo3%JXNmwI zH`2sHYlQ#n71cj#oC21xaZS4ghIjtl9<+7YKA+M6Ll|0v8rZ*`i^z=IcFrep{WIp)2xs;j8~x;V(<>S=q5IvPFr$|FP(1G zX1;rgF%{4Rz4HKwYj2O6OnJ(P1Of_gAi)>u&SSnLq6AH6-aQT0`)%&s4i;q8-AqTk z*<@@E%{^J@ggHvCWGo7Ih;!csTS^XGYU|8O}XX#>^t1@jiB@dG0N?`J;&~PU-k0Rz;*LD7r z?o4MusZQ08%k4o!?!Ew&xq=Y%zNE;-(;H+K=R>FNzIn^VDbdR_$Rke5Yh0XwoPU$! zqRv7YROsYsRe;UBxL+=C@fR~g?d2%Pbv~j|ZT{GDL3}(uu3aBmv#VHr^fbJRf@2xVZ6e|N5A$v2EjMUG-~EqfAxjWB98q?KCv4pVdv7z z%yR(rREEBrViJ~7TfQ0KSREtih8In3yGHv8(?-2oq}|Xw0gY4AK#;fh*u&e8y?V>g zT;cHQW>}fI(aBH~e)p@rZ3LI8uVx9G474J9rxw~F)=T=1{I7M$)&~PCms@Q%yyGZZE0QRxw3+#)+rquK+%y(Y z;^FrjD^C$@3~p#f`veRj8Okm6CO-O2QCcY4W{qP+uff!pqK$x0biE|ws(QFbu%%6c z8zB#o9G%L;0YJM}9r`1^nCcVtVKDD*8S+>FF=C5Gu!2IzX8ojZ{MixUnpfK-8#zUu7ovK!< zBMoxx@kW%dyJEv_eIL}W?zBhfeXwwWK9y%W-w3-}95uuUbR5o5aOg{B&x(a2dh03V zLa@w%j+aOfGC>xgYnU)0`KVcCk+#9-Ai*z%g$^g38wTgr5{qn|g6HG0@td(dAKDi> z){{rRPBr9oOKlDshnK4%FIyC0vDug76y30#4Caoto~6pUp6Msq0NkC{K3$nV5=PdK z0v?l|Cm~eyg47xLfC|Tw`4&lKpvad=Zi=TE4fo_f+_R-;_i038^=T}fO{H=MN)~%V zv8gA>$AK*r!rLsknAh49oz;^&Hd%adjMbCXrAo0LS>tyEe%WIQd7jnt8Rc)FFG7gZiC-6$^aPWcN>fsP3KRtR7IDC zL94W|g55SbJOr#JvnYWtFb7Dd!g1CPR?MdPk3s_LY(mIBFDnnzO>FI>pS4aayGTn3 ziUI3(_v_T?H)R_{2B&)PRW}Q<-nfcl+=r12pM2F42%&Fmd>1{2EFLPv%(11O!OhyYHJL1Z z{#ny?0kjtjHv#-ItE;^ivEDGOnza7d3JT@XWY*Y{k%uVL)g=*NdQN$&8-&~Mh z?}r@(^wuMyo7(*1V;$PfvQM)Zs-8~(Zn2#aDu$#V8E{Ga80=%AUgz4Snu!A%4)$*( zm*bg-`+4OKrj`)3IA|cl-f>h3{H%bdJiQ@F}+_itAHv;W+O7Ul=3kVArILugd471Hmv%Fd50foh>N3hLMU!vjTna9{xZm zyjK2WxLG#Nn2?q&c?zo{&lHZ)2OjO?Uwrm;3x*hmRNV3a@>f>1p5rTCNj~bYsVC1p z#e(IB#__TK0%Rjf_-{uudtJR@wGOjdk1tUj;bmeA4N`dsGn=zm5jH@FBkVjYR|wiR ztD(7$-I#S)54-g4FHd7Xnw2mQRkU=^WMut&BQt5+G*7Kt336m z9+f038gT9=`N+m@Y(~29-_%}^1OLofopb{9EB3|{Lqg8WE1o!1 zUM}e$gCIb`2Ct@elBzvy!#110H%T?HPETrs83UE7w3JvoQ)X#eVA9?03-)O;?js^~ zfMM+8&P(I2R|0lnN0bcD<AHM)1E17tSPpIW^`SpgElA6Ob= z#p+oskG|)ez`@@FhqKimkrhPbGr@vvt2uR8S5J`wcx8`no%JFX3HWKtyvkv8G;&6u z{F|?lfYXPC);%cR*Ig^aOOI>+U7}}6#5uxfYuAtCg8J8`hW7{1l!7o}qGvidOEu^ zS9zh=UTxaOi$~J62+SOZf)DCj*_6$ql#AW~L^|Uc7RW|qo`Lwnei_f&;q~>Edv5;| zf?>y+M0Y`10);MBW|U&J0A|(ues7P4Oyd#-5*@*x9vvDerHIVMq4eZ@2ZUh6Xut%U zfAvUj{7d3GsYVNz?P{b6(35nKCW$1@>fr_s>J$Bd2AvN-%cKwg2*D%jN-A&LO=Wev z*Yvl|U`CpQ&Cj~gOcEj(?h6#f!Ri;mxN8h3z>2vu$JM@F4;yg;qiiLWJ#*v=0hy18ua`{)h(*q=*BcV68J zOLWGtX)G|=j8FkMGFu&A67b(qkh$NMZXzCsCE~5+e-p`#35zm@GSEX}^&Pd8}%qaHld1db(gK+0p!*8gDO8|DcJ@ME$TL|L`7g z=dK9ElV){EUv4V&ttjVOa@(mV@SFlRU+lOr2CRD1>b0!FSZRJBJ|q|FTMGkc?`62l zxHLBCdE>HQgt;Y<-Ci0umsn_WIc!&2X;+zqp>7&Q?3uCxlM&<}3O(D1is`#z&xOtk zPCJT6UnQ~##;uXsMgg{sk77ttSRjOAIWqqV3Q$Lkl^L_ASSU9!^nKN?6&Xn6yy7m_ z=;W)-!ZU2Be^ZzOdWAyQjMs~LmIs$Um13>B(8(2`gnqscxt6Qo6C+{>$7c#p_qv2P z*S?=8U21Wym>k>dM#BWQILkt=;+ZWpoddd~5K*esU%?FbhWezS-~A;eTL^ABo7|uD3sxE0Wd$GCNUEHz zHrM+jaYx$tBel4f>eZz>gp42S{nB4<#Yg-vYq9@AmXo6EA?Y>d07dLD)hY^M8)tXD z4ZRsC7w5s+9)LT?P+Xk9EE)Z67P&Bbk>dJrL$+pb=VE21iW6!_qaRBZ z{V%!zg#qJT_mkqKRr7Ys>^**6C$%@LE8fk6Q=XN{29hmGw}v!JJ)4&Q9Hx~OhYMZMnwG9(^#jI?IMj8{FvG?kFHFIgkjt&vg8&0S5pi|Lk`@#nFboWl<$%$QDp7dY++V%cek}VBB|I)uZw@gAj4m%eBa}aiDq}4rRMQ zYORwbHnbc->Tm{(t6px1l!OObfi=mb%-vQ^s(sm_VF!Z+=}b$SNMEd%K9YQQo2Vs# z<1W=|l7TaDlw3QaA6UPWd(}o5ZYoRwT1nI1cfGvN3NDs!J_8o#1KmG46N$Sk3<1?$ zI01V)un@|0BnlB2gu(FNzJEGRP8G?MJ6Tex{Q6EG3TLZNK2xvlF&UZ3^p=iBw>frj zSLGrsW%|Ann5X@plV2i?e)7JHLT6yxI(vxL&yUk0i$_?CEI79Ydu-IM1oD}b)0uBsM`YW|cY!`se*FQBoyzBt!!=8jkuZ10Atv8} z?k1>M-Jz(csG7(oBVtl_FfKYaj|UF zWawu|$7F@lp6RgS7VJrm3P7qd!!nd*YAS(T_`q&OFLMTjn(}@v$;jfSo3tzl=x)LS0TPX(knwLv@KUy(sDjTL77M{R#ytM zBW~4fe%S?yw@XwA3a2^Kzd>iCOkGIQ11zyp{RwE^Pz+4;?-pXw)mKV;-klUQJoyl( z0K#6fST{CCeK@$00yxl`8_pa}#YHdg@=1*Cig%JT)0_FrUhmmreNk(ySUw zug&&5f)D%azV_iHt~`6a^55eQW2j%HNpc*1pKcD4Hl5@9wekl1>um7>ID@^fs) z&{=B@&1m9lM6uo-91c$mF6+_6?TP(bsayWWG>enj`qfV+MU=q5v&D{T^hI~nmm<-y zKXr=JV9O0J8lA?u)U-pim0mItrwAY(Tf|&HBYKaBqHLb9k*NFw=D+X8>p8Y5d1)i?@5;%&H&PisA@c zPAFiwNUin85xRtp6ozkGKV4q*k=AB$n;~F=lwpt$0peaN) z9qJE0u@ln)AqU}IXH-d`86{F!Df>vFoljWU9JbP<<oX|J>`iBXK)>xO{SVbI8kZWdQ_y4zckS zI%K4lvDv^l4)*95(@`{`C;_-VF-9I)){-^z+M=$V;i{QxU3JyG7~x07=EUzpqp~7j zolpbmwZVCJLE-TfNAc9nYg0QV`ye8pn0&D3v0$-ERVtl$z~7D1zRRNn{)r05U1`iZ zQK;Wo8JpFq^pM1ku8NfG? z&(lli%caSk=&}3O7c1p4x6RhT0?t=@Vx_3QloNdsV2Msh#^R%c71yV?IWaaFK+mei z&-zDM{)MQ{dRmL_8Y{a`HhBuzT&kkn`h3?^Q)D#QXj`uXjH>Vmw$QVdUhz2YBJr-A zb1v1GqSzZDVRp4q)USsJG1Uc6BJ-~g8OuITmAeb}{R^;$r^D9dbwwfg_4 z`^vB^*QQ+q1QDcLO1h<_L8ME%Q|T_LhZ2EIj33%S(iux|cgFDsYCpiEY< z=RWI;H-fl(U2AXBNbb;FvnXP6odJSX(w9zT%eK#d_AZmr-jRm868FgGChqgE%u+qs zVOqHKDX*6lNqB-7;}uE#b{g;SG`ix-QU2JePJ{$f8COrGo9O3YBMeU?F%{w6h0!BY zU7E#0U~ti(a41&!IsVOzwPPF)tANmqtawUbi49+l27OJbBE$4z&ZaO}E`0D3QhdXs zR|j$pvcV-Vp&wm(D6P0rD@q&6Hc@E zw_@Wf0lf#f4(_#b!jOvX{E`&=Ana`@#ohRtJAr_+23EBCdy7}RFcBXOXm1^+8q=>S zJCOWu9#>HSZiISRlO6mC$QZ&=&Pf!(<3PCW6gAp+A1Mv4O#BSoK9|dHC3+lAi{Np( z)xSib=y*S{!$+e=d>#AlC`Ekc*sqdbUVN6pKmFUTOCO%_O8?%$q3oENym9Ll$2n49 zo1z!7VEArwo;}itAncnWkq&Dx(qP4J+3EzZ1RW~qB(K1Q|!B%dTwvv zypTl?I2EdD?x&tEYyP4{Q3gj}0@>5s+@G6`-ox@APeu{A$9VpFeIWLQ-@)?XYV%3= zO^NN+(qgB=FrZbudU;+_cDOSVy|4LTjNoVJl|Zc5H{&*(H>-PB#kZno?O5I>kZr06 zG>77G*(T1}fsR><9!<$e!+ThRzZvSfVNpPb5tGpwU$I$S)-4>W2JM~QWGQq`N^qX5 z;sfW3c7GTh!VgI4$EBbb-d~dS+gSrP-5Ot0S8kG>*?3RZ1V^Z$z`fGNR;$XxT|s1d z8Ir2+P#Si%lf_EkgK5mJ*leXdmG(&*p|cGQpQ0X`vW_?wxO_E&6D0JJP~67TL0VQT z?Po$co7x9_^|Ps2(h2XUwm`vAb+~4Q5VBKWC5Di7yHKf4#6kBY_~ugZWCkBzG`H5@ofz)40&U*5XboikMEB;`-d41YiRG5a3Ij(9On6*xYWKL)#uz ze}3)@?d1V;1W2z(RRsin9Ajgrp>UT&s<@BU@&@P5)z}VVF6*D(u~J|9qSq zp*pL+jV;Ru1vd$$!RKxbgVsCO&z*};HG;t59s#R2?sH93p z6UD}=+_u2B4|q_vkVGWSA)}!Ss=g{|&~C*eKue01d(^8{CB^S222)QFmx2ri4(R24 zd!8EZr=E&tMao<(DczMo3VK=UT)k)FtE2*iqmWlX@^Ir3$=h_zkHeMXUBP&Ym0eSCK;|M8n(?$Dt~tG+pkxa1s-s2LNCN^7h7 zmvcnR>@{?&=9Sq7<_YnGE%L`Jus6dU5y2Ba` zBnt(9;#OFmDVt#%Xus?7M;I*46+hfWFX0|pw}M!zg#*UyflGeu&g%Vc`m=rc=q^ku zkA{S`FW!M=c4LI<6}IIh8M;JTQcdV&B!KLOD>%92 zgaaQ|dwAwe8}dW<_rq74M#us_=&{B{v;*%ZHSK>kRF!WUDp(|n))F@dlUMRid`HD& zp+E@^+!)I0`B1SnVaJp)ycc{tT%68X+(q!G|AMP1{f=W4I)g-xSyW9I+dLl!%M8QMWpDcqJkNg*0xy2;}uT94)k(QH~`$O z=%FXR-90$)e@rv=)EEAKSS_hozT~*q+j$;*?DX)<_loZ{glw`qGRi27b-69GX@#x{ ze##^hSOFg&aUZA1OWQKt^o1jt!xG6m@*ks0F{N zIe&WFdpaQ)Og7ZpI*tw3+CxWRyv>F7i6qGB5c}a>||GIt6r(Mrn}QsAZ}z9R$$(74&BukeDWaQbX(ck06Q@-}l2N4VM-)yKoR01}j>%o)BN(;=~1q{W*(fP+RIq}cC zNIHtzqx&yj%Ab^`@PfS)nrThFC#Qdj0^81l!R*CrFuOhNWQGW1QyyoFKAt_ja@L(i zyQKt(FAt8Dz`$Jai^GGyJ;4GzxKa!byf0I;hC}p~i|pk>0mKUteuMpaDn#+DYYIb` zn>2K7@1NQufW?Y^!<$N=EMNQ_wC};F?6$1?~?dvO*M<)Cj>_{`tWs#4n ztPB*&4}vuu#bbSq&Il~#$G$fPh^Vhxm)+c~Bsgs3munvOOR1IGWHVJSLmqht7$=IS zJ8Ql=LUJI;M=qcz2%Txi4kjlAR9`Ey6G$1zHiR@4GDKkWpDG2qn{0QKW}3Fzmh_$tZBH#}XNuyrwCYFb+wjXM&ZSWWlc zY`fsz3<}XQw=*=_DEZlJji_{dmEk)PHu4h+OMSb+M2${aPYXfxWy#FWmJDo0j-jp5 zCRja|Yq{^qa!yXpMd^2%$W_r||aKR5x zx2ZY+srMV@5O(8rbr`lg&eWN_Sbt7JweH$P;g993yPy|J;AuVM#Wnek2ETFH(||Ey z&m@`7E9fNaMWl9p0qx;(qT>uF^Zpv;gF}r{!{8u7Ugep@%b1P3j+oCAoehnu*V;BR z0dE%eJTJ8fELTqdy3%_gukC)OG_VTqnPaoiHhS}QW3xzbX^YxQ;_N0QYlu%6 zRUby=!Tm&y4a?_Vv6OXG0AQvqAtm!Ab4s>_dNduWVg*lF4+XP#kjHA$MbjwJJ?Sve zD%{-|t2ts~Udk7Toxl~8TN%6%-64%+J?}U)fj#+r$~If{@@>Q4!84*82H9m9i5eM^0s1wZY*4GheS8m&hsPupz+?LYmVjj%C^3k zO_^W@lTl@SoA8q1d8FX2J1B2%!4QL)gU@c=ArF1_oe7r4={?K5RO5p1RKdjA=W?@l ziouh8EZF$1uFl96qszyP8tsv*%~zi@kTnW9%KeA)lZnc?`wQ&}7CQ2>Pc@s;99IQp z+)hm*v<2yDxt_@lUM;M2FLHWJXw|BV<{xe;?@oNJ%6qz51be%*aq>b+P?&AQ%RS@CQ=Bnq&`TE9mzfF+9bvoPi zIChnZAf8pd<*+IH+%@Y-+bh;7JROB1xSh9jsm+o%_X!R=qMGUv+&ODLo3ro%8lcA;?utV8Nwm~-KYNGlQLe`AF`GCbskq3z8|}?(Gdi=%X86Xe&xGNe zd;EN&Ue1mWcVHa*=u6qqWghv)CKX4o7(2vjgSUV>@j~Uq;qowLX(X-@`}9IK8{(|- zc{sEAwtO3;4fTDZSI``LMfeN)2Yj`v)$YEDwimB;#?+d7cP}*-%vPyz1X_<)2sev- zRE8JcHdI`j=wKSPa>uQp8xn|6%C5uUYQI7Ze*f|r?z4NUFZZqT;XxFtZRO~%|7ay~ z074w~5v+6^bqZvDjl}J+9R>O|y16!*$AW?o$2}VFr6J!(NEo&HX6*K{PTXN-QEaJ8 zxhhSa?UQf$Ts`%8ok5=c_Blc@6$R39{tk9#TG0mli1vzlt!2GE);Gc&ty)8=6@>B_ z)icuZ2Zd})i;G?BY-Z%$=M)Bp-U!D`?2sUF9yG5Y*Y((!$7`|hPiluCar*)t2E%jJhsiRe1{0R)O6cetzRF>YuC?j<&51P!Q0>+ zH3f4T^@NLaRSJ`j3Janhpgd?_d`|F|Yb-TIoYiXdD_{y5)p?+w&xl94Xpu&m7jxuM<=AH3#3n{tB_C}{lhzq8Mg%Mk zWdIwKqu8*kv#A&!v~9BVwM4j~GHSdFNv*_PvcWj-Mfv_HVXVyiSrXDN>sm&^aWvte z)D2dyyQPNd=zi72qr)>;r?tkJ(dz%$o5G*4pZ5I370>HyQ8On4Tg z6X|k3W$@tkWsu@8%lI!w^7lVsNOWJ{YqL1K$Aqn~O2p@T4M|grmL#Mn@jB2bWf*E{ z(I*zw+cmZkdo3Wss>*Z^Mgspf46chK51|}$8Y+Qc;Rk3Q>1x`=L!L(a&RiX=eZ_sH zr|vy>yS1M#3_TJM8QAeuMPLqSpn*L9iEISbWozO3-}y09VBDK+8KSBes6pq z^{Xp{qF$uP&tT9Gf8ZL+x{L#iZCvVQc?vTxcni$fbPSG29bgV1HCg2k4nYl8l!wLj zkv8$TVtdpP82ScP+No#N96i_kG9E6b-n=cF(+jb1diR*5V|V#2_P=_Ie|-->M$fAI z%xFs;5vTK^t`1;QBa1!^e3arHcxHrcur8#_Pgx(a^c>{tpNz$sJC|vDL~hkcet`rV zf7wt^w0IsJtaI?WB(~?2RWxj_9DjxnOkZ=|{m1X#BmeQ)b9O>H3d;gw*TLxY4~07X zb{HR*3KC<%fH|ECBUHH6&dw3Bl_zsKF5SENcze`rY{G-`3c~4nLM$X5eiS{c!I=4D zv#a@j;a|v6dNpK?5@Zb)#(0W04OsYkQ1#!{y1-77bWnA`Jot-Z{`8eUzCMftW}c!$ ztc+9=%}5fVGQyY_<$0JK$99>|GiIo0BGzI+g5jB;vjm6+8rNU9>1}1Yy&#R>o&Wwf zCw#Tr9Lj7z@mePxk1e;GjkoZ4gc^Fs|Lvdu^`Cx}K$`Y4h`g)QkDkE`h+sT@OF|r` z3%;G~n-7K!Cp>ciJVV=b+R-`wf&p{ggWII>);4ESy-!oS+6tX@$GZ((<4HSqQ?vi& zbN=`z*c0HFK-_%38%HXcW=$)+;fdlEL}h+Y@ELIvJQ%uPghV}Wi=vN#su@%(ym7?d z)~)Y^{Wp^A(=GDS>%Tz8zkUSt(`4^JpS3R?GW=3Uk4mrlR^p`y&Fit z@?>y^yLcVr6pG`gY{+yj<3TcZz-iWcBLCARy?M5e2B7j@;nCUNypFPX+%U7PQ>mK; zd@hppwL*GMLP?Sa5zmgCsN7I!|tJH9q+Qb5l8A@pbpw{EeH=dD+pLYeD*l z;bcIo`qLR<&?3PrwoJu-Dfsn@LI+2cMJIQ+NGWx}0g-b4E8qB}hziAKYb!1@&PXnsivxMxhw1j>iKx!G4lgcSKm{ z*uwtJ^xh@V}MiS@cG%+)Gv)AQjtzVq|GLDhM$*$ES4k#ICuin&|6YSMF_ zUt-&1m?OugfVZuXzIvD3rq4s!JPawJG{Ze9S3cWt$*%xRBo;bdMf7|}24>2VRA%-E zN`TqPg^iB~T(9U4kpz7G?!P<08vf&EQLSk2{<8v#i9t^;rf`NVp>nhA=vMj7&|RE8 z@6MyNyWJH%=ewMb1=AoPI(PIv?8pG1Al!2EAfPY7{($E3I@6ypD#lH9s;U;4vGub@X8RS_vS znxxdD9&iM*DqTiVrG-PkMWK|>HAD;qeV6sFZ_TG7lxWiR-&?IH7e-KDY~ee<3rq3P zAw3V1hQ&1)lE-Ace+Na(78w5s*Sq;kE6dBBZ9UxP1JeRq}c$6x9lg zQMf*rVN`MX+f5O(%Hu@tI~s-Y!@1hj5pxk)J2kVk*rkMJ(H6xE}J{Utiw{$o#=^|x0 zuNlywhGBj4KQv4jpt9z^;GkCTo|g}2?C^}YQ;bo=33&54yB4q!?ZQWTaw#Pme~W+$ zDGiadt^a|gYGcjtN9BQrV=)hXHRP*n)ruy*3L#QUAzK)U46{^z7-AFxaOi2xOxo%1 zS8-^x&8I&2=o@B* zmnRrBFVkIcZH(p6*qbU>bPt>tyB-Ak-u|CD|tUR8r!MxKjv^lmkfjm}XEZnx)%u-`s&C6lX%GbIWgHqG+WvUgT& zjYq_&w_zD5mNkUu8S2TaWCyc;`44>PGiLc6jPv~mfErc@<8>ccC)Jm3PwPY-3|AyZ zL$peO-KJJ(gAnSk-M&MrhjZIU{)s^(w?&ru%BXLj9H##22T0b{dbx)FLd+!r*lN!c=wMlFUxVK2?m$QZQsIO76yIq3tk1J zJYbX>SBZ?Y-u(8Q_x%sL?{Fx_Mt=GMyJ1*dt|AJzCm9G47HZ?8?7??b<2aEoUk z)XRP@DM_o>To4sUqC~6WmxDqYCZ;^fFkWWit8cJ{#mhkMGl|K;W)F;Q7XVK}xW+*~ zrFW#nCE_qTsbVSE^0B9kMV|+Nj%nh#qp>hz8GT@^vTgpr>W^ocJ|X>saRs(y0J@{; z>*ebPw?WZrTZ7CJOeQUvn|{U(JR5I~f;{-L-*>=-uD=RH_FH(5(Wo8@w9SlD2@u=- zorCgsX)I#?{XhS^AcRBr52%)#D&4m6CR9VCs*hsv3gG4d&c=nVQ`v4>DEpqHX*uz4S6 z;N9520niB~*MGM15wXtzfgZ@_BejUS6C_$+sK|JdkpkuEd>XXW7W6l=g<860sQb@{ zVnYu8xZpGntmXTGK6${UV;~zDin~G)O1b|9`Da-0e{w6p z!^+FcN5nq*c`di$$xuN^B3`CO$CmZusc8TCS%UnwQ3##*%!N<%kJRL|2;d6B_3_(m z^+bphD?j}4o_^yFEKmN)1m5zjLIZw8RoXnile`iS!1ZJr zWLNjf{>XzQ{pX*5Pc(*XN4mL-`1+p#CP;2o>4QNB?tW-hdr|7oaZ87b5^ZwF&(0SNU^SA1Q{_@-X;G6$m*VW1%cibCwhYaj%eW} zzQ^(_Pd1Puo8XioJ^a&;_%YlLq4Q^u5%~e*JUP(Bd^&JEodRg(?|B71+aH#sg_@}v z_y56+rU?VPUM9x;@UKSDa~H(n@F|$|KNZ#UzWz>EG=Sxly9jR?^Fde)(%M*c0vJ3X zShy8VA_4Q`hj9KM{1BPmYs9yu9BKN*9Z$+SCApyw{+n#qf2C}ml>sB-hb7+T`ByZE z!Us(#enL(@$;ilf*C?<4D;LNEPp45XZfm>GqfS5DUP-t}OJ;YPtfc*GS%>TjS zqR<1klqMWu^9Rm&Cj%tr^pi2J*Jm*1)SzopVX1P~Bes+)g@Im!5Hn45&1Pk3sk4r; zF^2!TG zn?Szbww1qFSo}6B8jA(P2W}7+|E2d|5%#}EJH+bXaYBJ)3xD7o_!MBkcGbv*y0LX2 z!pn@RtCcYelLVC5YRLNwNj^v(>e^0wn6u1$=ijwJ!71jGdb#?&hlJ~;l^pZdYwk+y zI`vfE{#H^kjLYt88<0tQc8PQmBscrQFDp7wLt1)7~Iq4ud8(d>RAEwW5L3i_$fk%EKfg8b%! zGCZkBN4XoVsl4wT`E{!gwQdOb?6CyPq4!7+RaX}Be+gvktrAQG^#$A zh?j#!`J-qC^4DAN?!S#!ngEa=Rx0Lue^{I^0I11vXxmsMMMjct$#6-RlsKH`r$dfR znGe!)K!NfUyYKZCQl)WNe_GVk0FgroedshL zqqsNqK08w>Z?|FkVmZO8n>2Jgsj+XmQao0Yk*16J`SCypj#ll_?tO+t0z$~HHs?Z+ z!qWTEtt7)U^Il)n2gD&}V@*4JYKPM^ZhHw|Gxrx((|jd|J|;*F zKgh27@+~ovlho+FA0Ey8s+n*XakTawZ&dwHI*l(Zt14<;50127#ztxy8VWn_MT^@m zhSMh*>YNQLOmtc`E& zev{*6&Gi$(9(XO?w{3f6&wDk2of#42g-BXw9YB9L!S>3Bf^^Vnn5$Qa)SzzvIUmbW zTtimygji_n~%yN@Kfk}pkgOqG{qJ^5-zD=rnx31TAhBNyD5C~Zo zmbweE#o8ZIc0|Xhre{!MK6@we7nA{JAe57y{x+$8_n_weLkG?u<{d#41Zs%1wx5&d zgI0I9DjzK3Z_Jk07cx9bMakD=E0`>UAhy=-NITkfE@?3q6YbV=1rAi1jWMW|IwY~0 z7oFVJHwg)2x`>oniqY6P-g=h(f?#p4>B`Ms;ai<^>EhBAez_P%CtZg%0{z!;vYtF`AX<%nboGVATB%P%-|p%Lf+`oLq z9Qu#C&v$1T2?>c)+TagMrRm9$a_Q~kq=(xhq^4&uJAjrM;pOdCspm6GV1~4l2J6h_pi-2l#d7S!RaBe#7>5j zixP}HZ@9S@RXaZ@jCjebXlfz6_ED!%clF3Rly1D}W%^g0qW*?sKuk!`8OoC0{7AHn z_jXC6!aTb0lOM^0PuGR@e4S&T6xt&*_1YggT)kTeEgg*`IaqchpR6z%e_F{a>&y31 zO@HqS*k*k|X$ACeJeldeF2}~RK;s}l`v4| zby);^<4bX+olgWgFa1cboao=*1Z%#&#;wTjv1#w_F05Z#xw$&g$sFozAFt3Jn;~;N zs-ErB8Ak95N{dL>p}KR9H+11sn>pZq(RJ6&Dze(HF}hYD$;-p1z+*N%e=>l;Cm$J35PamOks0iZ`SZxd%XURGT=NtC=4J*<*Y+`6r;|}O2fhS#bPtE(fK+$7&aQu zW`?)l5s~&4ld8k%vRHH}zj7wrS;u`VLFWNPx{r@V%5?P8TB$FZ{%SraaI^#ILR0b_ z?E#l%PX3ys<+U1)DO>5tGv&bFwV*d%o+nH}G>Ft1T)hhmsaY-S8WHiiI{408zOf>+ zElZ+nN2kj0EN#Aujb5NFd~sM?Xz0e%LwPBS+P)Bq94l`1g1TP?>0)b4mauAP`>}td zVi>BEroSB;rrzO=%q)vT7djXIDOjGws_4mWBOFEA;RjEY#0!5}Z|*(ia`Vi@)c=eX zt*uMR>{2~1tYG$TrR0Lh#o4%|Dfz!=N~m%i@yB3)<>cr9G($|kb! zrZ>vp9eMk%Fp}exL*(m?QQ->9X^L1KoAS%66aIz0jtKfiQ6dKU9Szz0Do*<;Q?XHwUDOxsQS zq2&zS`^gbPn&%=^O~OU#Qks;_s*mr#qwRpV--BW#nTH-zmJZT8_)FU^mmWGT04vJVpd;^6y`lW11}B)6V;d7l#C;9+=NIXiX5#4oI1-*zq~* zIBb6dgT!r;`wEZ;`QwfnqG(k{D`5F}cI-~J2A2jOSfsdvdB@aASA>U+{0kO2Mg+pl zmY0(Iiwb?`Z6m9Akl{8%Htwvc-|Km39~f4i>m3Wmir<#2> zqQQ7<1`}|08Or$+?$*f!oSHtW1NwFHwF@#)ObgmEhlj5V4c!4>#hv4$Q21x|S-=gG zSs@y-YpOQ-HsJSlmj=e^bl~Ow#*T+XWj90E5Em>zVb3A8c>`oxxsSo#OzHSgw6?JU z&X%I`EXhJSe|p|*yDY&rDL&e~aWuFmij954apIGYgGTvCt{kd>Pv{fa-|qUAIKbj` zYs^poqQ|#xJKp(`T6=ckfw$ez@KJ*l)u2T%DD{?M-A(wRjjLd7PjP$4MuOXhhlBDUTl05cg-idczQ_wRAw>sA)C;=O2HF6BNj>y?j7mulRsF;2mDFv%{y6 z6C*jfriYS;Ooqde$AXSW^~dLAxU8r5=_hh7lvFx;*tN`J10Q!#t!o00!D}fDiyYs^ zcjCpvYg?u@N(9hh4Dzf;dx6V+=3c%NG=4cRky`_L0<6@b4+;88y3YmMFF&IdgSh|H z+|&EBF~*qAn8INPx)B0Ja=#xH9&T%fUGFv>GM{zmRldvBk=#HKOGNvdYh;O)-t6(7 zx4~yK!??DyS1#UY5TrsW93x)q0uz2j$j8}R&MR6s3%EhVdh2DlgC`C3Zhai}k%*7_ z`;z<6R6BbrXF#vERXB}Op4b({erd*K`-vnnZK$Fwszed$=wsukVVD4#j;gHoCFX+# zbR?!-XE>F*vx*-r6t10 z{bF!%U)YvUH0+L75V7tPQK`?5)Y9$-gRv@aWLLWjm%A1E_!vbWjcRV!@H!o4r_r_e zq1K((+<%<7B^WIC?b_2yVEUYU60D}(zGv`T3L@eFiqoIr9sQN&{>2v}qxF=h?C1}s z37{ec#7?ap>B=c;yJgn`VeU1-vx7eFNrpz&IVIfl7KIyUq25i`8lDM${<;P}DUP84 zpCZ>YgcOhM6_zbOc*cgq03N1IlawpZi;E7}m6U}%I4o9)cCggdV6`ZFaig&DsG3`C z0FRJpYPJAdIBX8TLk`}k;F&;MCuL>qOF_6Zk?{(P+A{9c^uhwzU>f;y3C#Nx9pCB< z0?YWBboxJRvF*2i79|=?V?=^%-fygb&u3q&87j8JOCm6RnQyHZ(rm^+6LS zj`u=@eSL;Go!M89d=|pZ+~06*nhXL$9@^ZmDT5_SX4Si2B+1$6r4`?^u0+NSmb$16 zDf&G05b5RZ;r;|lr$~6FN%eWEdSysK%%@T3Ceo>k@k4T3Q~N26w+;fFso1q ze<#kIgt6eQ&V;R$aO6aIT5%HCtzwQTleQgG(Y5i+gjqitOkQ7K5Uq_|Q1IWV92iQ9 zi1=9G^BUF>?0sQ%vOQfGY;fbQ&TTI*(QSIJR}sP$M*`+dAB`v_?oOCRE>51!s)<^z zRyH`Ey7oS21)d-dcmf-ll*y$*$^B40ri}+dg!?~(`1H33%*#C&5)d87z%@eU0vDUp zm%jHb@coHNt*-*S}gTY3kpYvv^e`*sTmBggb%W*`uHnDUk zn|7YrE9m=UBtRsa)kgeXdV2wp@&udCuu+{gERO3hsatdI2op061 zt3X5*udbS$9jR6KcPc z{hxi84u`nnO+DfEfq$!bhnOvXunZ@zZkjsEnoXE*9)SRReNd2>g|c- z278majE6?2!gfwqBiNf}@EF>M)fZ3KY}bTKKVHyGUaQE#=<>Man z%8SeElbWJ)z_EXT9vmR{Ed9~6y9&Qrt@~SEFG86J1Akx8AD};`2guKGB4J<)VMEjD z?ce6?Ig~GpGWq%OU-H$S@*+Q?PUCmH+Q_kic0^M`xSj{?fpOQDhlp4RhF)g1j|r3Oq|LrB2G$Ef;2c)Yz>h--E6Ac}}*ooNim+R6|x z#L{T*)K~6^3W#RTl+Toi&@M>adO*}oz zv4u;_W=F~O);DT^BZ{4u7Z*D=W%v%z&x{+{DH_P&*mz|Fg(Y4zd5e7QU&WEk7sH8X zee9FugJ2n z(;zmHcCyl{^rHUB98!+F!H$j0gzqhs#g$G=74wHy2F>-^PlI%v`e zju4{wT1MIwqKh7P*alOJNB7)^i?M!FLak)cXh`~!qd=WLR%>ROalI|b#7g|?{K$3d z?gPLLz0-vuCzN7TOy^_sxq$$2DjAEgQjxEp>plj z3Lbg7pfCDTib3~M2=~RgzkQh}s{Yi?XVU#`mmxQ|TAh)eSMEt#D#bp8zVkfG%aLj< z8SzIS7$Y*$5TzlS{^B%W`WJZyTTVM9**&dca3xlAX&GWK# zoA2qPv%~e_j;=^r2=L6BA21?KD#_{anf2dEx$2cb(6u=g>e;@!Sa90{ySwQqlU-{7 zEUkCv^?KEt*mt$5?k^OE+6iKppgVzfIIIZoMBertXM)x;6=G-lEo)M42fSIgP z$%i#l_U*w9OOI?<=WHd~hcXf|UoIc?EqByar<|S5xcA+hCUK^GjoQAMyr`_2-lObV zGfE`qjKNu`cbthhn_VVii;7c|%kntQ*{rp?JL{wk3z+M#Uv0ST=g5IjKk%3gQZ}|3 zH?$O3tSXGwBEc5YKHf`5O27>@P0>{dr&5I@?jkQUr29M`9Jz0 zGDsz-p6ayX?xDR}g(!bgr8Y`*_M9Eesz1p((zPPuPspLuIuFzwY`xK#u`}!o%T+oc z#gKB}AaMSMP`e{VQ}W(InCVHoCeG~JE5hi?*<{mP_v(P#iKt}2#;$(;k#k55=P~ry z&zV)178a(XF=Xll4Zs$LJ@KTKGc)b1jy*@;V)y!jqd@+k<~*1QO~5B4wVn2`VH-=W zYS)D&!_0k$*Rg{fd#7Fp@FdGhMZQ!G*Qg}kL16jF=cgF%Z5J9$EZ8W=ds_T*e_|t2 z%5F?^XMob@VbE$7+L$NPb7B~19&dCb6m+EB$7R}N>O)mBW}07P8l4?5g`Ef{xGF=$ z`|gnuUK$?2MA67A_|SaftI07PXt`)XXUQg%9L%zSYup1!OXeKeNs2-`e%{DY8BFBb z;O^}<$=>|WPP~Y4dz(7Hzqxw*^MZsT{N8yUBg|GVhYatP<_`!LOej21ePH4+O^??K z;x|&{DIHm-u%sjl(Mg;e*K1TFt~UYfV8;@vTRz;)mdpgcKr?m6s>^IMv%R|ed-pB9 z$4@Vh#`tY}ont?CzUbAH4d7A%U5-n!$))N3L9T{DGRD>&Bt4nUos?bCHyboLIXO2y z3>Dvmyoo7}enFZn(-l0VS6{zjUp|id2=d%J0R;{hV7chokxicK;U3)u4$8uio>GmD zy?4$;b*$5Kd}GDH@V=mE$C;${+NUhf67)ig+cqfL9}Nw@-{cv7&wo`Wr0u+gbk>4a zjPue5A&k)dW8oEw?eq5`jqFau7W`NdMx;)>daaIuiNyO{`+qxo&=mq-3xM;iL zC!9%?(}q^LH*em8A{OG1FsWAE6ZbOAfqDFm6Wv;-?g9GHaCDhffRf$96+PyWWmtR*1%ZR_R-gsa0Rj?3O>9{ zvLP1J5?m?vsXJ6pt|lG!T35Q09H$w%9rVX71PRnIuc9ed{NR}ZVQ?j>JNH*5iyYeI z5m8;JiTPXOe!$qUZ$A{Qg;WyFB`?6kzSeQ2qboXZ(;xroluB0=HF~MAkQoDJ)@nQp zGjoPW@H%RtV|PqG0NGp@R9;|QmH3hA$g8dnqs z*lk)mZ8o#vFa*plU(@Xs6_WM|d(eeSh=jjsR@hm18xtOb#q$g3uItt)xAorqIzx>_ zFCeV0u9i)!&&kL>2C;6GV(yBqd?#)%Sb;nWF!vMY;_|+D3t{0JdYwe4H7q3=rT?=pP=qy@Z{Q^*?#?^;);4hwFo`LYQs#hDd~r&*mk_9{5#z-4=q6R zd}N{g%fZM_K;x_}$6I5_08D&%hXUGe!r|dj^jnOu!TrZzo~0G?R*OgueiXOky8;D= zvlp{oSgse<1SV_Oa2MURJ1&fT3B`g}wo0D8It-ou z-XVwwVW%^g*=f9ytGO!7b{iSVQ;BpR9gtpVZzP+gzvN3%32a{SUJ>msS)yG;$V(ET z9@I}W{D$cc19%~K1w{H2?*3O_XzL5`>BXc*&wroAFRbiE{5IhyBMvZm07+!hkGMIh z4~#}yY>Di%c2plVbJ3y4<6L-$E^}nY;n6%*|)?X#S3v zemUWNaJ1iF{BX>kXyEjHPM!jc-Rf1<~^wE>w}3AoK|6^O!IJK|7k_(PBZx!nGI_cy|`fx7vrmbgDCVQ*R(pSLyMT-Q>PlY8_2!T>_vpG%Yy$S5fp zY4k8phz+{4B;G=bgDZgNIV~sv#S_SJja-aIdj!W2DB6Z|?^Frz@9!^ujM*|*N#ZJB zs%DarZ1zD)l%|Fwc|)B38iDzE6LrqQJxmAT>yJ;AO@k+!G$RDvR8?2M%TQyj#u$9T zZK8iot4KWbtP%#_B4=y)xBv$Dyl0P5R4Z+2%_ozS;3cUK@o1F3hN+LxV%6xp%-xb0S`niV7l`o|&+uM|@4bE7FupNECxa36Fg_AM@J zY$MdX2L!qY>&?Fds-Mv<8PU%)gUMOc)3a@CWoYb6{PMr(V4LT4Sjvqrlsd&^4ouOC zkp)Eb;VZ5BA_6d3Nbxf;jzz-cN?>uJg=I69WMfO%Mp|!2j@VxT^e&}rmQ(;J+e$zn zxk(o;3p#>fs5iX*n1B@;eLk^a)rHJ|O%{lCMa=MsWyI3b8Vq%j9kRbY*pYJoMHovH{TzZ}P*1o$kwnVvxcu#IwpyX@ zQdgEZM8sTp%)1@|2G^S$E8!SW-Fut^@db%aU88oJS%VpL?)A)xcut7-PJcwEMX6@Cy#- zykE0dMQb>eebtGfuWz*CcRl3fNYnCBm4pEGUgP1f(+YeOz*4dE&ENsc;bLu2P|0zl zkS7?i$mDQGK;+ft-MoK=$Ion_{h13ov<1bSvGr99G0s2i!H3rXn$JX4;bj*itQ@Rr(@`8x~Q!Di_poT z#Ko2>YE*r)HohW+-*yH1Rv6e&K|EH?sbYxu^_qvMp!0=xvZRFEMFsH57Rh1Wlo%+G zlh%Jy?D|@?KaZpX67@UuyK|zYi=Yy@&4%&6*!t?Qthc4@Edr7f3JB6jNJ>d}w@8OH zQWDaQ(%n3O(%s!igS2#)2uOGLw;s`R+~0Zs+1KU1-1}L-wPwxSbI(1qQd!nR5QmSN zDMZ%d+23Tz-ga>|ITvL_KPL-2*(6&jSfWTv1ja!tAZlTebRDA2lLgBtTH&GbgGkeX+3LBU*^i$l@%t6cDw z@lc)2lLx;W4)hS;O;wFxGec-%DdOYzJ$8Y1k}fHm5%#KY66PwUr)6Y8o@d3!!{7Zn=p8ybwwd$Y2CPz` zVnH0?>4Y3u-g^d;|MOyhOx~xzut?%L&Qr=SNT*l0x``Z6K;A|JfR%b^4`sOHs@QB1jeUCO3 zp`u3wZMW{J@OR;7k5nhkV9m@}RP-p!|L_ynpdT^l26Ej%VEWOPsP^~WM`r;3>W4^@ z)9-)TAr8VG*A{?w;;}_@=0^Dtv2GiEu{t(dl5skX>TcBPqe{4c!cc4E2ytJa2>ABS zS8WBgg}~RHISzpG z)HfG6)tYqEqX)!eIqf#Z6Q?V|yq6w&ns(UV5%?}H2tz}?@vs5yZ`1Mwof3R(HiJ_cB^50(Q zLj!b!cXM?L2M6p$?G90|e!D7fiDrC_$WNb0V`yU0%2nQdBL76J4{%A>%&v4ucgJ{v zybwhLBgs~&O0gDMvipe-R6_F8?Yz>csjm;o`s;eO`p|J`tiF0>wDk%S&UpqqEUpyf ziq=xbNd-*QnZb^6d^0F7RvC^wz$eCO%RUtNBXV(UbgUX$CdxXMDFYfG6_3;WqHv2? z?Tw8#gugc8<1jz8froqZ(HqoTnLi<)_ypYc_N4b|11W+iSO$uP>T04WPQt^(ufbrS zDHgJVR}O#86bYd3DuGaGRBcrh7h9M&ir_9+wfv5d2$DL-(}gFvO-lJ-s)*plf>MLQ zqDSWg&g~&8v&TVG%-MeUrukR`7JZZZOp7ZMLREHVud-#$oxb1j4ugt1TXjC2vL}k0 z9JHbdO+$s@aD8N`SvYRLl&>5V#Y(=R@f59D2lclYKvAPiUtMfE_ptIDrengJd*A=t zm5|5%YNWb-wk07U@w~;jmgV=s|A5l2O*l(mo1X1{`AmdC7CGnSK3o1eUG!MomukGR z-hLPI)S^+veyb01c{xkl|B1kOK+YTG)ci#&(y{Mfla;_zjw>Z53O8(o7zR|Rn7?C$ z6)b361~XQWd^Vq~j0a$okj;aqQVaZ_I4m4~FfgnYv!_D2kxk?jzxCUVJvtif9Bv1l zkFAj|yPK5GG7%J5Z&BP_KUA2Ea2BM3!FHe)^B1hhy!$G@fpLdz@jw6m@dE&X&!hcN z2;`S7n>bEQz8*b)-K6y<`*~_FgS4X%@2B(J3iFZIMVoe+mW3VUU3)TXuX8kdxqh^6 z*qx)j(5#!zYCS=7Mcx=+n0h-lP1l&4Xs=MDP#(EGQCAvY_Vewv?B}sTi~K5eR%+6& zJ&dg1wH~T?U^X?oX54VN$sUYyMNNw@Pkpe4oQDHI8J6V!Ggj6y8*gJ|CJ6nt2vqO{ z&CFN7_}FQA%&}VAh22WzAasIp8B&mwKc51?Qf3IasKZrDnhd`-G+Gk)rGx4Q7N}Uk zCH~0GswA4`Gld))5%EO`TNJv1KhWJfcsa;=1F5y+Z|9{2UDrrJEb$U}c_p&9xMtc- z9z&g>_6(->G>MB_EFg<`sgnptYQlntdo`%tX};D7poIBN4%lDH%)k92F%T9kudHk% zW|qA5`0dQm{U1Vi9N+Ju+iURVe5HRXg@Si*i%=3|*F)3Pqr6bI5YK(0(X^klUaQ)k zZd21H;^XzDq?%L>MxjBPP)z_~8mpLY=TOe$|4(=fI(baR_I?-6BBA-p>2**nF&X4= zBc_qrFpb4@bnrTj&I}A>Q&Us3-p{;^TIp4RO{BpKZSf+6jL2?3v895=6&U*dH){GI z^GC;)>{~AJXRC#Mz&^VMLx>gacfSS1nTU$kk?WK^Mv(}=K*J}Ia=&3MhzTV+^adak zM|s8ykE9EMei-C7-w1@C{=Q1x#2<*;+=zq_$>_g)pCxTtpqo2_*=J!!olU%ZnTuLlc zrc}Zp#7j312qS=V-g(NVm@1n%JzSuspw{H>ILH}zD?BB;w z1~SaWMla}yDs7MR{yE(Cyj|_1rw)LV5>S5@R(p!-Faw@GOZOEQ6%`%D356!vqn6b0 zzuS?YkpN@f`HT&6p|GThUQlkZ(hfIlSgQJ*p;1jQaJ?~Es-JDUJuY-}wlt8k{Z`sV zYGa~!btBA|c&V(n-Y?iXl{An9QUmltpNckLr3Y4PC!b}Z;9Ut1zWloX16e<#1H&&q zj{(fN5fb=)9(N9;2ukgg%SuYlL-P(@qtQX*VmBGvp5E7 z3-L_yc&w&RMlJCzrJbb+3HEAovAe{=oL>W~D(}M^)CeRdma-0~RhJ-9PEhiMf4GKY z?;T+kYF&Glc9^-_;DB920gDS4uJf;pMFe&ZsGxqc29gxejl5K!hQr-aOfwRJuFTot z5!r9h6A7hBP8-0=$o9Mjn`0W7bQAEM*IGXQggFK;iu3|>K&QjzYcs&XY-|t+%BKUN z&a$E*ghpA`{n>Jfw3k~73u8Eajt9R_J6Z$@ z9pUOWTx4Mdq{F6T)R(IKAwA|dF`yS-<>gDGA}_8@b^B@Qsc(k|2PbPE{?mW02eLI^ zE`9JBnrVi=K0y5x^H0|aRCa@MItSg_;ohQXaw&IvU?@DGkzu*Cy_na}v45<6CCPsnA-xGZ?^i*1+DQcIIAxzdw*D~(U`=`31ia9*7C*um&};J?%84z?p+Lw&*$dY0 zL(-Cl2Zdlwu#lFUrfASko`A!qcmybrcB%|)*NmY8goT{*u zNkGNs^TjbTmwQ%N%t@*fjlAYea7`P{Q({&f2|p2sPDJ3A+NmkG9OU?Rr(0vl;fwwg zx;!1WebH z0zw`*^Fn!{w*v_~tH2%p&z%M2h8LL>(iR;2Z`a-j4O^Vk(6rHu=;_X-a$_jV+fjva zwq@xgl0sJqOpKZm0%z6mXk0M!TQRt_#Oe^Ku9f?$uE%oLh0w~h5Ht;pX{H_%o} zc#C7m!YnqY-nCRGOPQMHK`$*464KoTyNkM3P=m!n7X1Sa0G-h0#^-Up?J&R`S#3T; zVX+vz;IfKpGFkHRGDX!5glBMp=1_cSVMRM$>53GadHN05KP-kpb?ege#?=?)k*PqT zB<}h38?UyYfQ~FIn^;KY2-?dse|Z5F%Ff}aBIFaA9EdkvW`aVp&`BT|JtcnIa`1Dx z+UpzRRzQpOfdnP&vELGVZX0gVEtqk&&1Mh$pw;Xu!3O)`rJY)Ev2luQ${6e)7?VBi)R+ z2O&d<;Fn{763i$L)BE7T**wr#Xd;pjrxS!f>pi(cgJ$7Yv*2b8n&`aPO1SU#KDc3C z=MneO_Xg|v+LkbU#PNa2wt(>cewAcNFhoNJN8tYmp_)Kz&#HV0EGQ85^4l z-j;&)L*p;W_>&o>_)hJqnd)biI*VvbHYckmT5sIO3||+T#&}P%VMF<&6e{R1DoDW! z_^PRCX(hP{FFqjp7QN2Q3st*+Ra$eMwHZ+D%qPID&<1xyMJtgijE9$dZIb$J!%bb1 zz;!CV*+fxPXm%1oMZ73X{N=ecAK(8G5rAKU5&shnBnhFe#pR;6xT#wN$HB&dvj7ik zDJNZh(nx5cSY=`{bq)A@P`@p{Z0k2lQy0d>C9~2b>+$~?_tEBYmW2pX^u&T|C*@0o z@oKdZ3TwArnoL(w(Gm*s>6@?1r^{{eHd|({XE$7#?hTye7tXcp;6n|5p{%AbA6@Mikp{YjBia@A?eCf|ddnMc>N;Pp4FSATa0^WXa=ZzkD0 zLEorvcK?sJ_6EE1UA4U2A%K?{6*4?Lobhg|LdRl@@a}Te@F3Ml+Xh3#03R>Lqtr|k zu(+mdzP+=LZ3tCo`i4HC^SC+=+qE{G$TJB-1@k;2V)7??OgEDyz=+=H%bi9wQ^n%9 z(wP)u;qsYz)8@Fxp<$`_r+xu21KSFI}*M* z_%@2R+^|kG)thJRbJdvY;9z3SPgZZ?PODujDaw7F@NW7`$4dfxapv0di~NxK(PacC z$$n*Ga0bg|{% z8oTT?^%KsATwa&Il>zg%Px;Qe@dl=2lLYzBBn6jxH+?;G^Y?4ECY^pT=(~w+qxdH`inm{_Es4#L0^Y1{04i@O`dwh;_XfW0@ zW<4abIu+uuHAynS&85tH{jO*_QELJjZyGcQ7*po3-!rgrhss&LIN3L?8B3e z4pv3r?ZZz!nAvqwJrW4bxTpyg}FdcJD_c?yO<63f=7!1!EdDBaOUXtDO4Ig zMg+9XVG2;$TG{(0iiy&($wCdBT98_}x?dxljL%%a@;I;j)>xC>UX@FK<6PC#=BsX1 zAoJ})q+AvwWCp;+7xwoQmpg1bix`b3AbRr6yo}i_e|c;Axf6uAEl_4h_L-)cWMGDR zT4>q&N~Y^u!)_|==d(!_zh_20h9bk3h}|kdbTnKhL5fC3H9WR??JQ~cG2Ye;Amafy zx9ND(@odp=n#B1LiEY_JD`tf!G+U~v&I-`M2wIZt!|TJ}=d2PY@;&-;BkRI3mZu!i z`C!%(^7B&@hv6sM>lL;chfP!-@{zzCj5x)T{r)SFvb{?p!~W^xDwhSf4K;bTlURFm zb)U!Yjc*y6w1}lVZe)~;)o9eX>;sFon<*}P5w}Atdq3K2*nK&;(1_npc^C`M<6}0FJ@( z&mA!5a6;nb+jd0I3#9Lt13CfBOg$>ml1gT3Mj8<0aU`z0t3Yf4>>5_=c3C0aVD=1W z8~&%)BotRqyz$64%g%Sve9ksM*gF=x_Elto0TO29K_L};W9o5ulj8MB{LYR~@7RGY&&{W+H+Td3K(Jza-gj0VZ5ES0Y zEo4nMvWkfDfaAOy^K}im`NCmd(eTC^+10G9z*nGGqDucNQ__g~WvVzNSJQftV|agg zNhSt5T8SUpWxm)*-a*$@b8?|!0HhwXIQsw1XLoIBRZCtKZM-iWSylM0?1sw{p3i7Mp}TivT)F71xr5R`mD<{AbG%Z$=-Th>KD_KsS-}jxZYVc5 zxj2L#J>B!h!qQJ1$nJcu)YBIYqSu))(FXqHF5%|WGFI2^iW28zb*GeD^d|e2UL)~z zU(~IepQ>2qE;BL>%irHVFddSW6%d^&Gf5kwXUQ!QOjpgw%IZl+n=_v|>O^K}(iQvq z!dEheS{;MVRCQtS4dqPv!7ZPR8xWaRk>|0uup9=3%MS%UOf$C&y#U1rFhWgDmw++T zjB!1+u{ucf0SPqs7Z(N;=Bz_z4IC~SACowAn8c+X?IyHOJwlEL=Nm@A<;+21QjA-Su|>Tma_6U3nSVNVdK_Tc(<~s=PzQG$;{3J- zFvWErk`exwoA^}+zjX@O>qBqav?_e3-XvJ&@Ggoe*CbLc_Y-+L)sDy1gc^2Vl*sd_4EnZBFc zY1Jt*kzgi%VP|`L`)bv#S0zW*K9Mv&iqn+3ByiT*PB;#H*vgYt-m4WxTad)&hYY!$} zBZzKSO6rwhl;~?hmvU@Yee6E%M;YNxq2no1X@5HS`AxP|zk_KwQDl$V^v5WNZ%05u ztGW0Rd?m^$DY_Z0dR0$}dIW`?B;MdFy8L5;~UrHDW%`9+NPdJoc2YD6)54Sk5oe5`a2wA?J{pQ@kiF5HTJ zdSO4Isc(#na~cKxf1(p2mVj}31-o)5Lp2?a zB@fCmD1N6F-U3oFhK%o+lCTr+6GB{8ldgv^ylSN}7gu>)stShfI>luBGLt!6k|+k^ zDD$%)tvK78(-gjro4K^#dVF!cLBv$6(WxA*RzoHD0sY7Fx{dALp4`#*7NL!~1_F@~ zJf)xOw{<6kf+f-SCMfkqkJ!rkJcD-<)fKXW*F#*foGy-V?bc2j38(ore!kG{c_*Yf zoF?J+Tvh=0O?U*0gdX0@53r#>*7$kN%;Wq|g!N)tW+9ipWTAWm(n?oSNF=2kg%66u?p$_nD``ioh}3$(+r@6h z{>iyA6IKBfU92x8UHtW6lt}8F%ZVC6D-)`PVwroS4{m`}4B%BMC?! zWU2@jm$Adawhi8nZj)20{ZQiCx$>EZ(F%Lmf9&9wQJispd_mLoYMk+V)-%AT&<_a3 zzI?e5w3N+IMYBmYS2S1;r1OLlr{parO7b1R@J9O$ZgC$Mr4+opx=Kd;L|tWZ6t>p;)0Q7L|5V!cEe?QU4q1CVW1+ zUbL+dpV!%vx+*H${K5C+;RR)el-DCglpq>2sbrMHL~J*ahDNf4&u5uhx5*^fNa&`I z!{3*y3Q;s(I`AX#V$;1LKb6F$5eCCx$IC;`mOACuhGX)ri3A^$PY=T3hHQ+bw8C9~ z78fJ0u{b|uAZf?ZJsBPH^+Anqz6Cc<2ZmSh1JK}7w#E>*)j=+Etwf9;l8J?+uPhY5 zq;#?4t4Ft{O}L#Wh19;cL}a=mdOJr=#5wTMws~vfjBgzB6y+G0-Ww2 zcwJ7Sbpm`WzXLhRuL*B>em6{^*1oir-PLLw4rp4k8Y20BcHhc_H;TH#Wr?`@6SA;#+?mS$0 z>p#2lRfQZ|t}P!5+bI87{w z;?xcq*D7|n)(ts5fl#f)fSiEypj=vzFD4(eA-KGTzH4oyfhY5ls?czMy&UI>o6jcs z%5Zzx3rZ%a-xc)#2brm$#m}4gzA{q}B!3GMgS@F#FZy`G zQ?mMJIJqI&xT+~W3*tlwLCv8s$V~VVi?(;}`;RZzAw#Et!v(`Od@Ya#d`mi|_Ay(U zE-#5ttpv*SdpU~}ZeJ!|40hs{8=E(fH@UE7&O|QfGMlU?i7k9_60%vP%P<*Pon(b4 zfxl9}A!)~T7XQS87;<6nKSK1OLkbf0NGe_Q;l9z={f^#P7LOafgTq6zIDcujoj>cI5Anj4uE7I9C-Bz86;{Fs78I+;TwK z^8;Ch-+e##47CNi)X7r4;n4uHcBC_ZEdtEDD-FkLcB1)-$A}OvlbDQMm}n<-A`d^Q zNiK;~>hd8zqh?5oW|HPquHndqXv{c)3r0KkJw%g{jI^5u&MBqy)sIArgnLT^lxs;= zN^7+2mkEmmpULGJJ$*l?hk(q4LW{2poOLJE zWG}qE!2$7b*qq>3n$9tb3j;nAEqgdqQWfp&O&%K@uJJ*8fWl~{3y~1_&e8XKeX|WV zkI1nt#uDJL4cOD$oc?_)KTt^{F4Sbh;cp>T~s#-u^U+0jCm9F5TOXPico z_a(gAnd)1ikkj8*k!RSnz)$nv;kkU|{+;+SHZPbmm@5u=|6*sO%&S7EvPv{7vZd<88jUmzMJuYC2;* z9eS8AIIw81G9$FB-aUWr%z`OsvV0I@#$r6lK6Od>W_oaSig*7)T=g#AQX4kFtc2ZK zPtKX%qmHGK6Hs$M>C26#iFGJ!fz2Vaczkm{DRE)ZV!ciX+mS8vM0PJdbg)2%troSb z>6sp>Vf%M2(U?OVhLQGMo8XfW6pW9oq0b~RhZ4C?gRgRz{DKw;s+KmfnGz70M1zyw z0kSig1_RKoMm|em{oeM!0*Dt0c^AK>JCDalXMYEC4@4=)c_g<>+w4!Qd3Eo;&3K~V z@9oUFjLOvLaUXvhPVSYhQ3J5mmBsFWeZ6%^9t#`_Y1{h052aa@xE&T(kbh5^tzmQf z(GSURM)wQ7u!p(c1V8P+&~-H<51bIx4W+h;8G6jgPk$~nIoh1@0)(1g@abLz(F*kk zFkGaUPWk3%U;LY&IFI=%^u7i^4-Wc5e#PIuQUArsM*d69d&8Rsms4+v73Rkr*3Z_l zSGuDphYKz|r=M zU0{1I8vNu4jKt}AIfiVkB)g!;O5#lwOaCVIQqNAi%s_(5D0C~D6s{nB5~gQs!9)Ct zzdtk@gq~K_7D>s@##>#^F*N1lVX>}@w5Slhbt0I(K@751qM_tehjrV?>kISzK|as zmMWAzDR{+y^_iSGn*~0# z7CWEe8GE8@Xz0^UdznoLB@9{D;F|zfu9|AqH=hUDv$%jk*9ZmlLQ%5%FaIL#lL7oo ztIf>`LA}ZNmeURGfcohqXQjaP^k|MQPb}n_;&uwULCQAR;31&1H(4)>vAsMYuwxIa zmT5HMC*Z1hKM4R3e%d0+zrz7BV%)-h60gbU(HZ(`f=WbxB!2 ztZ67V7t*X=!KnbzKLJ|~9IjMs;R{$;WSl<857>rXbe``WJQO$mx-%zlM)ObqAt~_} zc*<7rm_gT^K>+;K^3GLjV7Af%C%01kUfkGt?0Ds`@CWgw;3aeH=gk6Qk&10^@}TD zMEg)>>3n2RjGTRGJgFH-dvUr6W_1x{y7A$c^cE{uVxq1s1dSKXh_XmvbPi%e#)f!TepRIaZ?P`gh1q#Nr+mK#S1 zDmH53p6hsha@1*U@R%IDmpZT1eZR?XH2w6IGc|=RGESFNp*oB0!%is?-*i1 zhTXi7596ISQG^8>n}#4Preom))mas+*ilss3i%0w#O9OiZ (a>-Hr$jiMl@l09O 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�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%0DPqk@f>fJz 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%0AsTc|wnjcjwe3O7N>hhL}OG*bux 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 jPJ9apZ)ma>7tuIwerKh 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 z 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 DJkfGf literal 370894 zcmbTd1yogA*FH{n2?A0AqJkhGB6VnKlkhrQ3Tr-~e%!Qw{qzEP&F&YdE45pZxPspt zy$Az?!EY!aAT1^!Kp|~qZeVDl4+A6W7omi#EZ0epp!PkK?=vhyn%5k{H)1|<6neMg zXTIX1a9+4N6j>94LWtuG&prt#2|ebdAb4CYH?N!oN{bAijebv#t zYu`1rr;7P(4wi2RLF9QC|0rw+K7p^U$IZra*!PXJOg9V+82oiOXDl|YmOy_rJqCAJmOakwJkX4mP{%-DVucBpXD78pg2|4elww>l;KLks@g@7p<&Fcx0&3x@ zs&e4+m7^^8W_{+Hei>Lw4taZ<5zU(0xu}Psd!eo;^olQ(v1{?;9q;f#!w0iqy$+uj zxwO$sp-(pAwCBtWpkKv`&{~{-mHLYMvQXYqEOc&D3crf}9%^63ZPeHcZi<5?^CfeL zb9>{9Nr8pCCg??XUJBvIA4PgrSxzKOf z^0q-%>+Hqk!N;$+TOWDUl0$wH=(oLi|IvM-Ryq_1wurU`>teD<8!fg-C_2}dlBDRl zFmVG%3)2_v!UlD6x{rj&tu)&otdD%w0!+Ie7g9a8cjY%w_vPYMCW)d7An!x0=^=c( z>;pN!SZ?-N`?ZXW&;+2uZ?nkI^`95{!V4<6z{gXQkA2P1;FyGS-IFI1^HXeKGJybYS3M3*o z>}~v>0bfCt5FAZi{zvC{&v-h>pF$b8Rz2T$xRM#HDmjHRY|)1Ybs$Mm^dV|fAkTf! zOZ94=!)?^WKi-pfAy_-K0*tYXVjkZ9fB03aRA}M~3gbVLW*+P+mnICes zplQ!ud)c(<#cUCh?C_<6nu`*G`xG}HuNt-DCAxnruMh_oMBs)(h+)X32hj|bMZ?l{ zh9p}+UmbZI`Lq)KxVgnS8LZS%{MhnC$@@F8D|6=aMMIkHj$plTE{|=I@>_?r?7Zx) zLjQN;ylWy)ji)T?RhqeZufR8<&QDF6UCLVsw2=!-Cd9V6Y#$pVKynd{e@zwX%5v3K zKkZf=53P8j!Zj%^vXAX5Af*`7`K2AyuG!9bSPysmZiAD!@KA8L)g!P64ut3O=p0fW zy7k6?cV&GA!|nL$nX7Z1U!*e|PW@sKv%tn0Z_yi_8sJs!vF{i(lg0NcQ1eC~&+yL~)S6H$H#j{8AmN^9H{M z;fN2F2EMzASQ?epgZ>lp1m>xyu*Ksz1aPB}1(7iji1V+B9DXB6@U4qR6?2uM%Q^lB+hr(0%V4*WP$QD#QNaX zxyct)O~1WHl6}@|+FO{t_J&7U2=E?cO<|2M1F7|JGH}IFR=rHU1iXeV7*BBK9{CB9 zrQi>e?Y2JGA+SWUM789$#6CbM3On~@=X>_;Sk(5px$JX40a}WP(6Mi}-&94FM7h(j z#k<7Y#3jY~#XH5{r|YC!rN@i6N95CRD_nk5q*e>%?);|r)B)r`QRqq`N4mnnlyT}#ZDVQiI)4rm` zjYy}RR*=k^k#!JAm!<#dGNNY$YxAls($U{B@sd9xV>78sarI}POrET%bY04&Y*4O2 za+`c?nqrz_zAfL*chy9Y%B*aBdY?>Uo@Tm>a@_}KKF7!3OXk4BvS!d-E~YOWx40^2R31N(j)C4 z!FvmPH+es6w(-uyxC(>>UI!KjDw2?~ZL(w#T|Ge$R3fQ(!kcM49DPjch*%diXRh-J zSLYeiq{)!>;p9lyySdT{NXP~^vp-Su>sgaBl^m6Zs-g?_qv$o8wF>$U0*ru<0c8YW z`Z+aKm6xgvr6#3c4APBT%V~?VOR9|}%1+A?bTo9Gd%Zt57j~Abm|IU0l-ZQ^mgW`L z>G@1mO`>j(P1^)N3p{-Cf>ePyyIZq0!BFe77Wl4_znyX>c_MjmarN`jU}vgQAP?td zY-)~f@Umh%r|!FUui#fthq>0u!t(IOe|BxeFpqL{I0RdV9nleT64rMkMaAa78@J3y z*HF`FsIIEJo6VW6d7>If(}vr|f$ibb&`uhX`jnh|mJ6Nh%;|Pje=lOE zY`-h_B#H0|wh1;JmdriI{lGoK{mk>D=Ub0Wk4sMtPcT9v3Z<9V*Ibkh z9g^azZB<*>ET{F{f$}#Ig%gEcg+7bri%a`!mg|)RmN{*+L503J!taDVL~U2HJDg+` zW#m$V1@Tf-Q*u%gTLS%}iL6;82{<`y!P_aWHK4Q*1P+ns9QvPcF!o`8&k>kbOb;Nb%#@q(oaH76FIJaxP z_qg|U|2AGBLWGBs3Z)83$sAb3z`Xxyk66!SSWb9sjFWQKs~&>#Riagi?ybj@e&T|& za$K3Y(L;<_c;?+Ahtz9q_@A4|FKqqFRtP&i-zWac{)H`vm+|yR29pGPo^$oiaqDm* z0V4hlD|}*8{2*vpZ>=^O+kgpdnh?|@89NJVH|g`>;I{UJ?*0&oj^f@s(`jFdXf=mY z_nB*cs{FL;A<}?wC##gA`ix;ib-m1D`Pr9mpJdu~psia3u5_yrnl#q0=@_qyy=qo| z73jL;o!eL#UJeE8n{~6kre+{yXejz+*BTr_Oc$WYqsn5Y&}?+MIW%fM{!!DqekT3P zvTB!V=hUwMzJG?z+E!%2m29+%LtR&ckyIJ~$}_LUm;8>|uXQg&r$U)!uwSi>K41M@#7 zCuYM(MjY6WEsRT)j*_XcM9RroI8<#uUQHA)hcBZCW0yZJ!-&I)U+0XrJe1j58RN`s zOROFvm>shOZ68-L^MfVh^%G1vpw7GN4*f?yjBgm;X+mly26m@h*6qgUv27}C-lbrF=+bkpUhb`xsBu(i&N9^AT=dP< zc-5qzjqF9tvsG#x`Sg-IRnJz3aHH4{+`c(oj2D>^VSEbZy&Wo9Ks^z=kXz0y>yJwy z+riwdyZ-X)D1k7AF!4(KcI3R|GX136)AizJbdI;a0UB{Wy0G=a{CLj9b?dO)hQes! zwr0jIaX!jr;Hq$$YI%85$}cg&CE@bxar#nD@A#Uz_WUl^8Wxs6yH4=75{xIlgcS0^ zi7iZ)7|g)JMc`Y0m)kIWK^KDv%KgN`>p&VhGnkR)yR&tkbGu>0#<{%xbHtNTSs33G zI24%M-&>FTHZftmU$PC4{yqkghnT>v4bwe2F@CCftgGUGC z6#n|kVg?ctFtorq3Jd})F$^Mb1`B+7U`hTt7lx&Qf&b5SI2afoLl}hL&qxBt`%ftF zy>Ii^5&pY33^MQw7x>yI!TtR-Iw%SL?{kE3;2sRWoPd}ZaFo-v($_b${%j684xbGG zE}&Y7s#wFo5WKkm!ivd0-v{15VJNQ*QI_}!(ls|_(AG28(PywXwYYx|43|9!I5pLW zXj9mmnwVLG?75%)=LrySet((q8O47dff#c?Q)A4;lpp1(%hc z0Z3L*_;+*Q7x%Nz5QqhckLfI)(83yI{W?KfL+7As6HQ+#kr|FG2t3Dj;bdG%m(}R*eU3D9Kd; zSV&SsK`D9Q2&mcp2TmOLL-W@WIEOpc3~YQe1p~teBPPf%Zx6dYi~Q-a&^*#UZ?eF9 z5tO4Q$scqH0AB^}c7zM~h>lkRVXNz@qTMz~Q*T{Qo?4r@$5cnq-Z` zjfsKye`x+!uQ+fhpyjXs*CD{7AjHFX^zomGzM=TPP2zqO{NP%o|GN~D@c0He)}Nvx zA^gAOxInYtScLN8f04A88(%ig6Dsc~xqvv3x5An#{~zPwD<G8_(BuC5g(lAh^jFBsKWdYN@KE?Dh)`zgt$obqt0kfsv@*~M z8EEe`D82-VBViNBJ%RFUQNu{^b8=FfqTAIg`K zVnOWpIQR^ICmv+>}kuwLttPZQPdI+8YHilgen{@xeI@j<;)6|| zY&@R-q7BCa`{5hXlwsTVnOzHhsbr~WTHA8BzV)7Ic?7|#67hz8xVRMh=;5zb-si4- zS#FbKUzXSru@QHpQP};hY9Kj5*N`z=WK|zEFMy1HN20eW3!Q`3F zS6B#>C`_b(3<3kT9j0`$_K`iS9O~D%2=M}oue1aIc(WT$M>I2a1f81qP=2&Dg2r8hr^5o^iR^Kz- zt_GOl`jbyrfLWJOdmp@ipwbkG=;5>q-_ch3MMQ$z2HoM2`+dYE# zTIa99v-KbI1^Q9Ae7fL81~}vs9=D-B=*s;uL#X(L(&wPfQJQDZNOL$c_s8Kfupc}D z$Xgr+7RuL|q6HX&1NwxbO@7MtNs)2sL4!o6Q}GY7Kc>#-M%6jBrYYER3D4gm%Z&pD2b?R7Fm^*7}5Cr zVF=e0(jZ+1N01s(*EiO%^f~tZ>$6twQfQkBAsrXhwh1b;AMJ~pBb&9qTqXztF-tBZQjN()*8tHwVYEL4$48Nch+~1Z+3wi{ z%#Mfxt4@X({lp7~CJfY^39;=v7dNFDnr(&KWpj1hWN1#OqTh z}bJRB_FJ~}Xk z>wXlfKb_xu{zk5qxMNuGk>ytpnl=<*fs$P6s^PR(vqu{rz5oV+i@<_Ft=uLYr10dV z;|fJzDr~S649*KmhJLU!A6O4F-k9^eGU9C_wOt#H4UjkFL!=v_wBnJrVTi;EH-~B6 zQ5Nv2$VG`(NDu(6_fSI3G&udhRPcRoXSO%pO?gy=)g#@(xx3rq=;IF~DGt|teC;o3 z6n%IV#$6iU0t1g)vvKlZF7k1miNQ~Ec@IFH7pUDYPw+hY=pI2%@m^KC|EuAm!Ek-J zosv)^V`<(GGDdM_mC2C~V1x9j#v|USvtj>kWK3s{?nkjFYXk2n5L>f(`CC(C*hzlu zoypfzLMRL#kzJb??KvY>`Xph3j?j8%Yit!W$;nD&NJA3eh;5fqLg`BqUeLjU%Syc9 zvF;5sMTSA}P=euyE;zG3A$oX~0tXE)*l)4XJC1BGleVfdZOqbYK|qe0JLL)B zzuflVFgNMwa$imRM;L$E!{B~s(fd21&1g++DwY1Xe7 zdn{;bBbVdGDmypLSl-PMVMS zfqwuoUcWK=tgn21J?MFX6Ny!%vvG4@LAg^{rMbx;@qy0*4bZ8$e#F#!!T*Z~0i9xc zO8O(TWZAhRP1L`t@_EjudzVS^9AR_31TkJI|NQ{R^QTXc9(Y=ua5#g-l`k8`vOZig zI<-IFErK~<+bxRg32&n5x4AAxkU+Lq#oNEM_wuEDkX&eeUn_XhzB9tBREUcYlDl|0 z7_8qg)H$)x(~qPu5Z&C(dv_k6G{a@gp%I5KAVe;hReFV%kwVS0m6mWtY!yk?S;*`Y z$FUu!&Z>E$TGKgNSf8 zxc9d1f}5qaCo?$QrC@wBLiJGX03%XPaRxkOYI9?$UTFnSd(y#tyWX|Mqch%{PNk+~Kg%PH=}Eg@Yj>u|)ZYqEE!W9@WbV*e zcr-Oxmu0l_w4sU=%_*#F{EPd`k8vM6U*NpvCVEx%wese4AtG3>Qj@*ag&?!6qow2j6DFDkj7~zAs*c}DF!9j?@^#OGzk@SwAnmcZTn1# z66}p3KfrM+=$JiZC0UGyK(+pw_e6bfab*fwiSlpt;d^s0_>WTXx(`ejkfDbbAGz?U zRL2D@{wrCp7*>b4r};!eFFr&h#c^26Dl04RTYvKULp}dmx91<+kYPWYrUVu^2rC?{3q(Ruj7TOb=i=5e|WrHf8Ctn zGbL%|XZ=%wD0`}yQsb`TXSd^=dUy_~VQN?6k^N~#$R{Hz35SoJHbT%C7Gr7pRg#}p zM+Pjr;S0~53;&I<$qa5u)>*v#f#m2U##F@f^H7he5N-}ZHi7W9Q8q?o) z1%U33Vu{jas8EJGw2IJfuq~&Z{){-Q&^mGkF-tLL&cM*9TNhnaXtiRMt%p?N033pt z<|M_5nVodBrqcatx`&$qDh)9LTXN&@gR8QSy!^sCV;gh3-Km>e!*LcO=-fZd4e8T$ z3v^$FFX$1$Q$ne4R!7k^(2#VlDl|gqxIE$2auZ9P7|8RrqF}JaaGzLwU zM_&N4WX5;POY?f72=?__=1V(T$2-pD7m5A)0~}SkE^)YUwYMlBYjUe;9I~+2ibs)0 zO;7*n{5cyeLVVaXUh9{?CHU8Hsa^FYq#Nh|9t8%%itSA zGr4i$*@-y~dD4>Z#AbNh$r1y5aKx+9>z z5cQy4-!>D1IwQ&r;w9Rol$AKijEaua@jB7PZY+L%wti_;!&bNR?DG`mI_bL*IQR#e zz8}Vy0@q51zGuhQsaXtvu;qcnzzH@7fWTQC)qD!g&v!Y_y1SS90iq75bt_>!1?Hz@@bbS?Ip!8?IiWkC;U(WZSx@(T5%LmERRPL z-Eq5rA3xixhkZBx=Jtu&m#Qo`MQYQ6Iu8)U7V$;7Y_g)tjgXj=;wKrC2Ch0>|{&C~g>T`HD z(PIo?y7xXq5$-aSS*o|@?m}W+WYYPf6~$=o`lnvHSLcHj|NAu53Q_U3vN)OJi7Z^3 zD*UY9smHLc^FVZt${v-ech5-DtVnriiLQv`o}umH)ap^G@y!f7IBcyu99b?};?C2H|-pGWXG1p{6R2 zq+L?)%SRVEP=hTh*hFJ7U$rrxT4g|mMB3S9 zNw_+y#H;yvfaW}9S7{S8ey~Aq@T#1`Ljax}oi*{bKuGx$D#s#XtNnw>jNaA6mPo=n zOv&xpIt5$GUxcK;-k7yH5fza1*O3Jjk@MKunoI2_JCj)^is3_8V*{v<7K(HqcxnsA zg}tN_435}3C0jOPv+h0kuuX27kp82jBjOk9TnZoalnN5$Gd>bo*6Mc?#Bg00TgLq$ z$AL(G+*z?K82KqTJW-@e6LBb!7(mE4w9vGNyxMYJrcvj}!cy`23S{@3vf6s9+L2!BTk-L+n!qbv zLDsUKVbAW&5b5LGq6F3>wVUUtAmiqreee4Wm`z9=)N_w(^~Xx&Y3J!nXf>k@1^T8W zajn0y5*5Ee?>+p%&Q3y>dhNH38dfR*aWr|cp6g5?$@#WwK>{ZZI$CE8&V&VrP5VCE zVGecvyahTM8vE96K>2v1&xY$DLbu4u)y1}HKeIO-{$FZ_LLTv&2okyXwRet6^?onkIVmnD*;G$4J|CpW*zqf)qf zqo}Bej-;y{PEyz;oXx`0_@Q?7S^_?A6$-ihMxbZI$eF+$_RLb4Gi znd{SMs=h^rp5>9qTl_*qg@scsMxR+j`N2==kU^}1?Ugrjvk3?uy%?ppGf}KROcdu8 zqE&ob{lm(o%%Nf7uupc*E&rY5dzD5le(**8(D>Qs!@Am2ZeyLxuB0R9Rq`B-YKTOS z+SRHi3h3%9laP%^lJ#e9Gl2iva?mdQk6Xv-g1g1$C~2!^>81gAtYi$&qKCBHKL7{W zy#(ApL}f$lXT>tMZND(kr{d0&nQN=3%3UmvnLUsdbQ@C-f5Q@1^0Qep^8+~8kM81w ze8!rFu=M7LA*qp>7PpV$jkX$}FQe@fjwt(4uz3a9hs?pwiJ&>wv!5C%$np9(jl-cO zap{1#cpjg=j3~H(yLrp#U9W;m{l4!|)9;aO?H7$(o3{pM z?;;PLaEvS(4F@+XaD&H7lOGX@S}KlfRPlQB&Gog<9$B&3s{e~laVQS$1}3*}k~90R zZ{Pf!+q%GotTleNx3BzR26$RaWmPjGW`nfjjp2Z|^)D-A)5T@KJrkl~U7z zZ&7i(&EfOJ3^FO~_^*|H&1S2GoKClst)YE>m$zu3BV`AZi^J7a0Us>CB132@8EJuD zJd3fSObZ>{e7&<8DYPeAZX-2Z=X-SU)76U|CB>ThOw;VASE<-QKJBSqWhL_!4c}{U zceZ9@DE&hlEi4CgL3}Vphz95dve3xC+5msN;Lby{yxnFvoRO~6>MwJ9ec{(|XLYzD zrp+kPJ0&FO6<}U!L3Roa(h5+>mQQ}IQtW$0&h0p&jqy`1GsD*xMdmC_%G=+T&3rBm zoA+9jbiT$eDqp>dHaLhJ=#*O1^WumQr+(&cvDqhRbi_JAX{{%^wd&NG%6zU?zDP&t z;BvOYoLr-B)1KF?eP`a~%9K{K_Q(2iLBnlEnen*)GQbC`T-M4U>mD?Q^~3HCO+!Di zlHNMX6zhrk2lmjJoNiBk;F6lF#RPFWAzeFYR$4sLxuX&ZX0MezKgxn^40UZiXEB|t z{aUKk(9_1yeQvCMu+;j>e2#0$#^L%L6=c7kRJ&bhcgo_{fCS9(8-r%8APK99#b@Y- z@nACi@8WvuHzc=PQAPAOOOrhCH`&7j$jdn*LE&-S<%tGg=ETCmfgQq(4QyZE>iJt_ zwIg0LH*nxx4jtU6wVrr0)(NZ{@#?FNi)+uEj+TOZP3RPEG^sm1`ue42M9-`u_wC=` zs*kFWSUoK{%->Mfo0t?&8ZU(>{Axg{Ti<`~7e=Zmw&rG@7b7#0m$M^8TQlnIrXM|H z3}1Ci+%Wk=BgkDzYYeGu_1Zr0i%GfR)=^x$)Q_~BjJi3%muuYZz8^O@J8tPTx!qk$ zBP*D^u?cHN{Ws}y`vB{T7{988YI$}$xcUi~!Afh8sq4zV`AU(_)H!eY!$6fpY*d!Z z&7}^C>+J4TN z7>DiVokeUmM-=AzMf^Ve#w$aL)=uh0I*3N|x0qewuTlkO>6G#ZDlM18rOS6JqV>Oa zMbODX`^sIFc4n%=Zm+GeY_=!d*(rX`vai(Yb@Fzd@RqeMO^`cxNOEl7N9GsTv8n`q z-1Dw;wGQuGZ{akm8YJ1}Z#HuZvXadw8C{z_cV0@}P8kk_H8YrWI6u+Cn{nBjt9z&Y zOR(B${9t}E?4|RIDGMCi^#Q5R3yrYh^lGdQPDN02hTepm%%uiFde^mRaQOI8*k;uv zf2L%-O)FQQWIV{D?|j{hBmNttvyq=LQW*Z=hptvy{5!=A^*pZ65B~9#?92hOD%qnNou6j za-SYCvhS0!=bF!)pVWN0K#Sq9&N#p&YCzgIPS8pvoq`5U+is0%m_m1qWp-eYf8+Wn zxT()??_j$1^_yG0x-R2Y!Si!Fn@m{$3yN{$v%;lOsZKEiZA$TzVM9)OBr$m3D`aQI zv&$FUex&aDR*)u8HJWtP)+%S%T>C1;xymOo&D->n{WSemRl#cl$X>qbV&;h9kj#(H zuKt)!25d&sd6B!`Q80%F>;7`9AU3Fb!8l-JE>BY7_!5iw#G8At-qw~eo4Bf->N`OH z{uJ-#NakdS=FTajk6mwGwZtCKB z(nis4NE&d;Mz2R^=aeJa=49ERm=IlawG4V-2R81XFhECFEip1IarKN9Je>W;mjn>A zRD}2~zu3NLW*>sr%2`+oCH*`K&6!STZM1Z0k+KCu*z5!BIxV8QMYuZo0tK~0W6A0S z78H?6qx7n!Su}_5)^jn9x_N?}uoSGa!aYN+a%9tsab3y~;~n(1u-J(O+KL(Wz#m^E=X!rk&0IE!xV#~^%@m{0>nRV}Ik>&xv0p-!2dsRNKGem>vV4yP1AGA(L80OHKX+5Fu=gfXC_XeXpU z#=JLz_`&8{0oAcf2!Eya3-XN#Ca14gddQ~m`c5^s+1?LKnz>W`FUiaIC^x3{o)Wp` z&R}FS>H=)e95VLOoTEdhaHbEEK2|bGdo87Xha0J%$1g9IRBs3!rN4sue~h6EE(V<+ zl^Sdo@`;=-Efnz`JqKWc8MOHPiT`=s3zUqKnJrOR@&=4X!%r&(r?oDSEYXe|+Dg)qu2@5-~XAi$O{uPv3k>0x6pE$q-mQu|C_K z6(?5<=hH`hQL7?@3APHgI$j^F#@D_XY+nxHmCREudn~X(@zTdpS4W3NtNx2lgfUU+ zDq6FTt2k-bX?q);lI!^*4ojfZ%_SzVk-O%HYpXy0&yUe>mx#KZFiFqz&02LdZ>o(( zvzLl7asWV~f_gcZ3H*FT=SrxM*qyIY(+W_KjC0*?d9Rm8Q`yXBL;>uhu=Z3;B9=A% z9-4KNdhO0$kv)oBRY>HpNPH=wT7=7@RA~*xx;gA-M77xqaB1;7Oa^2oUy;8PmF5sv zR8-WmyyB8A|MR`W(Ui+#bb%$xDNx=oeq|_7uHIo|QV@~{IaYof-mC6E^Yss3Q9B?Q1)mfPLPT0F?-^>aMP9BVHn5|Rk$RXgNB z&X?~@XRCwQkJaiNZD>`By#)3rrjwZGy%PBYi>-J#30}$mAb$6GVILjsT2-^gmY$5m zO0ddREL*7{|59~DYhs~ax9WhA4TI#=?-HHtnk85X;1aE`%kr#N#W#(;rh9%IaMG(8 zXx&V28#j*TDEWCFd~v;v(=7d5R2k1$AjF}${hQP5Lp(mvTXlWQJ+Vy*Hd~oEhTWO2 zY4*I8#{b`_Gp}I2&LRBpCJ@qhAwSjJr`_vL%DVSgqing16B4$cY=7dQL~X&P=Gcw$l)^QQSp9i$|S5_QT)9m>&L$K za>tYlK)t;2s`a5xM$uKn(y|VntW?3=C!SW}V9OUaWp&9}F16wX+XSAyA@M0GFP_N` zD}P#XqVz^M7~biDr#OSVH?XV8&oADwxp?fks*2pgr8c;{8mjZC^Tq&Kqezpim5mpY zA9R&mC(cZj7L}(k`$%wn?$*}XVTG)>hjUi!%cPD6lD ztYJC+M0&+-xn{5agHpcw=t&c$sAhTu8)M(+w zQa|tAGmrGCa?*jdq{5kuj~DK&CWX!=U%N|Tr_ayr0d*`ydEI?^-6I_GG~HXd;nL_k z%0%}TkeJIT?vpV}7HBmvD9S~Suhaw3T%2#}v<{Zx>T=6mutP-?>I@*N(TJ`fZF}8; zi*YU2^awg7!`Lm45kf{yc};=6jr_Ci$*`_SdaAD9$^3}M*-1+j5Kt-@`zc?){-_e+ z=t2C9RV}^R{$Qyk=ZUcr$0_T2PhxZJ+pA!*(n*KpOep>C9!i8g=RS6cP!mBBcniC9oPp)2nj{a2z#ELQ1$G_DRrRn2ZW# zK5OL%&()$1`Fnd~PYt~G3Fk%?Qr5tnigJ45y=6h0m|FX3O$URL6J4*Hr*@KLPn#9X z?nc#SXl$9Mt}3dgXyGBsF}_Fp7tVUUS;SB4udTm0~j584GrpXKmF z@#z!?jyD>9?yT?wjy}uzYP@x0u~{U%@Bn=Hoj7I1V&Q7fl|s=$f$=5jes=#}9r!Lt zOUk3Kt-hy^Lr*2eYlr+4k6Kb*(WC6^^B~yP!PIu&2=yRmK_DmPIOr;481_;^QL%Z}h}2`gpQE;i(qQg<^2_<62-c!xeH*NankY*kut6ygsk3bHljdx! zK!-srD}@n8iZOgSm2hAhkYaN{2Gc))^%Zu+Q?B#dL(RE3&BLps$M&jDb`R1BYe|=3xM$ZC06&tqEY+uV7c9oSyQFj>FjAWtveQI z%WQ3>WfBUbbhgR??9q`$y{V_#?SfqbGNFL+Fv};v#*kEl-UP*fvP1!+92YmMz(pDx4EnAZ<lrVElD;LSMO2q-xVI zexTAZmy3~$ISWD;SNo}}y_un~VjWJ{`@W%#ue`*v3s6I9#Q_ecwWZ$cL8O0-DeWDs zD^h%o&CW`cmge%=>|AZ;S$jzs9Y}4X`^iKpG2|(m8R^bc+4LfT!_k@oT#|s`MU^%5 z^Zuu4^E;#NUd8HB+jmAIQQ@!TUdpWWCo%hkIS6SHK@xy+LUFc5X=Mm&lKs`G>E>W+ zrmbkTZUT9~PlZgXu#CwzkL7jLmvBmdZ!J5YO&*@jdsK1U!*W zHqxUsap@T~ss-VZr##mbtA}|QP3>nUB9)GI#{0|RSPP!bm1H{G=!2W&Ov(GX&t9{b zPRT#cQ>#dDx!j;WDUL{DE?A4_ur?Sp0Kk%XD7T_%0r!)4>)f=;g-JBTtS0QOt*w5# zrX6GUuBJ|!DlzT47=u>vX;`3|OVWjdi$l`V{`Sfk&NKQ70N(&aba_K|tNt8PJRyU+ zsOv3f5 zouus`%k~LRvL3Q?Yc+7GU~(8=iP>DfR;U%KZY-&w1n)+9_T8dwioShi&9q|s^Kzy z`DwX4D(#>kKi8MYE9HrVNzfid!j?j)S$LDOu{-M&o|Jl#D7@5Fo5EbM-4n~k5B0~V z8=wl|dJo{=)IcI;|AAKpBi(m~Y+!yUGq`U6nSA2c)YRqS`A)@r%I0WJfSl=sEQ!@| zIX2ekdH~Ne894CHS-<(AG_P6zrRs1m^U2PO#1OSIBcTx9+aK%gvn5P5(HN(PELe<3 z%Dv^lPC5NDsNd3eCQ(}W^6>&KB}GKmyJ5wt@qx>et&MhaZgh}wd*_*5xuF&{Kv$*% z9G`DCK+^-HojAb5yxn=9k7g1_r6dW6k1Y|Ni$^QL(^h|PV>puzc$ZaF21$WqZNQwAZ9bGrD1c~KPmgXjSc;s{nWYLI0pw*a9zG!`e+z6Ff!+vo59FGpi=#x-8BEu z(0{EYpB3H!a=e4>Wg=rXc!`hOx;yEPDP6Y)#MmH0RI&n@3#o_qaTKu?8lKl`pzg9> zqZ0;Va!TQwX{{`qtlY=js=SGjQsgffMjbb8vIkf2ON=7B%vusNS>$|FvbXlcfvCKO z+A-=jf8l~j)+OHrk$k(tWzdVJ>bjL}NJjTn>5MGRA>Pq9o8_{gVyGoq@;9k}ZW>Yk z2@BM{Zw1hGOHvLWFjN@&gPcl|n8&|0Ve2|O^to%#vC{B1LUBqnV~bqD%NC;r5qy;g zJSd+x_tc?hslWUI^gNKIw!;9 z&sY8>ZIUr;=8{spH{mpM)iyb}SYi9CCA zy0!VG*&FkneiwF%KkNGTYrSs#u(#{m-B6ei4Pu8QQl1Mvn$@m|fSP20@A!VYH_sT{ z%M(b-;ZdyD`HTVa!$5R{8Bj8k2;2bt;yd7<$qfVu^}l`l)@7qs$-%>uaCt^f(N(4e zuZ|d>UNTJbLw%s0K!PFl_tt=YcF>o%J@K(%IZB=kcsl-g;l&R!XgSGjwAR3fkE0 z2&<_uA1p|$6FPuO`KN%Z1JgXxO?hJ@Y`7-1*y}@XV~y3wrowD*vtpkE>CeE~g&)a= zW*_dRx@_XS6r(~1jiS}=+4MV5O!p}~Q<(=L^4GrBVJKt$M{~?X2{ne*lS&^x833@QVrxWXK8y6V_mH> zNhQn@;s4Zh+#Au(nb&)$R<(Yu^lsmjRxT`gGMZ{Ywg%Gy{)_GNfSab{h7eOoos(_^ zMUm+!xWo^W_?<5r!D|WzfZ^E%oIAj@i|u?@vf_5RM$o9VNC0XE@1q#ohr^^;(`Txz z13-NqebB!6)F#h)r7NbV5P-eSR?MUIhxW!wx!#;av5$F^*6j#575MsE7|onBy{Oa( zW>=1WDM7rW|H6&)E31j`ScHT1;f!^^%y5Q;pd41FmFQ@g1c~N-zF%#*>}_~-U#SHk zYYU4mjVcmv9qNRYk4f|LLhqze?Ud#1$@qMW?we`*xZ1eII;S&3U^I~9br~7@#~yw4 z0Ok7J*#WE0@g(i7eiup(5wlAs;NSgpOR%TFk6r`oV}EgZLekZ>)ORPS_BQ|?DNX*o zGY+G~58k=1bD)ojPQmyK*nGk(ME;)V6IYeay>7C{AG7(Pa{i`>b4BRy{QCjD161jY zacN|-Jr0$0g5-t>v+kW2GpiN*_*ss*(&ZpDOS-ih%X9?JS+`8tsZjr7uk^IPU^E3p zUnAWL>X%kFy6S&ou3jZbXAqd|Ic?&~!px;+=4jd>FP{FIW_5?4!kJ}9g?V~Cx zcYrriCb5mAw38Ob=^dPNg zJgG!odwP~Th>pQn$-x%fY`vDJrgSv_^i54g#G9hQp;`8xT|ld%(YpR&h=JXa=bY=W z=cNvvW)JYFRc`614(FK_=Sq-d-#+o|s~w^%IPaQH{}2W6>vr+Dfy6BSfb41Vop2$M zbQ@O9(ahkX?=SAyXLK9gZskiq%gE$oq8Y>c1GP38Mcc9)L<%3QaE}n4%h1D>PAUdN zrTh~Sa)Yxl{gbWnRB)oRWEqF;_79!St3-Fl<3W*)Q6*E?VDTDIUTs0m&3(QNbU-lr zQYzu2TDb{}scbdm)#hwXMJ2$7({E0eM5*>ixzXF{06PQ#eCe<8(o)JLsI_fZZqIy` z;xC;kbZ6}6uNk^%3nJN=HaBf}qf^@ZgDLee04sH5=@U?Y*Al=EM<6Uv-7|4ojZn&x zj2CHLg-6C3fF`<92LI)poSo@GB!peRA->TkmE?W)b(4M&3B@A_tJ}gK|H<)}*0}4v zZUnMFQtQ^sPsQwV0VD8HZ!dXHOrL#F%tQ;)vYnjMldir5fzpGN9{zP0J(SggD84n$NlIBekZGf@wyIoDwzKLIc8 z0huPFlFhBYCPt8o!?^v2;iHcgQbXBH^sOweNKfK=ry)25XwImh?oU3atZ!GE^xG^- za={oPPPlK@F2=Ko^bHXW!Pa5RZ-VE;EIW8`Ad$F#=h?t!@dd(tyMbtM&z@9`ox<%e zzv8nM=Y}tgn_?uc=*YSQ(NLQ9R?1(=0=&sU=E(lBPX6cj>InHfqv9*bT|_Y;H;vgQW6&jPz4{Tpp*S2-VXVPhE{Ca{@yeCw~<@PuEiJ;-jkPPC+x zNkKT-nHG6W()_EfTCty5uYh!qwBM(CiZEdit0bmpypsBVohN~a13jT{1+ zbL3W<>-Lm7FWT9W1vny*BgyR4rxkoeFZ?myS|)ON&2znR8xh%Z!~-H z^zDL){_A>#_};Z<-Njy$RTY^2uDJVcQt1-0iUi+AlbG2;kJc;?{0E~LwG^)w zJ%Nh5%suz}7I5?c0KdCMhAjR-W0x88egv{tyv#2_@XJ;Kz9>r9rgYjzGv@R4^o&)ruKP__E&K*^m9uWJ@Ed>| zG2%?P>i9k=7g=( z;mF`*=H65NT9n%rFe7(9W{T%>kWKJ+uCiLq62W$YncFUF?fF<9oZb6#VLjGryyEj5G@xBrrzEsCw5H%9sfMg;y&9o)^BJch6zE z!L@?_$0G|^V9WlwwZ-|q)F_m(eI`$hEnB+j)W&P;cyl!N zJPONx*ag!9A98w9)^X;zCkC z%qi(Q@O+`1rPOrP1Z4TCCSMcSJ(UF+(Us4$H1_ZKrexO}8}@s{)ZDw153xxoprem^ z7N@Q0u2=dx2JzQpmC-6iG+|9(9z*FWnEQ$%4YuH?rY8`r7H<3f1f+OwBA+jsaAFq= z5{=5`EW7&3f|1r8Ud??5u|@f8%NEd)9ukDMCKY0Ni|1#RgJ#It28{Zp>?ioeU#-0_%(?be^ z;>QOsT|(Q9#>u+DL2V)1>67bn9}j5r+Ox*m>(hr9ho>$}i6u5S-%or1tJ=ecm6iqBv(|Pf~q>hvr%A%fq)^q2!0r)NNHOf{$$*j z?<~S@uzX#7xY#UoF4tvyVQa0Sq?c|UB&McAg)+m@qZOt~;5K?)}1Fc+>hoXId%C%V7EjT^=dQF5bcp)1b5r~qQ#h%YVS zxjZczT2F$RJP?vrRXti5vI=dT1-Bzbzf|s+pG*KH$@0@eb_Nhbzk^)>Drjx_A57&P zKiK}`PfhYWd#~pg3+ofBI@a&0v$6CJ@bko(DFG$UQ!`M`3P>F!l--_&n8DRN_OVr~u7@_7#yS{m*w zH=8F9St%f;Q&scj-GX zu+IS=ks0MB`a^lV;;2=7*2w|FHWA6}I4G#?jo{D(%@;2F%Vn_7Hu;YyYA|;JtWs?X z1;iXN++i^jFC&0|^4{XB3oKVWu?L?+_}--xT-i9CL1n?`lUss`4rrrw4`bJgsB~PG z%TR}7@fv{s(X=p`EXU@qtd%rc_6J}m`mX~k6!%{nEw&=a&v8{-yVHalxylE4CB z8;@EJ&gxr=tboc!ebVbZ9KDr%z~J1?(p2=V5q2oVJbPA!ggaSpZ!@*|?=KGI`2}x+ zQdyroR=X?u_)OLH(zE+*2*D*0o6omwhTlq`dDK09%Ek?33I&<@*(j@3kfEZNgHXua zG4s1{N6R#+aP}N%M~p`AsnkRsqL*4>v|?_Tq;K64fE|ZK@j!D6KVyy=P=hqVNfK%` z2bwHgX;*wbFW(<6w{d$(@HJmhD}E~a%0hH54taZMNKgKj`>Rsr6R?zXe>YS9&2@mb zeoKIiX4#^aRuk=p&qilR!h`(zOD-(p8*wXVGR#okX7~8=bY-+E;x0qt#S8Wb7YwO; zPZfzf51cY3VASk1;ysFxlUe1#bL|{FXGQ!+1;{bc;(-cz<(u_@;?`S+;K?z*g7@t_ z6kRr4{IoEnmy@5;fwc$20zoA10RrV+l`{ABkEjnx zb{+-TPM(4$mmo;@!>GmOOZSY@km}eW<@24JvJz=DrE% zK5Og@j-fNfcMpi;dR2!~&N1hKvcQO58|lt@BhQmU@(ll!`rffR#w52+UqN5bkvbd2 z*{>fON+!Kyt7B0*fJP0b{;6G{E=ollN6wZUH>CJ8l@O`wG=k!I~v7g(xyb+_&H~C5r9H7cKYvTi7EVk{omX4pWH`ar?I=$DQx+D@^#BB)+ z(9*Li@g!${V_9uuk7C^C3v7vXZK?S|utdD3L>C3vZUzreU7k$5bysN%M;yYQ+qY*) zvd(X^A*eoY4>Tre5r(BI#s(5n3*A~@U+04J`Gf)bmwO>iJ~B`)f+OQOJx|ZK#(*@; zmt-O%)KC#Ljb8dSvcz1=>=1kW1G{0YrHGaxbwn8m^##L4I-*&|N4=&uUz}!3TI2v+ z$u%K|g|NO<`b(`@Qk$MtHhS0~sgzeG>yO@3VQ0gmcg2eWq*86;h-gOB8hnG0Ag6B6 z#Czx8)Rcv17X@o5?}h^O#|jeX&+(YO?PPYXr7Stu>%_|9_#*Qoat85az7@_~ORWjM zK;xSd;Jrpg)fvvtGFI&x1fb$vJ?`P*$7`Vf=0FIQXSq>wL9X-?``0k=MNi^TtZi?E zE5+cZx)f0*&ceQ{#J*7nSwI(W{SOU=KSVr99ANWlpV;EEI|YRCA_GqB)!bUGWFxOH zQXebz29B)H#vj^UxnV*ATjIwg^~9b(ftXsB~AcS#38UON%3IsBRvsq8G~Z_gEh0UFb=(J{dqI z^}XUoH-F;nHDgRh=>4G!h`_#OCLl|3dXZRkLFM9K?Ao`|3D|?+vnMP-xPK{HoZU$_ z($=T@=#9%KwR#QD(R}S~mh>lgFEoj55m4u`JZSNndttu@^7p5)FQtgfPR9WLB^o`W zn$T0-YK&RA`cBu9Em9PU#Dh84W3d+oR*qFQe#&J*qf>I=zU+`Qh4EQ7@mq=$bsp4iVrR>kzm=2T|gxc_A zH93spK1r!46^FhzaGqE(2p9ioc^z-a_nK*Yl5xd)+!G$kyrrIOPpDA{<`JsJ7_qP^ z+P;!wX-!pI6X6$d#e@G3TeC;6m}e$D(p5 ztqD)ht23Pm30#mGh2wZWC_uur%RU$wjOF+nuq2AY4A*Yp`ego~Ws-DZBm=l-5!%)*vV z4A$0l&%}{mjx?BIZ+VKZwgY_Pf(B?t!rJ|n1 zB3=2MOd0`P+jjQYvR8j9rj1sc3X(9o^wi7RLIFyRLR`JEcgmc~6;*)j`(^J$mJpc` znQ}tym%_Fq%}`OW&ZT}yL=bM(f=yX2tZJoe=t<6rCqttaB*sqOlXN%Act@t6xWOX( z?&Z6UMFo7+-T~T1YE6gI&+nF-fJ+(Cfd!Qk1pWa2x4{_VhuN4M%zkTxZ zjLTYf5q-s3LJ*-hsu#6bk%1KbZmLwVxc7}mBvu43w#W9>73tdu3YjnjBwTH=Hy!Q^ zGw7RYT)Ek@;W5Rc)Q$T>a7!=CBy0t1E8-R))myb*5FW5PWtENCv*E|Lg6_D9+H{Y{ zu4?gQ`lM9sUgusG{Xwy!RtZ$utQeA~yFYqPx|Gv{-Yp!7L@dHQvZhCM3%Y;uFu^x* zpK;E$2VK5Yk9dE{)+9C&lr**4wt1Jb2?Xtsy&8iNd6ZIBXTvVW!hu{!VR*mDua@8P zaJUBJz+HsJ^p!bQ4I34?Zfr|TXbIy{{j~(IFghScl6%q`#qizdeZ7#Mt^h03J0oaE z1bStJ_6%)j+sTDowxWhrE4Sv;)jpnH5d5cJ>60Ef<{$$2x|5h}-vJ2~7mK-rG;~Pz ztN40SzElIY9bg<@w-jzIu&lkq4)fpkH0-54PFR`9qbvu%p2vM3)gNuEk~JKJ1oGuR zji3ta@h41%6}a+sI)V6qU6!vLe4RcR5@kmg_Ty}sKaRB+)B(d5UChQQM1Op-_o)~& zj2(o<@Ck$+t39|LfLA4~9j}(FFKD(nduf~MJH7Z99|w8@PSi@g3YjS(H!-f;`BW$d z$!3l0IeiZqZT*JH35QL+jOmDj;auZbrA)-k^vIZYE<|7S?8o|z;Z%kXjNusr#y6Qj zm?-x8i@@pDn~^*zWjDHp=NmG#V0dqH`KB#0uCe{Vmw<~c$qtSHDMoSjTXp4IU&6`s zr=m}@iJ0M)8QsiD(bc>IWeM~9g^_mQn0;b~`uR;{v>DHHfEzU^RNkL0@|g2?_Ec5^hS8#Tal0K z>ir@wN+LOEA!6>&m1qa$Sauqbu-E~PvNJxfF9YIMh3ZL!9%w9UJ&oXfsbw|G3A~5p z%RtpmW6_@z0+g43uxn1W&?IOS`Yt`+ZNV%~Cl7g(RU|?SOY7>*`WY{F-5hV_X;fD3;9)hTFSTczOEK7orK@m;lqT{KfmOFcE!l#WVr z%s1;XS?I6krxpZKwYdhd-Is>e=u9cW)4$s)miRH~+0EObD-?~hxDv@P1A5ySTWKUw zx9&G8{`_&zli%iSr5zC82qdHum5jVc32_{FA7!t&!va7cAYg7D)1qte+ujJHB{} zUD`MYq)8o5*vFiKgPm{dltCH=@pgx|h1Ov3y7PLV&FTf>G-NS{d}4O9XZvy{1~Xco z?b>C8_|;Vq;;{T?`8Y(lA;6P)7yD+2QrW)F4NSCq?Qsw#^5YZSl&1qqlDoMU#>ZZ-U zQq;3o&ID4u)_68+HqH7YR{ZujwP1wKk{05r6A;DMy|Ov}lL6Ny9AlD?NPLF7(F1<5 zSrcG_XIe=-9fN9qKFO0OlAembv>;-EQ<&VuJVzE*rxtlE&9vA^qqt=PQdE2&(Ts3q86gC@ngJVeW&U|zD-yCmU_*2Y;P(OAkR0RTf zLEFO}9(VDb4FEhmd2KktHL%@5S)B5>P8^L%xLX0-3JK?m_F)IVVHyjKr$3uUrbI!( zzS(vR?}zsfr%LVinL(#@tgZ#`92IrZ8{J}yoYGMIH*cKhbNENpcWr0pR;uB}?)yqH zU-}qlCW;3hk~q}b?w)5;eOvAl9|xp~WggM}d52msJ)rY6+ip$yM~LHf5exteuEefV zN-UW;Hu?l`?C*rCWn%|kM62x=@JOl?hR8GTkM`Lp{Bi&4hdrRr&R=G%s_a8fKABw z3NpIXVl0*2GsTjUVtwdpNxOwx(-dLmK(%PO{rvd=a(>Bys;LocRZu7~YY~?>dtWaN zlp%@>6J4(;|e5Dj9Tn&P6K{b=2l#VXHOutFe}$eRC$EBri{gkA09>zVl^UmDv!ef`bG4$HLkpKV%#?gK8dJz)<1%Oe3fw?FA$O}$50 z>kjdql!Jf-DnZ*ThsJ3ZIz?)GXXjaA8&cXo&(Jy=XDLB@x4*M-o#Gk7-wxfTN)z?o3;vPAy8*~WcH=!lujNe+_-%R_bFsM z^xz!{q!N`25*e;|Yd49v<hwvW1sY(#uSt>@qTZ2!=hDjE7!J)9)t!Bi>Qv2`OGFA`nIgc^%$gDU4 zA*r;szLwzR!=)(h`CU!Ub}rDB-kBB%gGU>NzA%{z9&ob>b^o=sk*?xs#+a@$sU!;N zVt9%3N9X>F69n7NDpyDPFhadQca)=z3VlQ@`1C*0a2Jo!J*7yrG?LM;n0eeYd#*ie zDI(7l@U& zHxfMe;wgA7=vvS7%u$hOz#crn_5?@+63PKJVCbo?lk}u|rE*C2D7qyuow?$atN*z$ zO%e)O`N)3*4<;YZAzQG&Gw;)y#nuM;qU$@dZ1jfkwpQguE1gucVsK%QCrKn2?7l)t z;Lv+;roWOZ*U&WGR5J&9Ey6zk zOi9qN9{y1Yt@#X*;CU^k(VseR(dhj_#od;xTn4x^(+Qpts_-4_`Pa@fW`xy)dirt(|L@S@R8lU*s|Hq(znU23w@13 zZUOtBa==}Eiyecb-73k@#sL3B+k{G@=Ar_6-x`}w8Lzv@WgXyVfJO;n{e5$U0_eQU zyuyD*NchH8R^;BAF?8yxStcpwnh`}`E+$_9?HfD%o6?t29fwfJu60P~D+Q3YO|C?;maYNBeN(OTX~B} zU}IwfA|gD?W7P&)mW!p!TfIG<3$5)+0$29b7Eu|pp-rsx^!3@NbxsMP=Y^l99`3+% zF%cbI3z?0TXJSGDZPYWR7~da8yuH%ry|**Z)Oucct{vx-SZlQ(8M_==czA&y38&l< zW+jAGE;J5KF7wZid10=Akr*E?kJwDHAS06-7Zx}mr7xfG8dsE9Y}12ei`i&b3`b(9LLFY%QZ0|0E9dtldF%LK2&3trZ*=`BS~|i872necJ9#@Ju{S z(&|@;*(X8*|MHGOPDRh^C@|H;n8s#-_Fvz5VMjMVM4XtsdYWRIpxt3eM<1pj89$6R zcf>=&iV9!r^MC|^k#jrC!Z$qMA6vjEd6sa}R$+E1wIQaxsh8b@Z zmCZf^&I8i8F->MaI|QkA2{1v#a2|ULq`R~9kt0qMN~x)e@yNa-ZBlk( zcE&k#1sLKTy;3XnPCXIyS;b4@w?)g&+wU}0`zJwY>fT80Us0rRU5je-08V~T3vMG2 ztCi7j1rAh0GaDr>sX(#l1^nlirw8S6v9@|L`e2Y+Q`&f&NC@AU2`Aq zyj!-CT)9Uz7!+8L_Yx+=!7I9bSN+_P+gLFX_+gaWQR*6e_A5s4ef>`u;pbsIAeFz! zbnonCz38ex`JA$%glIfipUHbldyk#y+1A!FB0=fq;LD-}a}xve4$hOH;kWp*(CzH4 z_RCt(kx6e?R-o8eFlLuTDf_tJ4rTUCE}ir?Ze3_jkq_?;Cs&T(gh=Mq?D*t}&|HG9vowoCT2_jWdn{d#Sh zy{{QH^`IdB5ot8bhhH#SnII$XN!;XzcuFg?<2+pEgRVe&Q_Phu&$T2L4GCZkYG0ow zy^@%#9m-su4347C!72(;97Mbf_qtOU7JQEzIV&A z0e`A7@U13i!Ubc{VwMIvD>Db2#j5DnzMKl3D>|2uHRHC{_o3j!q}4cx@**?))^o3# zXfL3{X#rHsu-IkW=uLJ9MRC(fw|%yOm1hWSW#qLv{Zb3^J?OfdZvoMvJ)`2mqe7{Q z@Mj4gUjl)$!<53F&(0pq=Avu@-a1TJi;G?AvS)=(CHXH*AMwfiIGcd#yb>Oz!8XEm z->33|9)apRZsqi)e7n9#DdUS#;4)gYKD+r-TJiS|eGl!#XOAK3TXGwH5{r7tuWg(e zhC3yQq=Yc}^}vtsXQ76@f!Mvfr$f)wFWox9uotdHRMHLtQBxqScUo3YK!=Y*Gy4iB zq%x}eovh;G;h02cE%LVE+vet~8=L6)ty?{5^9z0Ej|6Eg#*4jZT48!&Ob*ChuaH1) zmGp8ew*U&hqj-?b&LiCNMpPIyDTTST7$Aor=t2s6?5yVm=o}(aZ)(CnqB1}gz~0z| z1u!7VH*wGG9i7!7>*d{`?aRANezU5elG0(dtTM~Z?;EK4i)qkBpZdZw*M4QC0fggy z>}N^jkdxHbFCY4CcaAZFIu>bv4+#vv_VBXe?j(yPS+UWGXPCjMaf-6%tA?5Y z^W>%Ok%B6mlJC*Yg8d~NbFQhU4xke91!=!+Vk5bk1O{Qp@NV*#FlK_AP6tx$H%${mM6FUmOJN2h%pfY-gKg%{93%ytnWy&!C?A<&h+=ldN&f zwV&LPDDt=lb* zwoD-ck5z-Cyi)uAF3`pl0}1HD$e}?2TVCnL+I?De_<*6RA9$jG?T7fCWsf958)Pc| zi!&E|A4hURE^`YmMRFS40)FRioA>28k%OK$H`cRS7NWk?2_V1-B=toyNUBj&kb2;f zV3>+C-y>wA*!JAGv?U0kOF^zW1VWIiCvVv9X66P45q;4KAjjaQbRXPu>0c7uT5n3CXE~J-{Kw@a+*8yi zaL%1^Jm<=-CGEca8rOAI>*F~5!F%wRTf6-A%VQ64-Y_$TA&)U$W?UbG64@0(L+$ux zro*PoR%6?j+LxA=ob28!Z*6VrT-@2QjjlM}E-1MCaIZJv0F$bw zqr=J)_axH|(@~b1p)?b~ug{W};3!we%Zm%E=5QQw*Q&tuU&GlfSAGP)P+-V!H_Jg@ zfX3per%$DjK^XNc`yg1I@;ipE%LKbNwuvp?M5F97G&WI(mBS}s4K{hhcrb_iP?%jj zXhrnJ@;;J2{-KGMUk&`3MkS=Rx}(J}A@i^k9-ArDQF%F2u0R-#@~@%07oW%C#$4^X zT{x0kwI}2}`6BYzUZ1uT88>%c@W8|q9=hr53MoZ@PlcbOBlz!aEKfIW) zd?K;$mvKB8&x+qA8SENTK38yB67^Emj^X=m|GK9~$B>wN42-N{b6k6m-#Qf}HeI`v zQ#KL4$z49^X*kmQk<5TDQlIM5og7P~&T4_%uC3tGTC(wb@x$Joh@JABoV9KI)mqM1 zuU_E{^j)+kyrk05QnSCrITcT%C#9^RZh5?N_BDL9qE`3Qz*gVgN0+|D=7~p6Pe1f@ zvV4XRDo;XZ$|H3q26Ev+Cn;Re+~cawpRle=u!NF~(m+E=RAJ$D!R4)@n=3oZ+Rsb} zv3{N65uqz5+Qr^IJfxWtxEfr zGnZ%qn%LN|t(a_#or#7f5NSgcStx8hHUqTY*`bth#!3i;__qulP6;g@OVfe7CfIM7 z^*JgKY9GvoX604t-%hZLsAboegu?R6%e4ddYeT2g5WHkAXbpIWr+zdWbE9Z>wm<(8)3AIDD8J^c?~K4eMsbwaT@nJyy!yh!vR zum7gvd4Yl1SJVUT(HI$mf!n2K2Va7w+lxALHWi7)mx!IeB<*Jzlvxa2C*S|jv(Z1Y z+I#6?-_vzTd>T3w%r(${F#@veVwrA^)XCc&sK-wF%5Hp?&wITxyC~+eHOECVLxLkL zPDE86UbWe89ugnFLTi|iXs4KDefT_L}V};M2DrWfU0F4l>bg+CxtkeEqV9ospK2{!KjL1y^p&URNH2-JI1BZJ_5sT)#Gu zlgY)qxWrW*w3_pJz^LqzIJW)iJ;u$!ImdIs6_cU$WP|z(34a>?cEwJc>cJgzz}R%2 zje0+B4Lx5EcZ=Gw(w)F5P^};=M>wlN_)%27=YDWge|V){`E*9eZ?JZpYE2lS!J<*S zuiuwS>NRj;r|+Q#Z_wdFj1O-SpEW6ea2|gi6fv$>X2Qq`pHL~z zkClz!RL*X6kbzTi707)B-aA!_4xBzrrvO$9W?-HpbLWr<^(K@BmMF(Cq|t2)!B5M15q0N@zGD0 z;wP^pK^KU7cHCr$|si9>1d@E{S40^XS*-|B&_7XWRYH zKKrE+4gt9L9b=M&=JBibi5oRqZ%oS2?o$pMmxb~m1o}Lc zaDQGP@SP7JAf;NWWk8)iB_zz7&hX2_{`Aj)D~Xql@)`siv#f5m38j&n4=b4MwO7Cy z=a#l_&&m80dcdK0r80u*M$I_$Q4E4)lm7O*|30|S+7eQYhQ469F%zai=(@@ zqV1eykwc3C$e`cchN3K3wOh5~(K**>bN&|t{!sAL$G@8J$b3(6F28n}tof2vq*nd#+>$(46_ry)dD6a1a#T{9F zakgE|4_0qz{oLdC3;!+ro&UdCmL1?XW0y23|bO7rB$zfx+7BTy@PC9Urg? zD(2!}-l2meCwB~)I|=Rab$(*YG?-in%ISIc;Y3TDQuwkn`g`KAB%AnDcr!htl`boi_+DIf(4SN-6>29O^vkp$1b*{0nxL8~pz z>sQ2!4EdSo^tt)CZvN_fe|(|^u76?W8`eda;X)Ve0}u8;2mW-^e|E-5h5r)c{*y?&4#^81ouw~`MOYU{kG8(>KA}3*yWE2i=q=B7t8*0yGjSEOl8;cc5R%!4r(e@ zNUgM4q>YW$bo^kKDK>7r(wyUDXOb>8jb=SQJljmTEG&!ik06M?{%C;IIy?Atp&Tn~ zR0x4EXkhD! zX)xOK|9u}N262xN&WEo-T)Tg_{qG=-QnB_H`_24K9|81{ym>oqw8E&`NLXi?zs z1}|6Z@HAL*XA5n|$hD_;|!o^q@wkPi(h+ zJZx4kGP$vb=x}gX8JpkTsyS|a%W$C5kfLinYrr~0W3eqIyXyK|^o+?mLvTdMm9Y_g zBHKsJIt>V_lQ(3%rl(0mU@%TFkkTwLsFUY6*Z;?{?eqTy(ETU313QXBLMy7(i^7|S z5n8Br{{@OFE3rbTxL}nYciw%~d2Wtj#uw7YpD~;>=^R0^jlmL1nuXIQDJKk60in}H zM0g@5`RU5aXs$siri&^e0ebuQ3%S7dFRr1`Q5dryO?K28YuSAjU3+^O>rkTtL4u!?#3jv3b&X#jI@f`SEQN%#^j4-HjR)VRe{{^$?Bdg%qm z|HEo*2bexmPKU@sze+T8`{c)lH{mRDHV#reSk?}rah98SpEl}uLre&2D@{)P9Qs{+8V%OtOio#vxhPz0CMCfXWY4BjsU6q-mmYoi_(&Rz}<4d2-48Ds{9oEVfO)d)LL2L6E8wyp&0qPMlxyx?1Qnv8a`WY`Cx;a4fjzFI)hM zB@sslt3d95urn{+e}lum-O-m5+pt~Fwk$i7+Xpq1<&(|vnCUgE>iTOM)u=1_WwM&t zoLT5?{g)EDa&PUe6KF2pltNHA&PpU!oSvSYy#sd~>eya)w))L!Nz|$T3w&>XlsU|m z%N*qLE5jIhG)ewkRNWF)g=2I6V+D;>LDF1~@NFiN?V9*A=TyAnvLCi|$entvs$H(O z<$c_1_DXQ{klelD*AWh^3wf3_%pCq?XMErVge``JSHBG<|{tR^dB=`{kkj@ zvWxQ0FVOsN7Tu3?S@0d(tuPCD6LWFP*+C0BHWm$5#vB|M$06$KXl)hLvH1_My5!&B zf^UB8Tk_ z)Pu^yl5_ad4(^^@!^D02cAKe8e_hASqw6SRL<`2Mg$S=y)(%_xScu*Uq~1+Wa7FeQP6rHz%6jJb{0zrr0Y5wMr@Q;NKosJ>h z^aVcWZhU+u@y**By#9JI`DXzSe7a(5!#r#3L98WS+Y3~JdQLSf?_5zQjK6l6!C^Im zn_8$iuYdd!!Jbr7bdkx3I(l(L?|k;y;-c$wyWG9p%Txp4-L1a#c0VM7e>XZ-9KPV*n%ikrP>yia$>`=S3qL#Gr=%8YPS}hsG0o9OA9U`_ zjA&2g3b^FEvLTFa(Qz`VR+cFfRZ@T{gM$^YG7?(N^J*>2sQEXp3|ZG!$CYKMi^kdY%{Shs0a&{| zQNj0Ta0>QMQLgSpkd$cH5w&n4!a?i@YC*7fB$4R1z!(z)2!OdN)n|zNw$_A_r~$N4 zxu))}D^aB7?F81A9Nqp$ZkBkwzMz<#vN+vpBkpYbE{(BeQrIa}%Euw6^!U<1EormS z)L}6SY13&~2}ik|eHs53UXVD#3qg%Sagr!+qG3qjUg!NjD~Hc5&|yo^EcJOEKXbdu zyrI{uwzX#}zAO@=St=_hFAu2YX9yA!65E|QKg@u>)ACF=T&pwj;>!AA^@`m5P!ZhU z{WJOQ1UhtS(Q()eU(mcaLlU*NaFTamv|OUfRlqFv@Kqy~6qjH=IpD#HHp=o*`({r5I_tiBtvFvUAVRwxvhlORh#2&Dly$PFJW#`NahKlXWlX5_~WtN8x z?9a+roS8gHzw?0KBEJvM+$cSRLEw`^heMtNJssAMZD)*M zmGI2>$|P)+p~Q3wrN7StI6J83GvOM`$cRg1vxo0`H%1TC+&M?uUieziXbs&pWR=&7 zY?cWnYt`zB{4x?Wni5M@tw(ey82;tL9eLTXmE|69F8!kbZRHQ7V9F(8lBkN!*){_H zaTDd>@Nib5QKvLUL67;m-5Pt;oOKN*Va$H|oNsHAFV&|dt1Tf()NPhoynjdoZv6*C z0R=;?9JFEDfZab(b|*i)t+PQN7CP&k+PUpZe`@D&UoNXdtZaDe4zl|iV!}j=50dAw znBTOQxuc(3+QAcple@0bp;7Wk>#oDc`vHk|R5itW92KA)3bCE7SFSBa>;5K;Y~PV? ze5HFcW#7Y9*ta)peAFAER7?xa4R)P~pR}%AZNJ}L0kcMB{ke$0G$q;VeWy{8U^j#AMF zyPXv93dO98^JUVpPcKv8CEOn~uPE7fcu{i3(Ry(R?$90$>i4Lkwh~bQQ^EVtT9f8@h4HtFI4#OFJn>f6gAf=*#oz<*Mmcs+t_^z}<&P4L06|mbg}J zB5AWZ^h))?$bEWT`AAultcdRS!#XvP39*nUZH&2OUXC(S$1~AxXl9^wgXG1|viEXW zm01rnx%+rZf2I7gjnmk*+d}qh%l3KLO|k={GA9t}D*CND5y@PgYCZbk?RupNice-ozEOOB zvYpYw_20qw2l!JD<<_rMqj@d)6o;Z~J|9&4i1hv^;LYmg*=F45W9#hMYP;)JIlGm8 zHx9H@xkWRWzSsORAjU{wr~6 zg_;6AhmNo!^iX~4LW*o<__Cg!K38o!YY4YVc31+6RIqX2?wDI`Oy%eN@1`mUq)`SV zqeS9wSka;nxOtpD{Xyc3fZ@>*Ntii`BQ{rzvtJ5`2KNa&%vBeNgda zi4iX5_LxO*v5s@O(u1uC+v_&zH?%Rt?T4{Ued|daD3qA6)LZLqwh_tRAJGc4K43f4 zo-{>YOLr~Re?0hyS$O=?N{ExEHeH@VlzNT|wJ!1Y;Q_YU-P7 zTmRCqFW0p*W_6}xv!dxh$U0o)088t5Y1n;o-E}8S%4Y`uh9|G(D}Rh>>X_r361yGB ziH=Zd%{9Fsr6F?LL!E{t7e?`!ug-Zbzv3$v^4&CDO5A+1*!c_;>C+mS64pE!j906D z1_se!=6S1;Q9{I9ddQ`*v)%Vra^Xu?(Bf<9V>dRU7-pVg(AUP^z8{yNaaA+v%*8qk z6d2(PI&EEpvgWws_1Y|RRJRpUf8*7>m|5;UYf7SNKPrM8YZFTNs|b?KIyYKr z`H6eWSC`xUyVI25INh#SUT@g{bR1@6^0##ejSG{2e02M+Rjm{Q!9tlq2ouj z9EYxtOr)aiXn->dQ4PXyozV&xilyGJTMrB@&jtm7wgFAFw9NH6eH}wCT7MB8zFZx1 z?k!}QVMuR(pA%_rn>tV>^mZfHjg=KkDdDBoOir+xY6CF4u4cJp{<@`7Uv6EFUh3U5 zco5G6V)9@}iR+?^xy-D2!oJRFOI50_gKC2T=f?P2yAqWYvAk1y6CMkW-DAT!Iz(g9y8uEBIc&;$k1!dN%-#(g z&+(?-xgFwG}9fFdXiWqucOV66~u$C7$2YNpbSwa&{-M9IbSxL_Vhp2fylwATg|h zq5CrDVda+2)>XKQIi{I`@QoCaph{MN3$eY`T|?&I3t<9e{|i=1Lgc9Nr>U$l=1*w$ z(*G&XT=wLYLkfxQl6$RgY%rFcvq!fXUyGmw+qZi{cz*}oeO_?vG{gSQ)>!YRe%^gc z7{dVVSX)Ant4aNN=MBG1{Xr66J<5a?W1Ng49hl_p-fW$BkjDYr@WWK?`@5?QZ!L`{Lcx7#)>7TgqjK6m0ax{| zJVLexM5OkH)inwTm&b2C4>;2-Se%!yFB!zMC_w0^#4v3uBb89$iiN26j@8|J310}f z59o_XcaJR&JMDS6GYRu(m~iPcYKgf{@8OURZDo0a)w^K z!|<|^c$`_~w9nzl?b4;;MlssWjk~*+*hg@WP1zB-)vC z&e@e&hCd4O+e-TyXERUx!&f0kJOYEhFHgGbn&f_)>Y-rva4$=|Tk2TqS=8Nz@yOyc6}+`W!BdFZeS3v?GVYpkT8$A z_sS=?HLrbH4-YM!N{eWb-RYw1PUCK_*&eodu(Sga4H~yO8@P3whx^i4EDsNl@mS@y z<4ZLBRSjMBCIyeGyEm9q6{WYbwMysy(U`3DDNdk4Z(Hm1mMSWGq?v(ejI)y2uJ@u+ zf6bnVe13&}P1(2yWZd;oDyQ`b->zI;pV2bpmXG8r+V2!!@R{EB1)Nwy<*bJRXrxunC3}5fgFYp-M>(`H7gNM5oEhq=kToDz1 zIJkVM3l|A0w|NbV?V(OsZlK`VX~(&KRlJA4v9))Z$K-X1uzkvy`)Dp~=GpO0ej>$S zdh@b5eh+OnT4naIs!}uC6|6yuGAd%HM98#Jd@j!Zt%_Hve4*`arWW0>d5mxC)g2YJCHnKK`RL=U@Y9}dNwhfnRc zIY#zQGoB)~$2yF&Rm_B&?P!ll>o}=9(oh^&Mn`ZMjFep@IKNm>{@7dp;6sq3!N>}E zi8teX&2B8w*vxKyle=)AV~d#?+I&E26~lGG6a9i&d890FiRVVA{)5O>WnDUMciyJl z)oR6f>iGUpYbZy!jM$e3EAfZgHB_}wWq58vq5eZ0~!lo{5NmLV!r>;lT z^oY&Dy9W zG4u;E$jpSaW@}94w;To<+MXcu0dJ;G32Y^QB!=7up}El_>Fxu zJ$8)w3Dtt?*Lr&XuU#Z~--Gp^eI45!1>g8f3dp^+x%IcZxh$PDrcjF;a|c6P8ny$j zn*iS6NZHM7NV$I5UvaapaodtlD!B?-QF3yPe>}AlGD_b?vx)A0Ah`aXe5Zt>V}E6q z`arPsZOCcC14KsQ_%LrJTM5g?TspSUfO}&0ZRPIV8YOcZp1H!O$twDM7Mjk~qvB44 zB68^CZKNRa!(k)k7Kn87rE+;XyLVc0}|3Hakj%lI}FnfD~x4hF5U)*vm=MQPOf6xVHX|- zjX7oPMCDcOrMexopd8^ma|iQKNUJ<|tyP)%oxPV0L~kr}Ao+J>Phr7iZtFY9A@Ym; zDEF;N*LTuqCI%)Mg2Y(GmEs>v%#L;?KipsTiP0N2&VOv{9?vwW?~<${3B|ncl}(GA zfkfhUqA9NFSK7}nS^GT%J!gz16~A|HeTaSP!QJ#<^N$b7fJA!YW}Z#{74UgApEO14 z1g(R{d4A?Ti_rs;6x&9lT5Cdfz4xfavQ7MGC>Lu)QR5@L&d8(@a~0tVn}^n(#CxRX z{BuN}hW8=x!(jd0)hbK>AAy4I9P*GwT{G5F}_2uoMgox_7t9AQ53%^}D}%SA}y4x*($Ox7<%E2-oTTVl1{}pLw@e>XQ13;V}#O+5-2NVSzJ{PNV=JhvXzP; zH|x%1^MloO4Y>DWV0UGay(Ll6W(m+pa(plt5o{3U8u$Kx%zb${6zUuHX;p+ul7to| zOBA88OC^M|kF~O8k1;b@hEQok_AG;BUt=&am{z+XVhqMqma&fP%NV|QTF&z8ob&y@ z?Yh2yy7WhldEe)K*89F6fcoCs;LT%UkJ)kHjH(1~aCQF!xBak?>ZlTg^7PWCE<%gr zOy8_z6UYN?!tu18rVM0Ju4Q$L5EtMpCEuFirGMo0>UuSOG|qB>GD&(oMaE_A7{O=o z`_L-Se+r}6Us4BP+Ynk$K2C1jWBNR`u`8IRXM7*LYYQUL-)HPWNbcRj;JZ=dv*NFZ z+kHb=<(=1kyJ&Rx>PhYqou_f43vpp{|MABAqHY!m39!4t_rijPvr(yJ)T zuZt9qjCOJ4u|vSB@Ua4M(HNM(!rcIcQq4G))@j!k0r8Q1lelQ3`X;Wm*j9h517}FX z7&S6xX!Y&Zi}pSxWY49cR61vm`_!A!~N*SwKyA z)w~$as#mX4+lvo`eN&Lts<=L3j#nbQCDg*Q$;|7e@Qm#GMf0l!xV-~5Mmztv#l31lUkJtQ1zcWk z*$0Wg%(GPu_A>3Kbp!wuiT={H(1eAnt^|K3`0-!DR%&YMD~`j?U%UNY1{2hMN@MA| zqoAOiob;Y)IBm@_^{ti^qLdj7?`_zk*E)BiCt>P?^h%SgQpu{CgTqS_l*pDujaoB1 zLRDg9jep)Q->cgtS902+ewXmZI=|6gsp>;bC{$7KWRtmVdeg2cynt73P=iVn(u8t# z(E-sA#Ymao@>{UgJ75OP?^fUUA;irT-Vw5e-5=DYy+uy`J*g z;yA_W=i;J$R~M_j-V1aI_SbxJygNTGl0~K$@He`}#fyd~VKEleZiaW?dz@xd>E1Pn zjA23RvpUx-H>x|Q@dwMCsNG!GWk-^}Pk&#>O5VLIHW{9}X{`IhJ~e84yZ$MVtBjG< z3-Ppv`%U06^A7L69NmJTyv!1a03EZ<6XJeD+&r4=C+J4Fg^t#m=s2qd#_|A%eB$do zVtxdWa%}?L_n|oJ%m&aq%hU#gD?w5KzM4TbbG*@3k)MBwptLTv?$%!EJNrCb#g5#| z)5EzS{_IVIeP9_X_A<<9pfUXoj9eM?< zl-gl5eC|I12Z5P~FDD8%F~O&0l*b;inM$UK7yvmpSRVTV`2o~=B?1T}Icoerx)Mnn z_j{FJuSK+-`Ox^x@l!XySNZ;-0q1*K_hMijYbtwPpI}m!3=rcfC$NkZNov+46?%wj z4O|S$l-ioCaCv2@thDHZtiw|h6uGE_HW7L*fOOvhFr0{zng}4*bo9&MeZ#hj>jM{- zSCby4-*|o2-2PLJaD8kYKl$p>AcD~yZR5R7=WT?S`H~I|0`>@l$E~p0tkk$LV9g<5 z78Xa7a1_y82y3dGEfLU|>QWB_J#R~-|H@jpU5Q6jS#ixgHI3v*;=S(Ql^{k=X!6dN zBnf4a7t!R8L{>eD?cHp29h>#qiGhGQS6o0t#HRQ8*Wxzt4_2wOoC#@!SvSXqAEVcF zHM_NJ*c6oX?n@ZqNAmK7`%(2H3~l%T;rXc`LZ!09{bfM&d!b%JfV+2JWeh2;T*u`A zX*y4`OkD3gP~ru>hQ&j;nmQV;F(+#^WmXDTh&w-C_`zp0n|v=iQDv0V?DPRfW1f4@ z2?9OdgPmP|T(MP=o_1-eXAzDX6A`X|>0tFDtF^H)8n8(Wc5`h)MsyJ_f3~GxCuUu4 z-R&gd(6*c_CK+*pzPuLPAcHFFFv>G-D!`H_IUlmHxtQQyY+&Dv=DS^&@v|58d->Kj z_&11d;DKhW5sC-D4?J4*0lpUFH>r5~``^oUpIOZ;vZa+=_g<({i;P3theSBD%3Gss zIZ4QI3mm0~Zg8RRZ*5X}oIB=2WQi7uo|)U6y!9(hp(16X8ymWwfa_1BT1P32 z}!U5dvTuZf7uWFU;_mUawpL>J2_0&QsGaZbqA|L9~yNeXuUa zO`TM1ecZw(76;!(_T}0I3xeA0{u(DO(VA_rc-pA9*_|S1a(ahL*i|*^qVS0)Q|qP@ zv$2Iyc^5ch3w%qZ<4o0tSihwz5{*4K5IR=H<%R^*ps#Pzy_hLbqm?#k%~~(cPMocn z4Yi(}Wd^eZ8*KTw9!g4EdEKtqQm`2AzzbLXR9MAXy@<`+viMH%Ihj{UB?W2we-2h?fU>wb5?iv~cNKmhDk_|p$2yB_ zn&~xN9J52dzupGWnUJ?{A$ z>?vMPY`-G$l#hkaw71laHe*C}vGOi2H*x6jFO0Up%!7aLsg@eQ@c#5vCxE9q@dol^ z9=9*>_4B&kPB}@w!v)fj!CTP^WjawNk^(M^&KJF#6JbMRM|`!k*eLc7Qgp3FZ2F%% zXLe2|4z;USHUuChZdgC(SFnxJ0rJ6{>$I~Sddma-!y=r6AjkIoD+E;1!5N^rY;1(kqASqIUw3IzG2;&@;#JP2TNLEKC)vUy)tU7n1 zpJKF&K_e_^a*6ml09e2BYP`F68?WL$)$VaMUhEKxxeA=#alC5s3@QLi9?e;c1>yLAK6XsW@HKG zayWqMExC*T(IOcsIr_0UxH%E8s=PWPEml9?%WkgTxxWx0@EJ&ga(bOkw=^szK+5{0 zhUH1uA4zO*9TqY@Q=#0t+eM<92o#AMs~1(SQ;b{Ympi>c)u$CW|478dV%{1f1VIc2 zq_4*^(m@E$)JZuc_;|gWviOmU6RjdP8x&SX;)Ovp8(C@ll#<$?N2{>WV67S zIQLg21cyK2-2VhYRjp$7a*1+vmEZebH~coR%VUdYe2T0q%T(T3bc7KqBe4>K*X`cu zPl5#-O$B|H4^`eN&h(F#+%ST61{Hkr4CmmTnYkOhYNPzLY!V2~xjjZj=%O4bpP0bGlBr+p~L} zIYkwK(s{ES06Q)S_F0={u-xMa-hze`hs~|xk74!}u*;X-dr(x0(4k?^^R4e=7p5#O zxJ!{(kFzM8uJ`LwGuQHroWkmOC2f5M7aqDA(m$Tb6{1C4C~!J3)6dB^IJ6zMiehoY z-%WaH;=W#i(KoEp1hJcX`)Vo1dYr6a-w3Z`j{x)1i9%CW`K$~ft;K~=+EIEdoX3IO z0*3mPThZzThl*gr6_Z_jtLi`nOjs@qY<{5?re|a{z=;;dHrtkBOeoReD$9MS2RAMv zDX0teeC{>Qi;!Rf?IyhJQNdRB1J*tb-tFZ&oy3=6Y(UbE>OEV49Aa?h2E7J?#RG~# zKkGU=cU*M+T^`F(&SdArm2lLAKV9XqmCrJKc@8641{v}Avc0a+3UtMK1Pehb`#I89 z;MeZb!~X_o^qrHm)MYCD-GfE!>Yi^8AIgDk)k=)5At%AH!ys;TERKl^1JPUpb>z%! zv8QGH0Z;mAdLP~?&pMp5u;)S#R+rqDbdVEOrE0ZW{tEvbmR5G<#;1J|c~-01HBUeH zp96~ZhmV<3nrGI+dhFdEj!VI>AGp-^hK~plR#@c%Q#h1{b z8QLP-ASQ)p8EM|7%g)a^(q*hm&I&KZ&^{}l7GkVmXD9fR&?t0?v~Bon^k5tREF<8F zJl#RU!X_k!{CR9`*>z~?vr>i$Rh;_u_R7Pu3{Tn-3cc4&$3LCeUWQI%W^;PY)C0M2qG$7e)s>=vLLGi@Pxd-<7Qr<4WL61xJhUyX- zS=YoFb&RDsZ-XP*XeE37fc@k}3lw$Y<`y)M8BW(4HmS4bJ5H;oPhqj^eU{5^UlwNu z4O&Sp)Ff2pj^1ku>rX0`wX-X{+PS;J=;11-BtBX8`7RbLxb=)Nn4zDXXi{J{X&r3r zr7>v}206R3kh9FgzHw{F=R2veeLLef{)%GCS9SFlANbF@QD>nkltukpsgMd~1LLZ$ zqLh$t^Qs#`8}W=&+CY;{6yS^&I*uMasu@=mR${cC>tg9D==(YCD6_FCXvP5wS{;Z= zrM>xwn+nUI1t+w9utj`p#%I^W{=@`w0u_RBflw0VKoB8xl$wOAO4Bzkl8ln+ z(&%m8qtIuOsL~}1B^HSH96xG&wGx%+wN9%iR5<4rk*|`QR@#o>jB;(w%opXlu9z8L zD=0RUjWyBAEbE0M>~n=iQzlON4B8$wUsqI{|5N~V`yGqrKvlYDhR?tG+S>t=xZhAx z)J)j9YX!kYBu7h2JNGx>WaXAu5~pnYq$PXQ^;9O$F+pj0mgOD;7ocT6p?lnq$8zxZ zXy+N9$P4w5y(2a?=Ql(?P2(fTJ~@XhZ&<=^KJ!<7`mC zUX!U8W;3|qPRNAC<;w@x&3c0hB@4bzjU8~&a1aX>2L;Gr8IrFa?Va^_&) z_tu9>xRg!p6oRod5EhGCi8f8QyZ8;7?tg;j?#P4k2JP{X9IKDwlETQT5#PSW(#7^RXobt?g8Jt0N{Rjw=* zt1oB^XV`^X&8-d#E1r2fwlFy}fse1KMLv^3idE3Et&B%VagwmDXt8zf_$8`S#ZW;- ziO<_(>w5yZg9u|0S5eYUUml`^`O)flHiQ?)&Orc5^JZ9`Ah04&s&r|1B};*bESc)uyYj^yg!odKLDxk;{VpGcr2WM=lNs6m&z0p zaDM;p;P;#Oa`s@K8Wkk#?*49EhFw=NvW_{Y9IPEVOWrnx{h*)mS}lY;NAdKeloh}) zu#Kh=wdC!xv?^k5CR@HxTa!3mK2~$+wMrs=v*0d0rHPRPnb`MXglj=Nq}UbYSpuIy zB)0tI-}K1%Ss45V8QG$fZPQa~J=9MZ+`fDc3XS)Cz3iYpWu>GZ#?}d;PeEqz>pHoz zwm4U8#D-1f`CYo|wt(oUQ5%o@)nelZhFj>4;p;YNVv3URW6)sJNLrY%u z+^xtwf_F}K>BPJ1SgQ3N=J%3i%h_wr(Y>MPv1e>&;Y~!XWpx9fo9ZgRVN+hnVkgcKzKMzP=O{k;@&JaS1|YA`GSs zAdN2&?#YKcvet(wuc1ma^2o>khFd+G)C|84@rZ4}}7Pzf? z<{xlw3N7&w08i6{rokIP*IwEt4q-V0zQ(FSd7W!ag(CwKHEe1Q*o$0B*7{Xy5-ZovS}2oI3LJ3+-V@I&7rumY zm)sb-kdr;PcEQt-r?JJx_$RHUQ11tQ1oDl37AN5y8L?|MZbQOzw${hLx2t}qOP={} zn1=spo_{^O&$A)^DWY6)5@C75eHk{%Npe>&UdpeyI)4F*d8T-*>&@XW+6RTjpq?20 zAc@NVyJVnWXM&(ae2k$>Y(|q0>nvFy;Wf7HL_kAyzgN3a{rcIUK53Wm?5SZFaamKa zm)+9xnB7oJ=7ZfE^a4wx&yDsgFG-Yr3$V?=@VLPfi2&-hFMuy#Z2a^qlPb0Af3Hjd zb0J=bMN6<;{d~gGa@?c!2x0rR{XBY*T*^V-2%=18SEUc*7_>a|QWYM3z@W+g)RjH; ztvXSUKc=UOwf$g~6mA6Mhd8kR@-(rI`s?rZ+kZz|`u8Whl{(eDZf=E^md;G^vTgPE zyoI$oRZ;sUO-JAKTsP#HweI5+qCc*-@Efs&$@98$@YJa*J!MVhDhUI#fBR%D|Ie*1 z3PvU^=Rl>?OZHOsg$ikcyG`mxUTvNnoRDop1I)B!T*-_Ul83sj`zIskGW*Y$3*Lq> z_Q}MA3&Qgs7+*gB4dd(I5AOn1`=MOD`RrQc18ttVcLr1Kr}V-m`ZWNX@9~5ATPLmq zI-iKBL>&AmxBit;2D^RH@`$_H@0rN6(}Vffn4H6?+q5r9F)@xPTg?j>zFKd-p?geG z?3Xm+;AQjw21Ea^52od-#imvj3CSlE6Q0I5?BnU#`S~A^K3(WUjK03@qu6+0^|0pUmXYpuC4wv=0YWwz|K3rJzrw`xB3@;x)?$OlK z!@g|Mugveje#uw= zR015V{Xd8n1f0;(WA))!PMB}Z7)F#;3Hx)Nx0$khTb^NoSY_1Xevf{*QQ@X^B-ku;MZ-;tcvtY zTHbVvQ+|9F5Ww#o&iF~rd2?{5djNH}7sp#|2iET=_D$KU9^ZW=1pXv1Y~;#cY2?3$ zjK5}caGti?Prj+W{4q>jyTv9S7iV(O+dCuLWF%yMH6|uDFi!NR6h_~Z#@F71 z9)EABr+PC5H74k8^=^bpePClK>%PCLWc|ssKnEbwv)#jexmv!c4far6`cBLZ9W&3p{^pj6?U-_05oV+n;-4)LqGiCYih>C@o>C{yE zGX+SZ(QYr<-O>N{Bvg<5gLk!ojN~go;-aHThp73@fKwli{u)j6P5&+_6F7YMvPFsJ z!>*Wz{0LpW2k~e$Q}@N>Q-X~1&?8Cwf}~Fz8W|nN-%D@J`EI2 zOo@RV&Z=PO)w2qw&O;_qX8lPSBrdynC@yGe4I6=mi^ha8HE z_WzJ%xnFpDy8`y2RNW&c*rva|g8tYi;ZTxz&SRI4nxaKTT_2~c*+IzlA zHT-@>+p?I-PV?Gk+u2vz=AT0P;4qpvc|L(q*5W;~dLR}v-y%LWm3`>$pS{a32iDn8kr8#=cSA+Waadd4p^n|_ehy`2v zzWrz0Aor}cYBLq^5(0t2E%`93s{e`Z9netXQskK5_KW_4o%mLCQCso&&{!m*2A)>jEz&qLy zY0t>p@j(&Pum*VAA`k^ZN*8{6@7Z{86f3>w2ebes{xUWBkB|E0r#A6_NOg+$0jANl z=g~1n7ZbZiBV&8sJbL_7h3NjS!1mCg5_{JN>FGjHoP5>=xDha)Mr&#!5cZJf<@sjZ zr&WQ6-1If=ridE#cq&Z$U#$2>@;@2z4*4Px(U``ES`Ds5{m97O)%8C$rvmpe-O-xW zx7!E^z&2<-8p{P|5rZel0qby(0xI^v1>d+0&SLAQ^PhoxyrN2t{pAGyh#PH+|8y36 zy}IT{b)RT;7NatGcz(u-oi-b_p^*l}f9W0v|9`G~2RWo*4g_?VIuLKd9PP zvEkO0m(i)PJ#7_7f6a=1|Mb&(8`!6P{)6uuUY3ek$v3lfkp>I0SDb!GR|R%+wWVSN zNUp%U^!Gj>a|2Z}GV*OB_(&ko_f39F^uLkc`bLrs`k#6FeXB#CJ=>?~>K-3&ZFD!$d7ldQHB`sGQF6wdK z82F|R1^o3kdAUOOmrVYM+m+$(qle$`>aQ6|(6+z#eSc9912GP$e>}T*mYXE3< z7HN8Tc(l=d;Wtw0@3<~BMra%m)zs9qdKG#gzwP9-gIbxg;WFi%pNOsV3}|5&zFG`ln;q*|8DvsoUmmpv^XU-`0F6$Apdc5VEr zdXVK?5PE)xbCR5?4Vcce@km4hcA5n+zwGJQ%1!f3IH`oCyms>ZV^=e5Y)qapX@KX1 znK;Fdj!#ukWy(d+aEplE^x6jX)1Vj%RA)ldcd9XO_Z`REg57&1EN%pZG_NPpyV3(G zs(Jnb_vlhv{w2;FY@68JrxKjA{S~AZiVTyPG?O2fuPu#7AQLevrl>qM^(5@0TWEgt z5nTfN_x}F2MCO%7(^6pq&z=w_W;5K6>iZ zoJ5q$r37)Clv|m#uV!`Rtd0O?zDe0~u*@9*;_n4?&i?1EJvICxU+rvS>Cp;tjU;|n9kZovS;4dqwuS2YSeAl zVPfA6|BW~>nbSA&H=`wSt7g8$>r|&jin}`yQWgU!;s(-zlEQT*?>fuHre1fv;7Q%! zS5sB z#y?lJ3D3c3DDw*#I2Ywu_a+IBO(@C{pUNWJpuN5eG@ec(7dRs8Ox-wZ|M+7DS!+(_ zm(cPp5+OTCJ}}5dFcx$s+1G!{HuLz=61lDe5{nVJ-QZu5Fx2{K;Us|J0c=_X z$>-~<+Y6ORmzGu(D!w$1=UGavBuNjZbA{)13FL!`*Gb+UVB>-Htj$kPJvX~a+U%l} zBPKPUpyhyvR)8$a_Y;^oUF0WKTiX;@i_- z?%&voPVrfR@XFDa%~a4^Q}h%j&uwfi+iP=>`K(n9ctBpX!d`c_ZF#zQfH|GsEI#*k z+Fi>{7k9h7GATMby78LE0lpVeGr9`2t1OS5rS;AP5mwlo9FkFLuO@rSU5|vT_~5;! zk_FqWQlcEeR>f@T)*{3xUy+jJ^0K+1b5RaB8XeJ6rnX)L&r=A@zD#v;9fK79O*Up- zunzy&wYDY-^+?G4{<*uy2Z@>KPcjU%h0Kh4FoxxOue7J>i)84FFf!VMgy@kd3NKi< z9FPoCs;i(%PzmUGTys_HdMhTGnlvjfJ~3X zG1{Y36^1F*lzswyuzr`=P(Hpp<5ZF>xkwx9p%lLR&!+fG;`WM56#;v(PJd)>8QM$D zE`z5?Fy=>38TA0X3;z0$lN&ZZW8ed;JmD%mIbow3P; zSN3Rn_b*}!5HGYY1QUR?O?!c_JE;$S)#Hi=z+r`y#Ws1gLxPklx1MW5uneGIpx7>z(#0MqwYAQ`P5X|wyA zJtDY)J+8LY0(-p2=N`e%Cu=6>L*-`-##+fiCvS?Wte0G-@vhz`j>2jZM4U|Qut%P& zQH!X4!r{sbr9`{Jm#b2|(@W}BC|=hj`T3MNJVRpHc~q(>!^+n#|Fn}KC9l;QXql*R zGrqGs`^I&@5pgp?ahBaq(vpUP&KPXWOoY*N3lTUsaa(==coRP2Jm5Qm)HdT;k8b z57Ry@jLIQ!x?3sIulk3ux@{y$HZ;=*yxY=MVD7Lb3a$$Is87!47#xFnrO=43zN7hc z=R9TlHS5-XL3cQo`coHKSYe_(QcW1Q9|w1tZGiEJxwJ!CU9z1e7Yh=k4qV zDk3Bsg;SbKVhwS|0Wx%(BOfP8-tGHu8iM8FsyCSJlPkNsUtCkK3?_^jx3m?o_9yo{ zLa~!JD-jd0BvfEerFEPSJDK2?fv3V2au9_dKN^Ywp3@vpc~D?C{`g)O0h{k%@9#bH zZyOze0m$wB-2iAr)ELJEYQaYraE&aVN|!{<#$(5GMqj|}3rDLiyqpAE%o^~C<&Cig zz3Dffoz>6qJfCCwEGe737dBSxH?-f|W{f^OK)FedX)o1$o22y`#ys|TW@dTDPJA<3 zBb--Qknq%ze4{0ya|rENguCZez*UkO83C3hQT!#tcLcPL7b+)DmmB6r!zoqRo1X=x zMrhBhh)HujXB@jfDHbU^fOlbVN0ylq@-qurBy#IhH1g`!1U;7e`KQXlCT3;{yk6x2 z&$RXQ?Na^e2gd&W3}_plt>PY`!hp{lhb-tX(#H*uIV+4UN%P(rP02%RC!uGK>{gH; zCnxdAI34E_e%$aetKx;7Ijq`ryTbaJb(3Yk1+3@lyY2W^DCUCEXvgeU5z~_mQ@0Q_ zixRJjD#+xf*^CS2JlM%EE zc&&|P#5`-BMhc+*?YvZV_ih`am`=d@#+)o#i{mOO)+sY442aT5J`$FE^79tQzHGHj zf73BbbZ451;5Wb8z?yf*ve5?dG9S@bV`>w*yk7D zi4{MR?5Atrnrvg4r8{XRHCdqfA4dWv22~Z(RTEPjNif6SRruSdv}CS z4_RSXx)O^TA=HlxV@(fh0TpZTb%=(kHjkv$rC~pr4kvovPOpWmvx3Yw;~fb0NkDO@ z%&(d8=du?YA`4pKUGQEj`SG=$+*beWc?^Bm1n<0QAy@$hDsaYo3Av}2sTBop&oCq3 zU!3Nj{t6<&Up)!uM+d>y2SCSW!1MV9cDpZ|rv!VGp@QKn`%#a{o9XkDG0>)aRfNxP zH3lPgJHF-J(le}?WkhuVB=X@4XN33<&jmT&bRK_KOccd;N>;;TIITe!rJ)*zB%+wAnbtnp&cs`HyzUQiWOS@eD-#r zx-HOfiHyOW7*B;6-^&T!Rb)}EYZZUGB`FPI>-SZ8YG)Xu&&au}si~;i4q;TFuep_T z%t3G=H!D3o0c;;-;naL~R#QLIP*cR>?QIVkQ zIW-mLIdh8i!UMDCguW?3)LQoN2FZX$jc?Y$(-%S^Y+=i&OQ9f}lJMhs@CX-7A> zS6+&5pTSG;XPlq4AC_o%eYR=?&-QBv`%-5ga_6O+O!Zb5e%dR$kdPEu3!=whg0uUK z+2=fxpmsy#(W;dR_8VGe`I8Zg-xb+>QZZyogZ;F zz(+NpSR4EO7yElx3jC>*-ofaAN5!okd3hd6byj0*s;C8z05$6Fj7Ix@^^jtH(P*CVzQa+{#DiWB5Hz zFQL814{}me>K#IcLP3hM6i0b7P`m$iSR&5ygihk=xdbVE znzL*P`vR1G{8!F?mEIVBkIyT49#Ew}b{r|VYgBJ~H}nYnw6lK>F#~KLKhctqJk{yo z4vHe@%gkm(n(zy|CZLgE&z;FmjjQxUC1VPE7uunVL}S!~ItywNMXIJv#aDwj^c&-@ zq;HD3()>FS$gPPx2Q-yLa_aON9!T>#VGVdS4%V>_YStfJo*j-oyUdk-FL6tg>gomF-8W_X55jDfPV&}HpTlWVf8EO@2}ekRaG zKc?nb6_i9Dw`%sQbP;C2bsSa!u`Egrr}nwEEx+ZJwwrtE(dEQGMvNi~ zhhlr6mKDs>NST4$2`^|U(tEN(Me}_v0lIkYCz&4WoZ%-NC3HPBas1L{(hhN7`TVaX z9H$aroAck39Uk$Yz1QLo+|fLr<*;|I;rQ6wr|v3q2YgeiX|ir9d<8!CgM#E_>8ov! z=#Vb!0|pzY=#ciotDaw{-f`IJ3?F>bvE|{hp@@?6GrMjPMg_Mis{DNvx7FUja2I7Im! zpcN1|j_^z1x-HyOc7^%Okcx-rjh2>bd{?w<{Q7Z+^n4d>mSM%h6NzM*^&-T{)K8_t>j7#ya!(ZtA86f>gu0by-&cHR0$?YOJNelvmnjspXyV z=eg&RV{B%n(`F2yNyWOd2zEMIS)69p_1U0uB*^G-3 zKtj&FbRngTAs_2*-nq>>_4@Rxe%jUkuRNbF{ZmaJ_>>Fe=C*(2<|>y{P<_^I6(Cq$ zDeJ{jFw*W7Qg<=#M4kf77=c&fyOx3^JC{vk3U;oSX2?Sn@Q|w z$uU7xUh1**iGU|4jr0{b^Ei40PK5`mAjHoL>|4wC4g++VcV7>IY~_$MyCvk-*C)G- z{p)tv=b@8vl$$XSTTc1Bon@?^bcH!u_t<{PFu!KXp6?_OT|njGSbO$qhkPFSiAK1e z=#u84u-FskW}>_*kV9f&E#~EoZ|(-vGiyoh*jqK4B5iPieX^#76`9(p*YJ$bThYvU zAMcqFWoMls$dqrZoFfzsHM|nQ;?z-yA&yPq7c&iutUCXi4t~O^d<-KvARLTw5{ZEp zz9|K6gtEzDm%GRoNHX;@${7D4W8BGg{N!*qr&3+NLlF+N-)ha2o-b?AF^Osp^i5-u zw@CDjok)J&D1H`3z!f1dYB_RSb!6kv!{I2YyVXe9Z44Lte?&~YOHe(&&S8@>U~Dqr z6IT6^5x6oj068_H=-p^OQ_I&OBnEb8Z!-v4nJ(zZ_QWh@S^Gx?#PY2_<@@xKHl}O; z3Cful9{T$2g{L~)_XQYzHo)TnHUa=Xf&WAFp-r=x}_p@}Wes3|@d zS@+aWx$pm8LEZxy!Y9G4YoJDF9o~w^v}s_cxt&e=1* z!gj4P9Gz6Q`RGQd*pm-aF7-V)8~8synYnFe)~nxBD*l=tCUb!(gS8u4pAD*5vr@lD zmvA9?uD9_-*8&sKZ#KD zpJ+JM^^#SAMLsO$%r$nU*>?>6wr-s(f==j+fTELcx}0t4VV04Rk)k<{j%msZ6B;j4 zXSJUTU=eRiQf9}2Fw;s%O1x!UsoNnFWJRCygc+6vT~wX>ytHOyBc6K6XTVZSXss`b zQ|tQm4fxrSZ|$gqBNGd!p74X-H*Y6J%syZ_^pc-<zES?xfr%jI;q#jskB?SV>c#n7!lEfl^$TNVp){v}3Z#L435k2%)@MUklJCJC*Qia7w|{1lU1>( z(f8EBH+$Fc-VM&zxAIL@(E}gzt>>0KWJTg82|(=ZOIi>!@EcvFr8p~6E+vY^JzdYz z8i<=tgyvQ<;z&jIJu97?vJDjRIbY`XXQf;!>%2CXOuNEvPImFZwqBb1IJcj$cFWn} z^!h&IHt&v_J&-4U!?=HkFg&FsC-}nP}`_s9a`z(Gd(1-gRiqw6}%iXR3ndSN1z6AcJu0QVRrUi(@@37l#6r4BEq*2wt_v!s4C59j{ajlQ zO)ag{{zn@YCKU=&J;Cu))=Upi)u?n-)t={(jVdI1l@#)5fkw!2t!5^^t(^&Bd})FJ z{$5yD+TZN$E8M-t$fMfTzx5}}YC5G@Hi16*k-k)nSspBI+>TgC?kXW#bbvS`+wJ6F zG(-qoY!nO-FON0(?8IHdfdJ7X^X)!n8wA2^9b$}48gHzf0=r-lu&@cXVW(|30w4RE z1Y3ptDHsI~pP+vUT(uT}Yd{Vd#7 zGM1P#?605=GPV4gtK8g?Rv3FUFV;fMh9`=(xX)a&-0r-U#eE5EvH}l><6(3tox_Q z>KAj>yN}5n%tkcFi9DHk3v!mWQf4nfN|*&56WWi0NWUS-pDE}HU2r29WYz#h&w|U? z!%FUz#sqgmNzQB~3E=Aw-TN1+rRjzH#)s8-H z&!NOvJwqz;g9vqcNl&hED!I}w6ujx<5!QqDX^%z@-8qG~4|Vcnl%ZQ7o6)H35hk)nia7>9a}B`(taOSrE(@)23cY(1 zMa&zjIfR5xs>VFtj0U|pQO4BEUguHGqR^;&?1b4dwvRWh%v;0^GD|N7ARq3Lu;`w0 zRB29>WC}$SyX&O;V}U30nTPO!wdIGIldZrJQ0<3R`MzAPv#9XYlo)sfwX(hfJB>=V zXC&Kr3Js;}sqk+*_@r#(T~?us^ZE~Bx81>uyF{7d^g8n~N`5DzY0kWi~seHE3nS zEs5)onpY>BF=)`^-6bUOiOaPArDI;zNw-KyR%&tZ7iqmd_b#IsODWuWEi0CrRxXA* z88ug~O-=Pr)(l={-WidaTwN@^;Wpzo3yu%cLQ!c(`wnTqGk7OnHYOQ@(b||dTP61%W zZH5vQe?#3Q#o6;1)meby3~c7Ek9wj#7AyI*;!(-|zc0nW+da3%>+6~B^OL_f*_nyF zVipU!6K7hwB*}ZGjf-Y%%hfnklQmxl^mh~1ZyH!-=opv(SlWPf=MA5oWYA}_UTvjPG8;c|7QOLu; zu7hCG2=eg=;QjqT%{}w^al94L+h*omj!99WiFN9|6@OZxCz|F+!CU=?pr*K=Pk^VlT$eh2r1p<~P*1w0`9?)MJzAgrZ&JY3cO* zE)z97cy=2`zMac2Eat`RGxd<6&W5Ra&=qWU*tU?!`rD$^Spxew{a&E%KAbZCBn?i4 zn_1kf@fz=WU-_8TARK+>e7P&daULR8U?v;2%;qnR1F3XgR3}DR+B4{Ywx);G8z+#L ztozGp7bJA%19aHlE!-d(KG*>&T$ZfIh_>Z1XIxWPhsuyD*^yicw;kgv+KeCddfe|p zy}Ath5bth3)|-_zhcCd6v+}abOF~Nw-Ea)IqH#lM|AcoqcfDA}TFQpAkT3xqS%edf zI`o`?Vae=hZyyAc@#%PJ`lIl4ubjxjIKVg6dbgSR*@rh$B+bNXV;R@iQbhIErRXlM zdDbm+V~c&6V+Pc0^>E({kISb6Dy^A*Xz$nWZa0XW3`Q#S=rfA_n;<-JuNzHll?p0Tn}g4~i{ zr_~LurnJ^9UHC|AbfVe>)Io=uv|1Ajqh`Rx@yXI7LoJ^=pr#ANS5d{;GLstd?ME}T zsSBR{74QkNz2)8XtbnEXio6+*=Y^bQa|YGs=s5B z)tFGm*SrAoe7W@x-TUVZNEnBd=G9ea=t66}rD*4A?wf`76Sk-hb`#Cw>*uQptG*@)LdI#p5OTmXMJ1!b}DAmyAR-Ay}LJs zH&%JiR@26Q@3Oyf1fxM{W&fw;W2aDS&)-hP5zrFEmfLm!v*GlaMNr@nj9)>DbAOvP zSmnO=Yb9X=6_q{x<^7|EkSY98pn4TscR}$4Y`A$|ezMCSTVKF9OIJ9o<{&A*pP6!e z1U97+qTIK7gt=HqvhX2DLO%Pag}_hg{<2@-XGu`+wSij-p}_1}$d~4Kef-lUtx@i? zDoRQrj#yOP%7I*Cj!lwNG4dkDZwM>f!w*12%^#D|0QPX=!GkU7o*AtA<(EW=zZEN6 z#|Y~sM>AMB9-4_+*ZAq@8g6fhlPf}Ly;@1GrazvI%s4llmKH>S!qfRxB&RGjxEZ0Kv z+|S%I*UVgV%}RifRm-HnXqCev`hsxjq{+Q?S6<+?;}-dc!dyJIhqQp0GcaX}1Mf5zSF*A*%^} z#g@<NbhrbQ^HsCZ?^B^-Sv9PMRTA}f@7-521?SlpKD_NC zF=PeGy0+!~R&ZW5sjVt|OSKZU1*We3(vgdBzc>TA{BhS4aEn(){aeS&YAWnCFJ1Mq*3oN5t2tr;u6?^?XV$#ewIO_LZ1LMC3h+w<*Abs=ZHSi$x#fOQm z;QB7HB|<28!|0;pCN|-}wEQsuu{kvPqLDe08!R%(lGRhXeLMF~%5p45wQ)M>2qegx<@=5!D@a>*ae$F*ar>)K_(N(%JO_uOO8*iX%Kc<7xUW&3Rn;?8;X zj}$*%(?2?g5xTcB3ledZ(cFb=>Sg z$spcprl){i3^#Olz-Yi=d8~Ow|Hf2d4BO1P4<=OdcO=d z4GB=YI`m|NZBA{LmLkN{^SwYUJ?#1ey(3#!O-;?ks4nqsz=u?xL)jgbwwtS1>ssi& z0i@Sv-AsVn0)61;AnRY&tnt$4)J%UtEL*P64|AyOo6ua9&!e&HFD?&e+KL84n489f zrVuOAWb8JFYEn30%ipl_CdAaX>U(2zwIjH+xm0o@*~dQk`W2hw`SfcExF>wupAiJd z(6LsaP9VnzqP@_{o?GQNgYs%PB77m>CD9aBi8QffF=hus1Xvw5a$Z+~g~#`iY-e4? z1&+V$@Fn6%Vne{9;>mH^O2&hlh=GpX^skx-*mhqg_UTs_K3$1OAmIg#v#*eJxC@Bd zw?OnZQoH9na#4;s5^x17(Uh>8m8Ee_tYIgj(m#JzR};Wnx_0RE<`PCE$3x02yJj3S zXH0I!=Y9I@?XhvpGPjQb4weg%(>`?h$t$5X^6w^sqgj678YC93{kLzIy7bY3XaB(z zA%fT>;(aw^`04$-h=LFAb$9@WFF7|ktaf+WFn)8Ht-p5v`eNcq!CcoTK4afrcTK;c z@M}9R6-^BYa3Uq^o`Af09#z|G9Isg;xO7x}e^+tJASe}D@%|N2Ou-@^KuDf6%8Ew^ zXyJxFtvpL92>=UW`B`5be&b<$lemc>j^rQ?{To>LI70f1Mip*C%rWZ0jgTW9mKNSj zKN0%8A`P`C2pEsc{dxTpR83RlQpo$z!P4`s_4)jfPme~;s$?QlV!%gdt#n(6I0+_7=-4IZo7ixZnZn>7M$9->ALW@4d3blGUhCe zpWbcm_>y{PFGDHJf!4q;iuhiVh&%`hNAdsqz5FKPIygRa`st-^g@IFCwXUBSA{GzU z86_~bt z`b)h7wt~6xs^I%RoPdfR&1t^NW!#b-SB$s>$o|z*g1gFvHJ9(wHE$NB9gr{(qz5ki zArXwDcgTN+h}M~0$qmLBKPAn!n%kB^w;vFsOh$!Ibr9p<%*yZk&2P9ev+)Mvl$kks=_$g4kNMhohu=Tw~Yz=lB%IpkXh~JtzZ;~}UP#8Bfus~BNL&P_K z94-RloZudC{5tPKWX&ew<`%492j%z}p`%&oVN>Wt2Z~7Gj<0R~e%!W``hSSa7k*~! zzN;XRsfN)&ORiUuBm>o>4BZ7nv00s>_l%Xe*{-!s@2c0IAB_9pgI)f|2Rq}k{yp`y zJj;;UDvOHXvtQ2TpC~&-UqhU#_#OQEt1$@ZQhZkF&>voC2uU0-I*n<{iQ-tBQvutT z$1>;p4DElUNPj(~|F=Gi7U;|h9pK*UyF?jEbS1?3` z{yzYVe|1#S25ne?!ib#(qH|{>qTQSQ8+6|$n8_t}3>2#ZQlX!lN7tVcVcFaf=;#lB z{_jWr1EyEx)h64*-uycJC^@Dbl{7n#O*rL4)Pdg95< zkxorvi)zmC8+}*^UNhWioo?T;*y}x_SguROpc*ltefxHMAbYy))|;+WD}4aKKQ5H# z3MHkk#2N>T1;~oE)DE6(T9;M|#*6c11f7pwW~We&k{Hi$uReaGFsH!I&3gE4v2cO0 zDh&GhEfF6I?A{vsUCAUo?SKDxEWJxiGHs@_2^Iem3GsXr~ zY*jLf71%}Qp7HFvh0#7G0QYXE+JENgc)jO<%1wub4yP+O@aN3i&CX7=puLsy*8*wU z_!;^0Y@TOnhkI=JT5L@fwl5DZ+xIePPYq_ge&?t&j8qc=bdvDTUP?qu-oHP@@)Pa- z<)X?RR4qmahz*AO`uSOZHW6&*PaySOj-`HcYG9f} z2;fl`|KK}v!Y4tJ)uXmwLN1Yo3l=&srccBvni^w8^a-*3SEGLDY6)yyD6eg1tBLoa zw}XrEz2cO%F)=%yMw4gn>zu9Gb<3tbt!nLoU`DwB0S=%Bo3co&SKKpfDv%uowuy8Bj3G(CK8WQ^7Dpiz{f4ZIn|MM?@394P+qxTy$``4y7I0aQ@alYmTwZ1z%;kto} z?qavfHtK)*y`LyDprBp6N&x^U14E)r1Xu1Pn zU3?Dhw!wif4>;{I;yL+hNPL-lX6{6Fj=!*YRu#5XSM`EyYz9uqRPXiZp+1p_UP|u4 ztQUqITOT6MP3;fG^@^7uqoW(QOV{c1dZP%UpU`U6TTu=z?Q`c<9SX~#SxynNj&c@mIWtyY#ZEEeI4Rc_W z{<%j(LAfW!WG&7B8Ua8n7~kVGr*I~+>;X$r$u4solmh(~?SxFQ?eU{@5)y3a& z(#1CTi{i+87js;71ccxT{EjaBDV8JF7i#?z;cSyh z*_xnb?-{#2jU$7%$DI}OT~k{&X=_{6;P#>cXG550M8{vFO-zpk1N!^`%>8rpRxhiC z;+K9VXB`Tlc->2>Sbhd%@6nElt-qZgNuv4tXdYajimJemjR`9Y_;|2aN^n#D)YN!j zdVOKS=tC0+8~b?BHsx8D&~5Ioq(9vK3w>V$v0$P3_9z{uFC{s`dD`d`oJf+DB-6g; zU}hbGcgGoyINfwaZa_xF4-J?6@6EJDRy6rQOvAdfEHWWlGcSYA+o5Ih7WvxK%Hat z;|I$h4(7!X-|Jv5bHAS}3bF^2=*xTB0y&J<`P!d-PsBMXY~2BHBEc^}PzfXXw@xH( zRzJi+KeGZkhCI^3huSJVq?LfjVyiWHN%1laUz3(HT9jj2m zke4QjxbA-DEZ-RR&VT1+0VeBjB)fiEQ^D_m1E_R(BN>y!7*&Mev)pD7_D??qkFL*>Pllsfmf0>( zoGnR%@-UDjn}6edbn*w3{haxsuF#u;pUexbg5{ zQus8QUth1+p`QaaQ5d0sbk!Rz9KmS>()MWwcq>7qzwHi;XPCDdOjO&Cz;0!>o7j%u z!Y18lQ0?JD(WxncekcR8iw?hj#-JZ#pNVGU?h_P7qmoQ&r}waasZUw;U@kH_DltWC zWD^z}&8yKdy`c*>8|s)Y%!=-wop~o&Aa2TMV^nWjsXcpfDv@6nvpJS)nS4g&1_Y)> zOD>6j7LF1;zH!z#x%HPU)L)?eDevD|FX#$;s)3Nz7;GBIS9INk^WIei+l5L6=bxb; zl+IM?%3rc7b+^f8wTGjY@Ggf&b@PZPKYsTS^Usq{`LGexn{a5{NHMz~g>>dC$9)NL z-;X#qY6VrUrn4L?m`S`{Q{|rLB(i|oLXJhbBirvLVXfX5o|v)NWuw6KU9oC2HVZBk zU@q0kIJ5jY|9m2>+74R0R1%`Zfr)$CpjMl$QK2a=5eE=ADo6_ zZ+cR(wLa-IO|;9!RlEc5a9SN>+bG9=VDhI%3I;Ci!t*if@$S<-@MUZv=o$}=JGXt& zVCir&+Y9X4EB4T=cpQo2{0O{?U*(1t!VphcUG6WOqcsA~{JH8Y?nGAx;VffM8^-dQ z{3S+`tS~(a3FP2$j@cxwo9GsfAvG5m-%nYqzZ^Xn8TbvC#T-l}&7026CRy!j(syRd z9ZgxGuT+?5k4o%7asHZcl~=s(pNLh9Q`MG*eQXlSW(`}SytNf98)t6G0TQo26Cp6k zto4D`WyIQo2O%n2BjN@XeTq&aEsfGt9ML)&B5h`_mP23?p7a-wjGSl=lIcKY&<5(v zz&D4F$FD_kOy&)YK6eT4QjoUGE_Kgdph7W9G|irnP`lt<9?dmzt?!!SZZK{GxYPr`@Mqd^XUs)=xR8YCfV8^?oSE#y{VIGvwt0&Ao1q?eq z9Oc0Q(p9AuZ)f|Tm7l+4A-+Ptp}Z`ea?!y&+TB!M(M{0EZMVMm!|PmUsW@B?#S6NM z-?{24u(}&KKfGxII<1A|(m&tb*VAAr?LOw%$=a1QTtn*dOZdng2itk_YY3`GN2tI3 z(gKh`CL=#!xAWNKC$qK5;URNZxHzfDfnyl_z`1!1TtKN28@K!#(PVB@Q*R7Di z`V4DLA83fs*?Zk*y})8*9DF&UTjeoX+FXAN$Vd$;6$kr*$31>HjQN2H2Ae@3kl=_O*c7!wO`!VK!`WB9be4K+;QR!*2iCy}1hP?PhatLu4*&>>W7eMNksr75;q_ zS>rb)A)u0na+NfDcpdub%MInZd9Uwd@MT<%Rs(8A@c%zPiSouQH4qy=h}qnS>3E_Je(N(Md9c zz_!6@#v_AY3F7QZW`aLd9M6Yr0GT9Lc%9V2OvYbJTo!J>@yS36v2yFrT>01)fd0K0 zez{|oLjWW-oa7!K#P!m5W1c0-k%RqDOXAMHVKL$Pdao%!$~$Z02X2}zvH@2)UQ4Vcb@(Eel2mpGjVgo;d= z<%W2FCZf-XL%PBoRzE6b;b)H~-N1`e^tCpw=+K=jPrs$4iZ( zt<-9rC6O+tIgPF&l&#Ss6FH;lOIq1F_5=KOVhd3shU=|wv{SsYIBwaiMl5Z(hIg?a zPxuqz&yXZb%dF#|w5Cm$CWqc+4u38o474dMw-)nGRB??5HNWu5_e)t>?Lp+tH6{YBM<%O7*x|>eAn5n z#`?genmH-XM0JpU`!W!#uZ=YxOh5Y?y1;_KT>aqKn!ZZ#ij<^;n)`dy7&>_naFyy> zv*a))zK|IB`md#}A2iLItD;s(rngg*5Fk$eYHatS-?efAr8 zqSpq+C)@?YXf*-ZZ0T$ecVb$`1GlMlE{;{2yfP1gRlLnyoz=tHA_EOO^xHLFkBcZ) zm@=1{4(str*XBC3FgJvv(?+)HZ8;_|=0Mc|53W<o-UUHo&hS4+Z*5GAq`hb3Y8<{{|hvHp(Whv`6hJKK= z_v2o>!_4ajpId>5(|l@C46I8aNfuj>a@qD97|p#|?`DjOSGcB-QhknU+28=#jzT#bps!&_^3iN{!#-+^o2g4R75EK zXcua0^vL08`9=eU*Y+sq<+VWs_|3+C5`S+_ET`bViKajud*s-kISp~p z5{S<`VSXH;ydr=eI%Z?K5WYwd`>r~$#AV-^{7`Urd}!KIs!|j&lI;Gy5#F-EU7F~A z7nY@l>r~9WkT{l`x?-&OV(V+C@XDc{hH}i1&r7zX5o!aN!+^W`JYv&NyugRv%UpNtCK^cMyq&>-blKHgU&35jn@s2#Ag>U_GDq1k`Q3UOSHSt?;##K$Lj zS#ULzrkAXo=>+>^-bC)n30;{jEyRSjgxjNDzv;@uJ=MV2kd=8|fKw>pcaP6jU73^? zQN?h;AeZ(m_+2h&@%-=emVa%5{*p}|`g7ILBGB@Pb-me8Ri-XFf6c;S_TlWeha|}= z^fx9RIDcq*%4b++*n`H{s@vZgeD5l5eq>-mu^S~m&mo%^spey3DgC4+N>o%ZjXjX9 zMbI4Dho#?0z)zDqN)g@e;+VwG>j>HBlc}!Tc^B!?K$b6a?=XBQptiZNwRE~5)iO>8 zqEgn17CEz>a?*BZOdUu!BoQ-Yith6*MLlQN*$?;f+ZZX#zn}1`9i5_-5G&NZLF%?L zOgpgf-Nou9v2|%?sqq^q23R0}Pd@&?*^VhF(@uMRIVLD8IS1*7vFO*wha?L4#7C$Z z+PK^+R5yA)JaqFo$2jVM|0;+|hSU36uUun%BC1x#)QU1(O$F&i8mg6+)&_Is285!? zksY)G96eo2F%GRhu!qTNLmovv1)5+zc22>jTbS21H-8?nGaG0*_GLS5zKi*E_h_ik zP`RW77~Ff!!prT+_6Ndt9E?~sE(FqjSFTXEo{Nyxbtu~=GwG8z5XpO30%NbHvZXAc zQCcN7S}-ZxzpKE+U;Izne7_|9H+~r1bqkWi0L{&}kx^we#mN>%F$V`{3QZab=R9!{ z6|J_1`e-*J_&MI95LlP-R%lK*XBFbvF2Osu|^m^nua!a2(Z%Krnv@YmCvD~gr&y$y1 zQ~Vm`?FaHcHB287ie{|_vqA|ZWDWj#me-|-eY3{0 z2I(LFaEjLV{tZY>{k__I*6y{ZJyb;n6Uv?Ov5|^0D81M6%D0;GmpdlRNMqE*R^4sC z_T=ohwLjiqynQ-`R~qSZkyGdGlad`2{vlSXI(+_7w8LoVlmHG?n*q~@)WjYLq4+xN zr!Tz}^l4ZqhHaQZZ(FjCNoCu0U++i_xyx` zY)fs}gl>WHb&b&z&`7)&#*6!+y$HDkBJ-F5=!>+RjsKVj%xwZ)emk8=X)#Ee`CacY z{y%Cj|JClm|Ju;+9&rw8oMNv7jEL^W@zUQE;C&x*{O_*S|HqHFzRR9y(K=r@5i^)% zs@)QM<${0Z*T^LnA`44OihhHrulRRZW{l+TlLf#Q{ija&ul08CAwP`t=hh80@)@xN zJsX`Ka{UCwvz?brbzz_Le`h%2xBru#^LGvK=A>#w=4pSR%qAlFKNz*bbJuTx4~!``bqXP3q7ye_xzFU;zHvP5UL?@A_d`SxuHd`Qss`z%#-E@axiJGX%pRB=TVsoE7R=N9torq39|3UDWJXl+$wt8!bzV9z( zpI>h1(fR+vgxWV!02d;m!;knZ;Z7j2rWHE@@jvi&b)6FoByZS=Z#UW@uJwRj@OQ8G z+JDr`|7l1o)sUy?n#-#P|B;0exH1itb@~?%DwB`iiIe$WAtzqPjKT5ZzqKXe&r$t1 z=H#^?o(0x57Z~wQFN|ljM#pw#+}^(V^MP}~5evl#jn#u@GZ+qufA#X1yeaX7CvoC# z#8l|-o|h%{pC8!U{^#t|0k!o{2FfpQfb&FyKG#pV4q;|O%uD{JExg#We<~UAg|TRX zpatQAGzA^>#25qx&YpfzL!?i1)Q)Jy4_&J67_i(#rBWc(VZPOUm zN;&M&+?zi*zS}@K9usG}s?hXWdKzAqr&o0QcJ!5t<1I1Gua8TC9->7IxaE0~lr;_q z4&|dUtpZEt0VOXd;H%ZY2fCjh&X<8Fl~6QtFrxTRQX&85j(r~k2`zygz^KOiUv?Z6 zJ+CrY8uyuGEr|ba^l;_IGcW!SI+5^#KI3sWLT6g@@(MqN<#;Z(coz{<3=|P_jB=j# zAa!-8dvbVr6U1V=GE}ytbz#rJcwL!;U)%Psihea)CvHTT?RRdxXrgEogLn z>}8Y=!f-qRS$Qk!GQ{;+%KEyCzRfG{(T^KI=Tthg0pVd4-IlPH;jRYs6?F_sA2Mle z#3eggZSOEQg3){nr}(pHwiWzLkGtn%8?5O?a@3wqRP_BHpI zg{iL;d3n0S*3)&mMpo z&6wA!J!}DHEH66|)!VAwGCz}S03|MEXJ_Z8jjpA@_2o_TliO{$_fDu2wAtr|c|?^bLjAVa#@3WmKA>tE24FK8J9oWdLg~v90RhT!+=Xu^ zM#L9QwnoG1HLD&A#IIoQAY})rsnc;a=2`CakiJ~bqsPZK%Gvj*dOnEsxkQjXr(BPh zQ|dkTdAjj#uM{tGf;c`F0bikQJ~&SwjDUFLnQgb@3Mqcvm%^ow9;dz)KAmv}g|Az> z_xIQM$#ts#iEsZ;(e}k!>IzXpW6tgILUb-5>MSzM)iGVo*Q$)2nFI1XH@9)UgLWhQ zwS>!VIA|?4>u@`-fpC;2yGj#bk8Es1mL@(UCDoO_rf3jWVH5M3G+E5CpUZ7ny5|Jt zOcYiGlLIXs!yrsW9R|ikGAE?g#vVZfAksJ!Tfe*$5`(g&^EES6xQR1frv}c z0QunDBpCBC@>){peWyp6_e*tbMFt07xSL~AoeRTMEKl^fjm79@z3^CfGFKv{%$$K< z5mI8&k#!KnPw5>!M5K&1!PzW+Oi&fn*%Xi7HyA@ig7d38+CtsVVR;5JU z)^zgp*bDD^fPRbRYwX7QJ7vwChq$Y5ij;X2Bstl`S+%eBO2q4ML{Sew-BaV0K<b%QWpp{|cg9wF?mvsmFUC_>ip)8kmoju;<=Hllc{flsR=Uo` z632Rgm^>RfIk^Tq#=6OkpeEZIzQZJn_%@A{X!SdJf_ziTv1+TdYI}fwV$;~qLF`E9 zdNX)5j%q^YofN@#sk?J8q~|jtz=VzRj-bvS(5GJhu5xx9h~VUJIaY5ajJ11SK9x@@ zXnWuI>aC1AYTZP$MWCgQ%D+CnY&DE?RM{se)qp?H$#sJFcu!7L@mOK4W#^k(i><&I%}f^?4Ko~{ zu50uqh2uMhpGx@CE}HH*RR8kaiwQt^uGkgkfX($Mj+dH1ho7RG8$+Af<6PgNs&$FAA?#ov(ed|AycI+0w*-Z|!( zY7f|keruA&ZmH)6lR0Eoyx6CXl&IGd&26YN-J8#g$4ctj_GD*fZSjgZ4DrBe5whI6 z`J!$l{8nD58~&&km8?+4bl}Z>FrCRQ_UNSYWy~^lvHhSc=e_Lk9zmInge2S-4^rtq z)117Tl$GR%qEe*}!+b~2P`v7^S?%iW$~HTK_*!XD=kP{mAB2r76h071HXdi^9>q1O=`oZL94{&}?$=i|Kac&<>l0k=cVYpH)mPel}C~nujss{GOoxA15E7XzJLvD&xYvrk~qNbIu)mwbnMlkq}Zyjgk6LMWfLw3uYav6m3u+=6+ zznIY@UA>3iv8qg75=c*J#w~TL)iyE1`9uRqDQB4%>6LW=D8!-nK0anP9AA80r&(f4 zJzATAO$W288IQi*B*ez@*X}Cn6zX%ctHg#giMd5-Bm0`bzgf-F?I)&R*w|g|4pYtu zq#nz+Sm?Y$LcFKgzp)JgynU?BxVU$13qPV|OsPYSqWVT+6+2qQ(?V3OztP=*7-= zs7VVLC5YP33QhDzbcQcvA-peQq$C@v+Vb}tA`ExjQ`+Y$2o2z7K=J4(Fo0;5GJt9f z5q$K)o)EhAE)fe7@uEQAe_qOjk1YW_b#R8baXM0qxzk zT*!eU*wU!t&a<{TpyBYi{utZIrHTLm=UDqpMwhgZ<-=LW<7JmS)PZ{JR9k%r5Koet z!a8*!H|9+GI-|6|0*IRu4xQ=kG@jy`!=3vkr7`C!>(SHQD@L+9*?E_ga?S>`^+na08Qhp*5Fn@Xg@eMX=6HhON*7nZromBRCO05K-n zW1)JXP3&lPmiznAy;k~0yXD=m9VZ7g|-uR4zSCt zfsUbe&wTr^<2;^vR~z%_`)BgP8Llv~kgLu&Z$9nY;-~wzIJE#D!@8LTQLs6VNG{voK|UKtC##;a@a*r17rGB*n2j+m|`Tbn`H&3~z0ER)T% z!{Grj#<1I}@0kG7{fLyIM;~8d;M=$i<2F25 zrq_y90oLg4m(WEA@0x!BJTIY2s;uTvKO6MU8z5~S&!%2rEyJmG%^QBYDP0A1;rcivDwqySF#&CaL!TUG}Df>lv}yAj#M=pH|+Xs zVZ$?(Cak(fPVwLj-j+L$A79f6Rv#x;fLg)s0;WX`YC{H@=O!7fjHge50_W*d=^G=S z*00^B7>VE9To8GE{PtCs_EAZ((+dgxn_DhRSi(^mU_y zD8Uq=FU8lh|3r?#+TJ;xzqpvfjy)DIVP#ULR%=Fe6#N4*_JuyUNiJFWbshw1<~+pJB3^u&)%BK z-1-8cg^j)V7%gmbM&u5%?_p}EIZu2nQn&C$0&Mo^Rl^MDqk(5RN`vty1y3*t2UAg)#j7+@<>#3BNffv9 zCp9MvRUkhtKP2p$YUSz0ZY=EWz*XF1Wj}nV-!+=P^SH_Y8z>m${*;7#v*U`dukQnd zeTerm=1DAX0$tz7jWD=sk_1c7TBHR^u6J=tGz+w85ZqD99%XvrJD3Yzn->i>r;OU>eRzqE94)A5D`C# zJxo)JvPU}@31lLsRbst9+2zCoFSbcoF6UuCZ{5q1ZEXOTw*p91Q_yacA ztIOo92tm*01riASM%gSOj6yvVdzd2V1m77rO^eN4I;ol+ko#JG2udH)_luZUOv93imtHVQdEK&X(Gl8S^K431 z+P|jCYY2-TMawXJ`b5~KOtc)TsUGCC3cgU>9w}RkDSxChEsn}mFSbw~FCu(e(DJEu zp6V!wIah#vkIsQ+mENm0;T5sanu$+q3}L+NLq+p0uv6%pZo$3K?X@{Gk$^;}S3lm1b&&N@K8dInU7&7c`Qz|Kw(u(sF1O>aUak+Er~VXwjO_RJ z3QYOMBuVt+*@p;^U}Wi;Sa)e^s3I?ix+QJwpQSp zeu^~TI#Qkty*77~Se-F@=~Y)@8O?=N65WJ#I(=TeK{ehdIG(HADJXpSZKRjI>U^qP z9NF3_^n(i5;ma3=3xox0r$>h-?qHJabugrPjqlGSuZ;6wuG8O6k%3joU@bkd(`&+3 zvuw0tb`5OX--C=A80uN7B`6N58P4 zNyffMB`lcwI;yms62ci*uB$K9_>Jqu3$j}`ef2@d7SmD$O?m-~dhjth2zB!4BF>ZG zw&}LLF;^SCoOVtz(_~NXD5oY{RtfU_phaIv|0DH)7LszQzQUa-w;A zW0GaLvQW_DkxUqLiC}I0Y;s}$l^-JqS=s2yyA^ilHOz9tXRbt4%C#`)tbBI3IIx#X-nXS-4m3Ip_#Ru{Z= zzeuEIrLe~dy>-5<0|kDN-3Q94606pBaaqr@iZbMDE}N;z3k7vu%i)BKbF9zV&GW0j zl-(4TcAdR8H@r`=x7saJ;XIyj_sed1_Z0{l^KjevCEB(S;T;=7gGQJM;q(Ji%wY3L zIXZ8$48(vjon~$9tXPZXLD}hSX;iq_*q7SUBpVM0Vf?ctK1Ej>zI-(=MaFMZ74AGc zC+TKUz5n6HK<$3&P+a^%yJ1<*ZEQtDihg#XZdqnq-a~%wg3}jzjs;D&c}{m>FD0%| zclv|6!$Xsi@uD-a;^F;|6u$#ss=2*tlh3B${Tc|hXf8!kTlR@WbQt$Ic=UV+`Njfr1#_LXd<<;&pN{jjWs%6XP z+-6W+mr=h+W>l4pe_ltmx11(-`F6IbEHgT`57*u1)G{VORR_dhuawvfDuj9(xb))o zM;ocic0QgdDRPkBZrOO2*48&)Rr{D1T%YP;)F9$}!%J4Rk6n+q0!1=*+ZR?sX5uJb zGLZ{X?=R+Fl<*9ltz2Z5=92K7?q zhf;R-_YZ%#@axB~Td<$%l!wst-;U1ZK|K}JLi^Bh>+o<_@%*tqD{_53LpkcUMS}4I z)(ZwNLoc1RuXtyZ*Q-v(LsrZ-S@0xl!ijMtasgD6rW%K3JC~{L(!Vs~$Jz>ff~@+F z)mEHqz9d8u8hc)vY00+yn`g zgpstEZNiy`Ica9D1|!o7FMr!U8=gvI=|0^;<19K6UfO(VUWt=*Qcrh}lqQzpmp8xq zGKbt5nRnIM6q$WxwyHrHDR%L)qT5Y zptPkS&z3r^VUBUed_FjF6xV0|0=+PkTo3d8zrA(&YlnGgE4heaB{J6i?mvf>>CpQO ze})yoGcT;)tnib;GgOm3pC8Mp=4o8;15WAD<-u>Pjok&to))@5`>8prT%qHhK!H5I zV0gQ0#GA}wb0UO$xaE3&FP~OH!o~RcEvKO9e5~H%AaqiHqL3GqR{X(LYX0?Pi4u)~ zvdJpjH4i!=*GMOs>W`ls`64cEXCXFP9nhuin6z>R8LwwRVbbi_=!8>ER(pb|;AeFa zCYnzDI0bxL%FS}<_`TNW*)=|UnN8udN<6BA+yG~#fArf{-`!jq`+?NlR6Jj+?&uj< zy2rEv%sv(Hn$>2o)YuijNXF2WsVo=ZBhAwoZ#<*Z9<1j!vcE0EX!%@>#--zt?pMl? z$-D*3h^GmzYuIxm=V_BmTYdK%=myhXFy2r$3%Oo{DANEt>nvYYCiNt2d|rDrVa2CV zvlH78%)~Xws-%`$Vkg$Jo=aU5eNj%Lzd>d5%rXAXc(?q|R?z&g9Hf@O^cSN_I-ygR~lf1WA)}6|%cx_1R~NiDXqtFO*BNCsYYB2=&T;{(SzVW)E_y9N=lLhca)exrEZj%R7S3;vejzs(m)2UE z8+6OojFE88d*EN(^Dc+I`|au3)Frb|&!VrI9Tu;+|LsA705s*aex(jnF6cb(E9IrLpW_4yOn^z*-^!bJ z?&_QEDiNE*!PQ;o28tpc)Q8^9XuTKHPcW{=tin2|BoDnnn?W}Dbe~M_^4W|U{TYLQ z_l-uvA1Vz88BBhxDnD(Z_Os)oH;~3 z%JUQ{UEo;15Co!DRF~naX1g+~h#JdGJj|Ku z%6RQMSJ?#?YmW0x59RP;3w2dWu`1x=&);gB|D3%Cie4{YtxW86<@HmH3CB+9=ZeLN zIgY%*mpZ>VdMBl9rV(+1HhzHuGT5u4Cy!5qGi1f)^DzIh=x{G=pp3#j*3z4QV*O=Q zR*qenjAH2m1B*QgA|{&GP5Yi5$K#EZM1R9T>OLbFJ-BtHF)0 zPbaT=TkQx_Z1E-7%lNmND`7(1A6U!dTAUoW&YWprh!sUUT?BD*W3@ZYjY`~&>L*L4-FmXZ{0V;x?93wB+(^z%8!LT9%2dV1+_L zX+@p|p5wH5ChyW2FJ_Rh=*^n8uc0o;gc~nA?UnXFRC~$OcsHRWLSQ1eI_cxq7ck=Cbe{!O z8dMuznO-v&hJ;QOTXu8ZPLYZ~{U!(AGgLhBc^s3um#4$(|#njgdz7DVb4$n5@u1CKVrD_ z?@$$mdjLF9+^*@}aAFF34w=Aigf`m6>YA#*k!PkepDuU4%rf&ZLDc5Dy#X1d&h_)> zYd6&8;A(de5-H1pd7TrVF!%Ql$xWp+<)(o858Z1^9oK`v(6%$#Q6$N@z4msf>nEB> zDC5{q8BC2hY5e|?MrgbOM0!O8!p1D*k=;%wRpwJSp;*f&f$A8^b2k)kpsmoCDAhHo z^|?zgwym{3i|ER_$*{4!Ecj_?+~UqDN*{t=jnDclt#=fEp~V98TjS@Conz&{lVpYu>x?qD1Y2+l4-|Uxx7u+5fv&Ic0Y2%F>tq z=l==zi;#yHZyHrRX0k*lHFNgj!gg=u54yw(0P;s>CoBBw_hD*A?Fl_k{m77d2I1)7 zX3fPwOA1DL@%fZ%HWEd7&FpF^M#2wo!MU5Y+bzh8bP>KT<1Hfn*aCYBIYn)O`4?yp z9Y~69vAo7~e;S4NrrFJs6CsRZ@j3;?Xqt9E=?b^Z1d-eePPwObNNpJ*utv1>_8QvcdAp?yT?lx{|*93bK}$JYQ`r zcf%^-nI_d_EuukNqm7hhr?m=ADpft$-hVkRT{tgJu}(BPFHnKYc zA&*r7@ddHLlkvbTX# z!1vj8L=(1WiW#grE1zxloAI5!teR%os@v*7=ogeack{jb-Mu;<+qSCGX(dJ<5-CoJ z6+Zo2I-14hI-}FJJ@3m+LuN{tkQ+)RqVo7`*G8x6e>bwf7~ekaAp)73u0og-cEX7| ztuGg+fsmo2qv3(b(2u?DPr_dbqDir5`V~5~86)qE zXTKA3RM&KHNHu{?K)b+bWOCw8>BwCJ+8w;_M0=J0?3Ms}W|5z%61&zM`uEn8f3JgeAFMDrU zGHtm<)dj$_p5#lO@U8Z#Hs~pjVuuzav6yVq=G@B&m545On$;`Hu`c=)&1X;7zc8zh z&qFgx!m@BavRvlSiQPM3HN@)F7^?neab(jLlLLjGO3{P*$RddRWr&ZAwwbSJVN+ES zkBz3pcB9kw7Jg5$)Xa0Ce@bYg>yv`Ym`lyN4sW7HZ(jTqwpI55L15s0Gp@-R9UrYv zqN@<+CXR$=+#cuDEnqmexk=6_>GRZUFP1)12t%Gf*#?EQn_LIc-C1iSO@}u@Q^!d) z7PB$8!pU)D+&c(H z#-P>=B@vPbg0EWwsAMm^7Piss3`s1r-k7hBWXHkTLucLyVQM+QU$ZOU)^A-A_Y{Lq ze%)9+m-i$op#uTY&GV9Nd|T% zX(P6u;9Fk&@M#5?Udwi>IfLfB^!cF^=yxr&y5*~9`d@VW-0{nyxOMUH$l` zOK&W6z{0HCErMP0?Qtom@8?8TY_h15dvRl}+{NisZzGc2BP-tsH3Ig|vx>Dz$gPke zeBQOzf_!-hWM!>L`K*$L@9x$rU)&s=;I66=7!&1`6!REV)pVdPo&B~Sntk<2VQhKJ zxI@pW=*`(H6To8k-I3~Ab~T!pcudKQc7~1JEsAMAJ!TNh+!L@%x;K58L|vYo8aivZ zwsHuwuY9ZC!|Y;0y)|D!%G!qy*KG%?@-N=|Ka{-(G@aY_KAavRK?tI^h?0#U5nXg4 zh-lHft&84UL~qe;Y@)XyY`rE#XX`{4y^G%YuYFGB-0$9de&>E;oN>k;+eyyyzH`kr z=QE$@St~19?13#aO_-SpoJF}z%3ZOXO8FK8MqE=BxXqzytHb;yL_g{zE!lO{@0H#{ z16j~_(3e*;mHq(xw$U_cN>pxSC23K{eLLH&6)mErC-^ zwC6T%H$U(?-10+`z4uT51+V@Ivi{e9e<8d~Lsmtc+B|AvowPi35exqxbaqM|hr@Ng zy5*Z=JjW<)jA2iT?6N#Py>lsqaza%2E_~&gsWbtTTIBKMXLG`IiQO>Op_F!eC&f4R zBF*;nT=6t-Y(2p-mXY4B$aPObdGX}oq!O!1G4)sG2PtexF^$b&+~T*L&stQRbNW_s z_BLT}`)b!%GgZN43gXm|`nUBE3+>3hpuf3;0ZG z&pXz%^j??U!oFFcS(ni$sfZ0NWtS}FsdKA|V{I?t6yhvscz4_)JEK2nvR!GWa{Q_> zKtEfsG0v*Rg@0T-rQmS75yR2lp&+1;s0;pxw#2OOUF`u?%rdJnv@7`T10wCaa7VcF z{(4_R1%}bM)5>Zm1h&68p{$4AB?ZO-5t+rbT-%qn$@wiA%tlKko3aUzR9#(@Sv|P$ z03=BlA#wq^onCW{nYAR^$?qQj#9S+iUj0sG`-{ev-Gquj`4zf?3on4#66>T?lfIdr zJswZuXXYPbVq!X?I|w6$wtVeVn^&51@`jKj^DKPq1bQ~2qR?$NPu+j6_0gVPk>Ihj zyYlZSE!6(1?(Y<|5pYQosZ=oj*%_dta=vfuLg-Qu;}`)t@RQ?*7{Sjh#_okYZ_%;V82|iNHk6H`BYwPtm&!pMj$&HWje0=ICINnCR`h1$!Plg=&QUP$G}=>zHl%OYI{kYTMhLR8-LXd1xoxM9VyeNj`>haII(~n}e4|jSG#w zJIx8d!$EgCQ29+cf9-l6w8%mS6cL!$o$)LR<{>|-Z*0(NR67WU%Sij<(i9O8&DXw& zfcjzG`V_gs(auCZkE?yz_u3nq`F%4(J*dXL6ogRm4_1#L?lE5Zt6T-27h5Pc~ z_n&{_P7Yn@sD?p*8T=v=Z{~O9KP}6Z5Xm*q6Qv*!Rh}#tvF&Luaw%oIii)%XiA$DZ zElm9rY}u_{RVKG2cge;;JW70WS&&-&Cd7IU54Q=PSoJ$>VSQ(^u$<%@&ytwCRX%=x z*%OuN6~w~X_H!+V(E@Dyp&_!(d&VWDHHBH-H+wi4#`M*wFOaUkdk4?oQ+x?8%uN9& z^&=zLkbGf|%z*Jr(s&a;Wd1r@lfXqvpSbSX2SxS`m^@oeWKbXJkK|RwuhI?S3hNoZ z3u_r5#}i&?u@(EQitS*3ZGyJP5ln@Otp-$_eoo!xb>4d^_%e)&Gfy4yUZ=@#bN%D= z%Nv)~fo%4BdwaWt)}X#YO(zBim!dAx*fnTW&4JfGpvrRn6W*{61(#SUx6kQ6hLEtz z{{+`gso&KaK5Xh7-I?zsc5M4~&Imy`xAg!pyqxd7;Yh*d%d81XoV&0u#}By5Ovn90 z$aoE=LWrL+F{(ZGVl@!A*h$_rM~?*st)F@;vRX*pN^!byS#IXf#rJbXQ#Ti^H*soE zZiPGt*S_pya{q>%{%-c}r}w|Ym6v)rStp*3@(S6M>c1ny_G7q~$nP$2dh>R2nZ#afQA!Z0D2_tReeYufcYq5k`aDa!xpnNzUhcr20-Zp5u6YJxr6sg$$5o8cwtKXp6u72K%=}YmV&^ERd$$930VW3kF9BeU4Q1j^{9g9;%dML;u&U* z+KexmT}!P9f}Q07VkgF0iTC`no>%^Yswzt^1miRj>G z%Hbdk@0$(KI_V{4nKn6O@*ubMxD9L58LJ@Hj8c-OuT(n=zp2dUQbKKo3E(x=d>TP$y6e{RJGLZ^CrYOljpwwxd!4G&v#saV4liVqY95z{@1=g*(SFC# zWGPOhjRWTl+=d^A&Vj-ySc<$URH!Fa45Rh%F2-ML`S<9*=zU~;E@3qO2{>QV*XZ-} zUR=G!@>kwWxdaew*{O#!IKP62A`+aF2HM}LxNqu1F+nLlIZV!%PNI~8NN@NM@A$Iu zd+shD;r*SI`6buAk@cqG_~ZOz0DuXFCZ15gUs%}crw@M^ISd!741E=dbb7cTj6NeP zNXeP{kAqC`A8YH0}(dgrZw_g6ixRI>QNwPkVj8mrImQhvf21pMT&Y7oXRx{&89 z+*c`xd(r7XSJliFEnNf$@Y!xxC_f1*#R=;yiOXfrYa%7hb+a~0gp!5&HHUhyp8VXD zt0?K#v%`#9>@Z62(yXMGxz)-^#fSeL_phDx-rq&a+UxL5IYArT^n7s9nIlC{lQMPaX#(0N|a zWHZbu*@PNv*p!vwF!UTAt@+mHyNPa2H)W*xPgl9JUi-&m#^bp>>Iq6y{3=xB!3-E( z?e_0!qA($Na;Y78P2Ru$s^@;_9Jug2y?KhmVe=$xc!l^T2OW|_@pOzbAUr}LZ@<;! zBY~^h#?a{cx5dr7*{Ayrv^%rI))JAAnbKdtTGsNs-`KcqZaq~mH*K9y;1baReOTNm ztq{dOJ;h6C!unTyf>y7+`^DxO0HxBGM(m3}_dJpkoF#2z#%grGzIq?~+`$i7jZQ;Z zl{4U@kdO-S(xG|Iqc_49b#A9V=3|Z&WiU>mTLE}|HJ^}_`0x#02f?xpUi{$%U4K&~ zIu#eeD%M4{c2PI{4yxZDsBT{t616L${#Cq#i9EMwAm#7Eh)(EIigjegK>4eSbAiS4 za{$ss5I;>B+WmR^-(6GTpma zw1|iPLLK=HV?pcBiyc&j-0lb8s91$l5DBRjWd=sWE~XpPWB!HDn5lMZ@Xj>{OXBza z5r)w72T`i3w+a_swodL;yglk~9pk$E?so|OA0HssZW9ipWB<>=`PB1xpWLv>xC^h(E~yRUmH!1jCL-1hV@!XL;N8&bA*NOM$9$X$VO^E>Paw=}N%BZCBPAsJQ> z*l>j;wFib{i`G^hENUIoMTg&U^iP+igLZ@btgrQOPLE-%Ui;^l{t4|qYmq|1uIFE= zD9_PpYw_%~ZfQPGwJDrM_uI!$f<(`M_g8)nsQGNQ#U%4A80rWvkFPjOe4r5ccGZW} z+y6`DZoJXA7E7z(Uo5NoVzP%`&)=YfYqNZe2hD|t3ML~ZubV0hy@+^-zEZR?aB>ri z{B@Vc)uqKAUB#h+bZhP$Kzvdl$DDA#;(mVmm=1vu;o95UDhh|eJfg-vJ%E;`I=|Jnu%7Rntvp`;OD6M`O!tn`um>nahe{K?zXn_wF(priaN-k zHVy2hmNWQ7{BGfe;wi%ZhNnkSO)NyetBbq7&xLW|1Q0brHUnhT+rdv7FPC~V{5d{; z#tp@GI9$e4DCl`^@V+yGmY_3^r6>L4d>F3oUF^5dr=}Yv<;&dUbZr(toXJMaevO8QcT#6gX}=V0dx?2sdK);W zu*rp;O4yXz+&{nbiWN*Uw#(;Jni$2Rm7D<^u58?&rOO`;wBFS9>R-tIkInS!M_(|( zo-bv~s6>s?$dp)ed=@{6JJkY)rensNyT8JxJ`mJ;KdAz3N$-9y=JfEnP0Hw-DNQJO zSbNqcOLrtdF^2wg5{uhzSpZN`UK;hLQB8Uyc%r4;mWmXfSoYdD-f53v`uGyY>T|s7 zaCD7Xvo=j|GAgXSDVR9sA%L_i__|(nY4T*2TTVM1*}P%KL3M&ZW@zypmG#sN!oE+C z*FMN~i&*^BG@i}r6Ub{ctdT@BvbAii78)#W=7+qyWUj^h`!)UThmUt3>*?injrIRI zht2|hKP+n@1VuZeQT$cWd;WX~j^1i?U01ft>It(Q6$t_a*RP$KT-o0^m-5G@eaKvZ z#Zwx!FJQyxyw3A<%bXr2pRbm9w7ayiue# z;)-RbNTYHHq+su8$I`qCS2uARI0f3A6>MBWVqKI^7j^4@{sx$+UFv1IKKR$tam%$y z-Yt@ddc{_YqCcp&SXWiSsIDo+u)}QEi+RW1F#TlQ}-p$eeW;F6kJJMwmxxB zOsFkhbdkZcMRyrC+Sk6?I{0}|55v-Rwn4$WYE8v;Zn?_As9YTsLdrdF^i2)QKV}{I5yU#}X~Y`u=4du3x21wa}F?DI2%r zJ;u|MZ8sD%rqXVWi`R)noIad3HGz|#8i|?k;Drvc)5-B9kU=aZxt}F718OeNi1vW% zITu0RusJBB-kDvJYYRWbZQI{f+Ub1eXf_|u4RATh_%hRU3<9QJ;9Njg!2u*TnzG=Y zL3+E@H|z_qw?j$!U+|wDMDT!H{Az_NV&=s8#FqQXW!F>&25fSV086sic2v63X_rX7 z&MiWx*_kn`70#rki#?b5hz ziZJo^XVd_!o`~DLXq6mLDil7w8hqF^j_LMr58db}M$;{->)Xmu!QA$%Plnn@s|eFH zYm4!mifXay(la%eBO`nUrP9c8IgLsi*r3ICs^%PpJYhoCGz4*TS3S6Uba7hKy=QH7 z`Yip(kFRxy1&>k14`^3S>_Z$a0PLOu?(BPkegZOie>Ln`6rILihcoGxr$A*Y z2=Z596`B3fu|=l!0`nW4TqQdPb6*TUPd3oDg{W6GZrj2&Y8_q>vSq|T>JF%$(a1#i z56e$4=p65<+f1j;RrujOigsX^>nodd6Plbo9{GXD-h%_4fpPz5rtup4q!5xrFE;{r zCe2E!y$_yNvn>xdJp$T~1 zcCS^xs>R;hbb`7-qb5Vso%dyez~RmkVh4G*#lf~Pf0-da7AW3`^|0;Z8YxqYhxv1iP7%J`6C`*Q}=@(zWv27 zys#~3Kk-)yx(L2>puGlWTc*zt2qvy=(I8z)u3X(7qhfGDv4mtaB%(QALlDerCLb3%G&J3tJLH^$r3pzD^5$cqmI&U4l8SPrIR6r7_EkRbS%Oxf?I3g!g4t`|d9S z4GQH7aAomY)F_NWVWrKDPs!IVFDUjpMof1Q%zAD!ZXl4fEeG$>P_&gc8Kr(#B4=xC zS1=e+_0H0>`HN_a(V$2;`de>=7N0hI&k^;jKnhDYx`P47ETi(%a=75~YHxat5Ph`B zHmDEUI2Cf`)A(wtR66bVJnFX?b;8q16-TZQ|_h zy>}NzXLRLTF~orVmKqKA&=R`YUiT9353X)|^eok2;^|%yQtF%h@lDg)9%`8SRuA z4u8c?2$s(4Np*(i(7(4;coV+SXaBi@lm0zLjm;*HL~QrgYL4T2fBrgsf%ZnjjhaR` zyX)x4J!Ax*tBOiZZ`Z_!aXy)o)uO7$T-3KON5jdl^F7rwn24oh$ABqYw6n-5Lh%z~ zAj)lsEbRuT;x~G>^F^*zb)ODJV&Zp8Z+#xhY?6pBcKLWC1wn-)pTy%XRN9*K|uWgfOvkkcX#El?vMJd}p>h%AUa z5YN$bw)=O8MK;OhQ?O2-v;^$IJ6 zHi~Q!yZT}(6ZOIr5xS(7qD6?;C=$-!>sIK)(32vT>UQdsE7bq{tbBMS47HBNJ4A=~ zuh43W#y!c_o7X^cWmZf{8M6@gexVVM z0Zq@71Fqc;Kf8M=HI(J&Ry&$Q;7{JX#UH_?m2Qpwi4ETmH0_0`=GL>mO&m)KoP=5^ zzc}1o8Aw6GM{*u31LDAjx;h3FErcd*F++;jf6@~}PD_g-icUcp`2*_E=YywMpQN%g zbbk8qx(#2xS_RMUN}u|j3I!QBd%Vjj#EMd8%2luBgNEMobIB8Px6U`I?sa7H**csv zvBXLuKDN;j18gnF9ZY8vKLt-h+<#7;2Yt8LnoHlD9-f6NGE1<}^EJMkoJp{aMR0>+ zq6uvQ>Nfp8tf|oWr|Z;l@-2R^VT3yaTX`-tV!uRZSP(p#oGj~6TsUXL(YF_U3Ur$% zC(|1bP&45qVLL~K6BlS-rZS?X!KiNm8+rvSN_Omk7l+@Chf||Y!;CtWZJR-}F#2oo z)mYiK(AV7-R5Lv1t26b4$zr#O=|(9XVNV=yFe+E}#@6#AI84|l<&ZD)+5n^N6YI$F zAtS+Xw@eGxt+#v5OeZ`)Z;F(^^;xqsQ*9~bX4XC(ohLl^*5H=#PF)y4ZosCw<)6TfWfh5 zby%058cycL{%acxD_jW^KowV-#`ArH*rc4g)|r@QX~4oiK2bJ?E#0a~UI8$vj^D_W zFunh(mbCDJvifZK>5AcMIlb|%(`sn0g6OQtkj1M8QO2N2g-ZJsVSSN3XcXzee5Y#v z;jxC|^v6t*g{yTWDM)oELT3^-W3;)h^QT}&E4c^g*<~$gCpCq>jPXjU`tp0c#dVgzvpUT8Kstqsb(^p6uCdreR2J>@Lwm?fx#Ga+Qs%-IO+X#x*Qe|&eh$FMh zA-0y>Ale7C4_2|>ipVP+kUct?A9{!>@N zDXjeyVZ?2iqFuQ|4+0;d#bdYg_-HmFbPh6K(dZuEvDZNgxpo}5AgN9(xp1OJM<2rv z`Jz)s1f`E<6WO?P^jydLmcF4!OG3V6fCRhZ=q#iGuPuf}$BlpMrt4%$=q6bLuSwCp zHiZ-3&DE#^!`7S*Eb_>Dg40lc6w$+gQff(>Zzj6X1^T1vz3lPCzlb#s_1@bx!={FTPC8F4`hyjT2Sx6 zZ|SrCRY8A3gEOn0BZf;6bl4*G@?bW0iC#A4_8bx(SWWy4 z=iIVYKjpp9KlkgOX#lueDkK{EFF1wB0;sUM(^~sXA821Khl{#}y~xv?Or&Ci?JCLm z3fs)PS0`~;G8Af&hWD0R3WfbZjvCw&h7vzmycqUnUZe6_?5L|dJzB9SHaljj1fuF- zLf(D}K}CW$b=uZ2Wmvk>qzFRc8BAmW#`dr`Mst?ENg-9N;4C-ffgqEBwiQhU-17kjYY?U*xl5=}2=+0Ns zg_~mi#@%j{?-dmRs%qkQG&tcw;3U{?Dz`7I<-6$LB|l5qM#5EtT}5u>D4CB%jCO?4 zl&FrLK0oQ*DNqd)5C~JSF<{}W4k$a|Pxvs-S-q&E{tvUIDdtGajM{Y3;H%T1;97aoeW8dPLWSz;5d-9| zdgWfm=`mKD*QvAptDyV%6^aCK&fKNts6F=u?copULQJ5Lr^c75c-sWUzmdf2ESfN2 ztNKNSIud70UkRAi_SL@gIfUDcg%IxFVv5cdUXzck+6G;j2|qgZKFTDJ+q< z_-~njn$3?tcnvw76e<;0n!yI`YPUp~=AFMpHxq?c(X!hvFZ8(E!Rp`M_eWbSLICbw z`zvL*z9`hPBZVy{Pt^?U___eLveOd?xxwaM>51v%&}|lis2(uZ_x9bq)ieBD`w0aX zF}E9KF*vx8^6Yb8OYr@r-O(u;|!KzU9KD1x5O ze>YhiGDE&ZzJAo6r$(Y*URw5WU+RxyI=WTcn%!E=THal23YS4PC!FVGxt-^U?$I@r z)V>QFS~m~*oqA_`|;R6-lE!NElr59y7xkX852)^{l0}rI^j&b+g1S#H-WnvZzlPj$o9x_Z=jqc_MW#T0BV4@aaRs`#t=`Cf@hgE7KQzSCx^r`DAQ zvY*nRVLi&bODSyoQIDX96~IFbpIVgtTObo)uqetyY$ID2Ok! z$z@8VC2;bw36A5zo%dG7d@)arJ7$`O1ml3QM@-9=nC_f&B)sa0))y?B`5+pv1foy! z**vyuL-S))G6@`bE_dxzLWCE7@dVd8 zD0*r1)op4WHUZzo;!wS{N|x7nGCzI%aLf^acsVLHwIo}EB6T{Yp081o#{5CeBU_-8pf$`?Ji!z7%4c9$oc0w~6u0YeT0FSe*Z2Qxn zW9~3lIrY}=qWK|jEFzeI(fn{qXI+ic2x%Iqw{F8?_O^TK?ku_1Azayg!;lg)9>6i_ zDz=c}ROKt{N3J)03wd4@TA58xuJxt85Lp?7Jk^1>HZl0|PraHN4d3d9a9J;)bjDvb zI)B8a&r(g|rMylLkYnGZl(x3CTmPjgo~M89bXKb6M#)Q%j8YZr4=H{DFi!4UtNj}@&yhHLt z4f75$mxn|Y#NfT7gbn6e7n3fvA>fFGlJh4w%OomeAO)Qii;4_eOCR;XF5kC)mu7Tb zWS5(3-c8R&(dMPCUgx4|Wzb`Crmfx7rU}Y(8rlxprhi7#!lF_0x{3>4Di|K$Y~Zy$ zx(%vimU5wDhQNZCXSIR0@k8M? zTxlt6%(9spxJN4m?OXQH=xTs^F!5jdLI|&T8?77dA@sU(e^HG8v|3a8Q2$&y0G}~# zv~PDI<)Y|YIntR3R_%o$7Side8MWXnvoF~d1%NU)WTs;kRAdy$a3QHODM`F?a=4M!g@bUWh6n}gE{yw15 zxKX*TDDtna#Oue_&<&*=dFpcNtbuGfXCOL!(0F(2m3RPm3Zkwhr}=e}M%+`AmlG3r zc9`P#4$vcvW_&soPg6zjq+8Us(t;6sgi%P&Y-y#7w$!5B_RuVh2Jlk0$i_dOjpCWv zNcZJc-5b#MH^IRqyq<&k<8;R)`fUohm3AwV&ikYv ze1TY1DNjR2w@GE)E3pTL2z*pg4h=;{%DAxkbJI8$={5Q807!ggYxNsCbv8g;#bZ?L z*S-s*<9xG+J`xQ=S_2%5vW=``7{)2d76Sn5?hjv2gKRtR;5%&-K$xlyU8`*@e{Qb= zH0lLV-z_I#k?}>sCPPWN>5IN>JdBP~FzPM$JouOy%Jsk=+4&W?ZG4Q#M0Q(lehqXz zc1z|Dfj=Icf{Z8RsWKncSnVd{I1c4cemLd>sv=kg9ATTwYBWv9@L_TbxNI$C4Q?#8 z;RVv^`s{=V8iC&HiT3mpPCmEQw+!-G9(Py%jJBZQk9U{QKd+tul8__m>$e82oZ>d~ z$Su5_mO`zbiWH8s=hay3#P1MH%%GUjXxhfu-}tV5DsYWATDJo*QkrdrnZV$7Yy9iP zKb)|auBANrW3qO^)mYwFwlN_=UTDS2ZAUgV5Pzu zn9o~Y>(*|#Ts&;m0^vGl)R30TK!)pvu--?Fb&LlJ^}am~M4FA*C8N||3&)4Z44Ji) zi8ScYfCyAZt)cx-RrJ;gum+Nfpbd#$>#ns~{65~ow^rv^qFM*dvF^mJlSIbVJSPhw zVTlbOIF`PnJdn^?Wi?Rwjg^T2Xv6CWT;#q`jTb>WdOz2&{?WpIXY#e_QSQ1lk*xk` z`k1sAfNO{Al8-C3Quy6okxMn~tnIlyf}bq7ofgH6(e_EO1&A^n|BJ*Hj*Tc*mzymzgSd z_8jO`2YvHKgnUDjgNaY|lQvR@BiH(->LMHkZ7|!rm~w-sDSC0yZo3%sBM{he;Jp%h z;KbSjU=R2Mv_;9vCoF#kvXNL$=|-_gqLgj!O58w~)2u@KRC^otgc}!S)8AEgQ?3*! zW*w90Z^U2O#_pXI=H^h<@o_=fEpF#Mk`m*AiJY#9uLO=(aurtk?@wNNOv7s9lp8_o z4xnD?RqgmVsukN6MZbd2(xwAYzJu+q!2W7SliYPc0|*Fuhfj(2pK+#9W8ii0xKG%T zk=o}@8jheHd(2RQrrcLS{NeECz12MwWuWD8v^vNMl%o(I%*9E$(oW{eaxOzH3kFRj z)vi?nQx$ON!pH3NPX7j+Tf*AD056`4Zm_S?cI|mW*s$8p>Q{rF3d7A?R%X%I%A&2B+;fH4nH8-6g2NSf|%6W%gUWK|hlZ0Yq zhyEm=$jT)Q9=R?~3T~O5}0UAxvYlb?!?yRW$17^Vs1;e z55iHC_9Ex@424J@N7i9G8*}7C_pMoaF4uXKd>zhVQd!XfUyuYocWKAkLN{M!hc^ac zpPq8KPuR4by(b%6U zUxYs(#to*hO-4+*9m7RKNW|%1HaPm4+5VZL}Km{*XFmZLx zNZF5+-<*R+{KNCf8-?Y=Emi4-q3M$Ivhy;m%8fjZTaf`V#V*uhhDAc#yv$0;K4CA) z)JO5D?xuWSKfypl`2=A@ zeyIFhTR4-^-KKzVS*9@_*Iy`PLJYInPpsJ*o^q(Qhp0ZH=Pv%kQH5yO-%*_jl5J&R zT2h)mRHJmp4cy}RY<|>>aDGknI|s3uqh40{Rjo_oq_6APpDoAwhbu(0^7&v9^`iH6 zNBrW7$G)~EF|VeD{ojc;FoEmJ$6LyW`Z7ZZCdIeC3)!C~^0|sahOF%@0=ugQ%ZEaM zXF$iF>i8$6DiIaCYx`H%?on~aJk31;s%$~Of6kjW>=QIp%*@W04jrjNMT*4^KC7Xt8mq}G)dINpx5l77B@ zhm_0j79N8k-a^PCkIf?5=0H(Z-u$73vW(?SW63fbIlx_`s%sd}y9=6(hw=r07BSox z8;*x}F_4`i^K7Buibp&hDo_fnMIO_nzl6O5E&OwCyES$_1=|78*Ep&p{5o`WqB~%E zH<3zwfaQumWv>q9BI5J(%TDu~aMwcm1g*|<~ z6Eca^4zad7>61Cma_%}46%X5}O$Q{O)F*;ubv8?^ovqR^#tV(NAxsH$fz$pxypXM=WZD9cZ*Fc z&L-vr?f8vA22hdjLh6heH$-={z)WiS@weSPReNE@1x25hj@M=YTBi@gw!k7kgI_6K zf1EgOQP(Mh+oXI=!!|`3LC{@&h?*c^As)vA+KkijJR>R34nN8(=oSr<>12+eB3Rd) zBRqJQ0!j#vVbqiY&Il1dAQ%XTv$+_@*~zN|XHh%IjVdG@LLO;jx6Vctfw0GIpJd|* zne1~EiQdwC5fK1AtPQ4fLL2T>h#A4Z0iDG@iiBPFdeB|5b8Mb;zaj zVAIDp^=&X~7CQo$UfZ;W>(3qKv+}opMBA}KF?i!il;82+pEGp^Z2BHc>)R=a_>b8C zB3b!O75LW=vI!_$;K#>*0jpx@G$O+vN-lpOb5Ab~4Ykc+a{ij2z52ay#^#Nl)81s! zfQH}dXkkN=&Nn~vAW+|Q#c{?&Gin-j{AjAbZcUepeJvphxN@Z@@-hxj%d6!oc4lkv z20p*kqbw_M0hzAf^}7v@u2!F5Rm1N;3#FL@Xau6S%1?^9J*L0;Gma*lK6C$yQ>0!f zs0@sFGEL((Lr6G9^G0mK#}l4LiO5(F|H;HOTaRvbIjgOyYV9Z=Lv4bMeQNiX+c{&U zN}{*PALvUVxlF*0Qe1sd1VT5p)8r=!4HLV`h@VQCu>ofCXN{}pe4V%1xerf2$h1u_ z^tbBLJ8tQ*J8MybD+`%`69N7t&UuAY5H~3sv@;WMnMr(#91>Ty6Ph#tJgF(!EX{jc zd7v75vX3F^Q8!u`OF|$3zH7gHp`Gn30!|m!Nj3GoKR*h%q6k+_zn@2p9(s|kealg+MP-;4CFkr!VpDT?&ib-K?jMU%{wIyqA zF;1wNGY-X&BJOS9*;xq>1Hb3mZ zO+jS3*$tawl7=OEFmZ#yjXO6b*XY9ZrUnulaI~4W%*Vt}UWd2Otms~-pnd}b|Ng;q zKlBd(>d6IteG=N^*wjHE$s51MU&Q@&$;z+M6U@=m2>YOY<5l)~)3h>#%-3-SmO@Yt zb1HG{wi%#+u61<|0PJ;faj{SmJi;9aEYRU`{D&LB`bi8h%~Cn7@!xb3KmLe?$E+d0 zCtwqO|L290%pBJb0$x}*@WYELVw3#hq)k|&nY8}eV6G!stzOb*zJwEZdC-hI{d?PR z1}9=CQ}7;yfi;g0Z!$zS!}KX8;t`iMe;}CO$)o|M%4RYI%(aESRasxpuOnr!z?H~$qT?u$PGsu6U=O*<>^4uRZnOf1P*7J^xQYd{>f{XM}tJ9Ez z$vzW!cP(c$CtoqOw&SP+=023Kp%Z1>$Iu<@MLMl2XwaExq1US@2Y{xRr^j{#%08#! zyaWb4FkKWdhO$>&k%h%q)V+k^^jGMr!W8Vke9<1$SF)KutD3(04&(jX8ZcSsD`s#B z4z5{|(+~3;1=?0)2(W4crVD8#9T-Jf%TV^!6%(vhW^tcS!TrK zVctB^<8!d|$!UG|;YLmlM2p)*eI_~{?tY_& z0^HWNmpeRz!1G$ZMuo;%o9UQm7tucFkByy;1_Vy+QMupz;q(+*h!czViE!nT3MPe| z1j|mBYLE8=)kF#rVImbUD zVqG{WmPWVk;T#3mBZjPB3cfGASMI*Bv9oA?z?O7oyHs>?x;_&`_=y!!StaVwczU>?DPQ0@ zx1W_EMdUXb??RHe+!#pk?VYY5sU4x>uB9%WTgmya7{yaf9+-YtS_;`0MDoo4uE7UPv7Smb0`Emalvyb`FjTu5^fzj+_Lrz?Rv7P_!?P4SbI5qFVyt%T z&V7hay4~t<_NUo_{7-T1ecpQ3pNyMs)BC%=_74<5`fLGqPZ&KQHqulCzluU5MFnO|?u z=jEYH*Ca?qO1TvPTF+Vf0+*bAG0Ra6p#^x`>cjfX&Iq#hHMI)9%+?4-O&TOnNWiY# zx&T>aC|CoK>zZ&VHE{KyXQO{+GSdB!t$?c!SVBLsIk@lm9gtIVG-`^70d=quYNNKzgDHOl{84vWFvr7a?{kfRVBHN9v+{ z^vFngz(pWrWLamRoVsVY+9!@ccKOk>_13)Emhh20M#@x#YOJXj{^60V?uKaT)y3a> zrGGz(|I!5AL_Nl@9QG@}lGQ0+AhN@^&gZ)_{g%46G2V{C^2u%sa2-A0PXO%T*QKV) zOC2z4pJH7NfFR9Ho3usTA2;yi1YEpgx6|`sZmdi60U39O#E_g&>}#?D4e#m+?URVWU4OcYRMao#2AdG|@_b1ucy@b`l3$83TrU ztiW&)@A-66Q7Vd|;`yBV3)KSmKAE^BE5?TbtOA5Y6jq~&p(wOx>w(R@1C5qqiTO+^ zLmO5yf=VX`#QWtmmqUq2es{i{$&X|;l*ae|@o2&ROs(Y?#(Qm<#m;OG#h_digkmkG z<(k6EvNg{qM89W9#T)&c`XM?wD%#+)>*u~Ro!1R(R;zRK8Yy0SPQb)NV6tdkVX|-+ zmQiq7OUVRHqW%ek%raLiH+7)N@6Du5R= zN3($4bs~iY5FFce)#N!+k4W-?DITSa5S@1YK_rLew79^}6f}6I8-L(K8QA(a)5R$_ z`C1o3T4F9#+kx$O4eV4CdMiaiJv4Zp{T)JhUWMQJfr8b{=D1qjS7^)nDz|9I-z$Os z>|L07E(-;%{&=~2zl!}>At;B^#txs0ZJE=0zOO$UIgO5``uwSh+e=1O_Ghg0+222- z!5uqloc9e6{8)C{yG94@l|F^WC9=D+rc1^*(c=q3X-O6@0Di{HXmGQ;oY%*|C1;AC z)P&;8PK1FFFVtoF`nkr|Ry!ws*@0t~JUWBgfm9}o=b+W$kS|Jud< z*Uw?1U$Cj2Ia0`UhYachWOoCpY$i(Apv#l|tDz$93-F@;Dtjz~`XOPTIS~x}KA&Lz z-aOS1F~o`hWR_koS*3)s!xK@#H4-w^Q2>=ft(USzalCAG*c)tAxuy(kyGEz zK%nPfQQ3rHmd+DhN#}F;V1Vh4^Mz4bB{V znXffxD^oa!+fU3rJUy6kQjXW`PcU$%*a_?5#J0O^^7rS@?~I3mdO!7eq{_=L0&T7kPaGd-E+tz(sBcRCRX0ps zr+quq#8@DVlu+w6;}Dm&u-h+MY1jtoLUVuT9n^QP`B0A7Ca`Q{*YLPO&KC zJ81p0&C2C6)O#L9V}0LFlG3bNGMurRZp99GqVAi-fQGOm~ZkF~cBt8&}E#|;#e?i2*1OS;*L0*V5X(nxM; zVbdu{r%1PS=aw#&Zjc561!2?OwSQ}$bM)MM?mgdgKhN{~Zv&6`vfj1knsdxC#uTqT z6j&jYUdc1piaXDS2|N-h4ib=@@N$*Vh*ZwH0ndCkB2uYu>^zWI#< zwO;zK|2Ta7K7G{RPL{j}CS+cd{Zwv7Jh~)_c;plM;13&wkC?LG03{-g4%#!H<5l?(@@3!GV?OH{T(mU!yrkE_@wEp1SDOF!-~G3nI|s$L0V!{T1U zo{QYI2f*`S&&J}{uK(K)TlY}UOP=ws@3Z<>OFSJLJX%Jt+WfL;yO*&r3{uXwf{&lDM{<4X? zJ&E^vKzP!EU;d|s#F^x6F*>NkUCf{&4=kD|$qjg5d{!E4$BDCcTEDE4B{jdOjYUso z3{OO6ALrZAKkoF~-~3+FN;}L$;s1KvK41IE55u!W1$WCz3Z|b1H4=o?V75!o5BAni zF`tbOy-gUH7bnk}F?R(r;0ZEeil1LnmsCu&w{|UViFxibE;`%&s03kHdq#-j=TiU2 zUitOToa9g&nJle+ykepRfFWsc^y*i?R`DSvNHAp*i`oMJD2HDw{I+LKI38rOQ%EPa zeh#DQmxL0xV=7u|V8+ACx9g>~_{ObA#w-^rsc!2ItB(kdIe5U_j1Fn-!Yt;5sQ-s2 zdW?FafU*C_E^cldaon8AQqJjn*63EE1Pr8`B|c%^of}A_w_Dm$JULy15J7asSfAa7 zH0b0XVZJgEy7v1~Whb}4UF^=9uU@Li#h9FC&CNhxl&Et z9DcF0P{2tCWx*2&j5BO;;xt;!unCzC$7k~C^IHixxd|?-+x_n0RrHX$C*;&(x~{Enpq}5p={t`Ccs$!e1OAZZvUd3Q=FOsx_-oB`NG0vHmvuo&&ezl=Ob?Zo3Na(6 zD@Nj1JdB6TMVqK8RV_h)4H66cA1t;6emn0%|F83Iq2<0lX>T@O%>ZPb(Ot1z4<4W& zVossmr=HHXB%K7)M#X{~-D?L$7rK=~;<%Yju(k<#P2Q&7irKWjf){g=_;RcFd8WEX z&5~ow>B3uPkEWJ=vc^@!x9{`p_Z{$=;ra2tpZg%Ak|qeUG(VEoPR7OO0x4rJgRi%1 z?As^SaG(c}@~iFlRwU0-(Ga_XKh0Yt?8eWC@Zavs-(R9G9*FqV27i`V2bV}Wf8JSM zFCTy_yVTkql_Y~g=^c{-$-$@l5|V)=fNm>S@SGEC2mWb?KH-v27QUR&7UKCPIi3jm zg>H?O{zHu9=#O#c$Me!lg>z1p`If~awb9c$dK3GTV$z4#WGN*P_a<%>Uj2t=F1Qe% zqE^4N*zwP295qe<*el=%>iCl#JL^rPLX8AY}q`yoC7l;meWj{_|yZPv{1jff4TfpAY(%#k`H@+lSKb0PW?MZ9*ns1~b1m>lx1M zeqtBJHCvzpgfc;dqLuCEHNc9YNEhxUpDO$S#dyXtSbHK|t_;SovA`Ce#u(yKi9OY- zJiFtbTJ=)0%6c~YmZ;+p&{;0cWjz_rv~g$EEBm1(PByoEK z#%DaL(d#n~f!MF`t)K2*Ixn+^h`VjQwGC=0yi?q&U8BompfZ25Ps!)nhg+_d)m3LJ z=ec&O+=KM5&(?G0=OX6(QH^8J8pJRU2pzIb$ZaMtvF0^UVJiriLo?E)FcksGXXHZ+ z|K`HUKIBWDvS*+T=@K&KoCeHX3Yu{#2%s`=eT}468hhm>xT6Ss7nb*yo0b)9JX}3z zWezuHNyk>6sARsnUvs_ts=4$`GxnUz2_h9nH*|c<<#DNV!ApJNk}65)Ol0$%iohseiiA)hGuD zRKv`|32JM{lPC6L)FN~I`3j9>da?<1ZrEfzK~}XsvMSz zAlP4z;Dvh)#IwBrl{WnChkKU@P%&vV`)?BpFGf3)2wPyMl=;5Tj1YzT?Jl}0fLox_ zcIaIe8vuZ8Vk87W|MxA$aEYAFT#HRmyo4dA+=)Z}hu%1Wz{Z!;fz)D7FIwzYCsQPf zFT9A(h}m>?CkyJ4ocrrTy#b2YBzRR;LldU;J2t)Xa-w!G*VQ`eERM}6L@Yi?wB$_# zNqzqLGLyW3wWx!6H+{>Lo&weU9p(>)UG2JcF5Fq=_>8OOM&Ew`lIhT-J*7Q-Q2y=h z%h=>!p9gVbXN*?cuL@MWoRAOt$PXXK{Igz)kz_Z|n_e!vB$ip0KLYN5u$W+-JYKcRA=^+Pq??&( zsFk(hO$}E4Zf-pAv5|8Q7PjCXf7hmbYBi*Fsplj62J~QsqWJlyau;0-@{NB;xrmr* zzDml;-iFEXf_Hm3&CthB^qKEx&F;?9Fa(iiQO|iS@;K~0qs``8B2J?l46tszy)18AXk$)EpG{o9GD58%}ydE4IIu1gycs zea7to13VFC%p#7&sGqFb}t~y3C67W62#1V&Zpug-kUoUCu3wS!06KcHd4VV^!&Ix-O4bV&?y`ZI;iZ|Rbl#7RG zx|qV4rs5BYCf5s~s5aT+0om@&63$Da1eK3bzVbrYkJ z+Di$3#}D94-8SD7F;K}#h1&0F>#d&^)2H`cNrMfSwMYC9pcnmbNxSUdChb=qFV!2b z_76As(?>F6K5cqA5i-{v&UEPQ`}yj_%6@E0f$od!x!P_62-NBUNDGbsw;? z-s+INEk9CDP7Sho4!dron@A80cZ$9?YbJ)7C)~WlHRagFGUM4quumNC)@x8o*V-iu zfl^SX8@t;(YO~d)JdTx$%#g;)26rpxgJZ{iesjT&ic&A%xFinEqR8XJ72dZ3)-#Or z&bPp{BO>zLTc>*f^#;SmsO3_4bWSv$irDWw%)uI2Y_J-C&sp(u<`FghojVW17RXec zu;-g7fU&1DO}j#zk#;cq6_XUy)~_UHRk@cmf15>fL4tQeL&Nhf;{&$4*PFDsm(!hcRwkT=kQM1l#!IEX!QOB)Q82*#rsI z%4AVVnKTX`W^&1mu;>CIo=A9r+xWocC?4aR^Kn4W^fumS;-mK~_}x(qEE)RxLvp>; ztBr0c_SHMC#0&08bW*l8H%*YenRzXeW}L0Ftg=9!9ck&o4TowOZZiZe_EL~*)M>nw z&@4qf?a~%f=7By{YaKfm)JyG2;SB}>M&p*`sM{?*s}o<^9)$e|^PzV~DXLQa2N#{~ zG|$F3*Q2|08=U*q%WOj+LZu482`jfgZvPMG0vmNM+WJlY zwgAMqlg5i_`MgAfYrUOIzKso&QX>pej5jbUIGPQ8sk-Y#uc=a|whvVHxfaKV*kdOZPaX$8#LjqG8W7VO ztZnDTj`>onYMFhVy*f$MHJ|o&jP&+rFt=>&>re+#&xGmfSV7TOc~_m{wJD_+=0Z|$ z4yz~qQz_D&bbJcugif8}ZNqk%&mC@1u=w<#b~=eMrxwqcap+0K#dmboTxhtwS+$~| z)nt(R`+noY60MNc2wqL==>y&(t)@WTu88Ex3QN-vkc$;R&)xG{8!ce+881q~EZaAa zyV{t{CvUQ?YN*;v4)|bqV~=4uC2Uw*^U7h@=LVrtCqb)GHWUsKbJyG8?PH%USCw&ApK&CCvf(#2k|ZZz zWZmR-b)0(vk8|#v6OIyAmUhO_I|O-jf>iU0*pb8}9N{=wYQwwjazqghDim8v`{e$F zj-I}xP}k9(PCp&~wHe+$*TBtXmRY&?Ds=Pk8g}iLxzp`0J!8CjWBH(yq5?~EVED$B z1}6~Q#aBs$3l}urm9X}2Ba;Yj#2Q`8@2_B@g)^l}$h`kRFSkU+%oJk(x2f|lknUdT zZ*@J%8WFvOkE+z81XX21K?tcd<-Gb?4{~0_u6~e2xubFsft%$!7=Xwia^6*)SAY5E zFTQt|0Bq{rfQb2rj1$BJ^q3HhODl5UvFbMcP5g?0EE$_>U~2s?!#$(RD}MIfAwoJp z+^H^1SfD!S8)T9O?;_}im1x0-i!d}u*Elrqc6Vgvr=mkAbXZ$LK|)@pDII4?G)sqM0R@=ssUTr{hef9vA*vrf3K73 z|9PFCF2l|Y^Ly5$b>F?Zdq!hwQl#%{3AtCtUW=yP4Hgj&>u%fcX3;3mT)_j)<7|%t zANmJ;&?pnN{H3wG(Cd2juPSYYN6xaYk{H3r#m(++lU}MmJKYpMsg@R>;G{+CV1H4` zdUmLCHNxv`d^_@sC5Z@3ZG*bu`lHlocXNJEC(FjK(32E)?Yl!PtEG+9=TC@}IBGOU z4l}i&{eH$z7KM!;jUv8r@ncb}9r0YB^>>4~Xo;t(kz(G02dNquPT&%Y7HqqPAUrn2H@ho?9EFZX zvU0aMarC!-z=Lp!rV46!oGPM_fr7ILB&ZX$Y%n;6}I>o1>4%li*|{Kcsp;t_|{Zev2J;ir(^jV$z8E*57-}J zmk5C(+Et}dQq{TM+q&_7WFt8n04-0PU#VI2g%y#C4yIF^kE9&!8zu0&HrmC*(X?6< zEdkCT=mVefoOono&@SLl<}R*A`eMbkVauO9ibG4yFvM(c17s0alXP`1qm~PZTK)ML z$q@Ia8=;&eqrRCNXs5u4EgM)tbuWK4R=GGyWZ{F2ijmKA?pD|Z#e!*Ek7-XA*q$3T z7$P_yxFrZ$gQ38d?{3<6$?8fQDN@sxvjAr{*TX^5qFWQe8Y*3LN}Np=vyYQS4&SY- z>G5S;-(T&h90^WY`07@qS8o?Dffo-~)xYnut^mQG=n7Uh*;BQ-$Zd8_T^<{*ubG9r z4cQ6<*?RW%P>EOx@r=rg3w@G~L?)n{=2fY)GBhn#E8^MTI2IGF6c%7VEzwJLcX7ae=p!0Ehfy^@8J{q3YWh%-_qH$mF@xB#pzMHotZz z3I8rKsyekyBM>C+FljZwR(w35jnAonHiY8z;GFwja z$zP}zj17LgAJ&s2kCTWK4ADUAk|J&5#E*~Sg?7CZ)+A+Jk%WpDWn=ZcuHBP#j|DntXMz)})dVLl5J@%n`M>FuX-(R-{zKi1H6K zNmvMhyOa@ftdyy(gT(F&02{KpWr($KHIY zC=q+si35|8a|Sz5?on>r8*u6qi-2(Lqm=#hJ6~^Jll%}0>8eciNb|f9M*J8OP4VeF z;V%sTe$eQSo-5Vu@TCwl`8nXt0Q}(Hfg(Nn&{P&xd`i}*y>zIr0A{kV-S_k?za>4I zuFn@=tYb58&Uru;3!0udZSvZm`x=Y8jGCXHXY5TeyaWJ-RgOWKU1t^BV+b@3@pRYa z#^%MUB-C*%$0Ny{&QK!w!jp)+8uyk+dY$&9+TjNf?=Dil@uZBs*gQ>=dF| z;ljb8UL7T$EMi&1W@3{iT9&!+qW7q`g9a1c+GmQSfz%M2cPk^-#l0Ik zaE&3q;X>1^Ip1~>TsOb(TWHppB}eMmqk1`4PYyF51`@DFwrlEX1uur?J~s4UnwY*F zT@lkdLOIF-y9p=c5V@vhS8KN}2iyuCxOhx&)3<_Rldh}b@vT`-UjF{~&>)=;FG!3* zjxE2|tBYPc;5<0Dz-Jo;tF)8~J>-~OEuly{esTrWWkp}DO7!V(fA}uHRR3y_hgP1N zD?>R(fC9(kUC&(;1^+h|ic8G!M86C}t*pGHjrDsudKx?kfqmC!u)LY=6{kxf z9O2xL>SlQj1-(jx9rWZ~MOa*QVz@Eadh^?ezUe;(jEKkg0^f<>QvSV4UaBasgD-nr zJk#8SiJIEF;ruWS|917~HHhJM1z?q( zE_Iw|4R}G=9d&SvFt_+kRUu_hajAIbdNf?H{iWW(d0{aooYAb&IbK&?v>pmSW#{8k z`$}8f6VHp@o4DJcT$ucFkYzlb*P=yTv(_$auTZn>BZFvFD<0F349yrVNp5+EEedFp zfaNG7boaUslE2hD56Q|VjenX*Y``%}+fDVPe!rM1%EPhSX=C!OT#_&GbU`~x;(>@P z9;8tWp%?|HLuaolh_o{=Dr_lS3@XLU;}30y*oVhy6P`= z@U6%;HM&IqP3@jkcXs~jm%WsC!{L(d*GIu74KcZpe%ovkpmDz2$v&#LitZwa#B_s2o?C0xG%I@ga<;_gM|lf46jd>}GY3REKg z5w|u0|Kfej`<43Ovv2U8{=UeU{wtFa^w))3Pgw0ng{N-lCF4*{9;e&O#9xl9OR`LW zlh(1ZZ@}^vw8N3eP5pE zIAEL2ZeUIv*drT=MOIIHBv-xk4k~*-7DJ72!Uvou_a^q%9fy9@*eu#K>NYFw*-H=a zlf~@U2b5jliLgcR?kZX`$f~(7!1F;9wbz}~OhTsryr9Zv1G=>)Y`;RZ&=Wrddq!68 zub>+>h$rHIS@WSEcCK6fuVYt2PSx7# zMCiW7_~N%gL&gDb-zQl$plII%#e0&Z8?gwDxJwcR--&kLGE2rf%H?%&?U<)nvAI_I zz)Ji3nL4pB>$&aY^s?(rgL3sz*QVk1^i!a&^bS&l*u8sLuv{&Dgu;p7l^y{qRkA zh3$f7yrPKXhBgtsG}iLL*nTbzrX}=B?Ld4Dn|`@YFI>HK$)TXKe7Pb=;I!I_{>1@f z8@JYx9<3b?r0zZ0jWpOZ3qq2A>f%R8zJ`ofu>B>tSX9w({c2gG(RLME^OSS)`k-=mOh|OfA3cj!%wQV$EQRA`1*(9wN=pgHPKo?ry<@UO!6H6`di;8&hY`TR&PvWWgf*Xtf-ZI_1i5#U8 zJR3Vxr(tU|yTQftF~l!OJC;2`E}j>vWEH-z0NK3VSKs9ezm2?WU5lwlVhUGJ4F38i z%K-PCFaQVBrTVr+9tO}fYaUUpjGD{V$7%~v-i7Lqbc60}?qUu$JMVLtpGn_vVV7fM zV>fvHE(2l2h z@~J#JU?MpbI^yo4SJ9BSPi9pxSM+*fN{HXu2@olpA8-V;0T#u>MBl7`5H;OHBYgJR zP{DujckY!-(b|SZEsZ2pq3$xx&wzQalmLS$jdynljsq8hCv@y2C}8*rpKe>bBHu5t zjgrlcL^Pu-fi^n!TGB|3&Z*VjDUGKrWjGkWPGa1Z6j7{OBYpZ%ZF~aead%G3Vo7H4;?Bb7q^&g~Xx! zqjIz%T_o(>k3%JPW#lyKW?*4|o>DfEA?}xc$-;Wqe%#8h45OM@(&*rwtT+`+*jqnrW1p3$kg+D|u-;Q`exAnL4_p3195GVkOKW5@ zD1u2Ed4qdZx2zus9(f*yGsqG}UeU03EB2hOklEm`(h!@-v=L|3eq?kBIg9jpeZqBe zP-m({)qU|t!TxH9d1lT2HfHjE;W+zN5kt8rDFK4!K@t)_g4?z83-NV`#BuN4t6_}LDt~sHCxVGq(nkjd63xI#a9`8fQRz+ z3Ts5iNx|aj(!5Ap!v}MO34L?PPF;a}L%&eBt-pMKlhG$VSts~C2_Q$Ky+t%JDR^Ez z(jnP9hR7$z3@HRV*I$#S{e$JSw=RiB5HPQNbmof{U6yEgaGP=-s>{;?;6Ed~S4%WdEcuG0D0@Dj zeKP@#(bOwfdXI${I=L?y>=tKf&~Yg47mO7OE>@bAK69QsQbbkdOoy4)IRquG%KC~+ z)!fZX(a&vNZ^E{>CmX5wl12+BfR6??a1@P5SDntD1b^|acCET{uVt|YU$^%G7%md0 zBYC$m>^e5}cY`r(n&-&Zwf2@JiAGburR=4q`<+Hxw9~Bd ztK78rco1Cb;aH{2M^-*dh>wU`r|%Zc2)X95Q;U%m`yGiY(HPP9OM8WAR*`huP* zHP}euHopDJc-!M2%c~czY<7$Lh3Yka*Kx^>EBKn*=p3UPPI8J?nLW;&{K6?+OkPfJ zuZGcthMZWSdZO{`I^ktz@NdadV~caE`!7>)&k<;AK7C25I;3~m?w3uN7$Y*ko!Tf$ zIaQOp9hP$X+!_epza^tfLN|r3SrYm5-LJD(7y22BegWqH{^2P3H37A2g8IMLc=t3= zHQvr}N({!YSO!SsVyNyvZ((pO=THba>?JTx<6N^{?75FkIc(4NdEqk34`_J^+8t89 zcta?d8+urzR#E=s;5o?F#8Np`J`jk&u%MF3ypES`+BBonqSm#AT+NdwQ#;lwz$_*Q zGP#nZ!8o{NsxWM{v)S-Tv`esv{mTb{&ThZsJmtLeBd^0kJk_N4;!{UhQ5%n5pS)J( zQx;>}Jb>;j25MXqHtxcre^Mc$4rWE3uEEar<(Z_s(98|ne+%xv}o^SAULTPkWGKD^y~|M9Fl z7w<;?JgoQWb(|$R$Ia;sASjg`^C^*58cg@yyK7Lk`_&n(Gg3FK7{b^a^T+_%C zd2jWU*YNu1=N}^!Yc(snhU=iDInE8sM=q&0qsB8dY-v1Af?I%EP#B*aW8i@VBr*=5 z5J?cQ7+)i5N6Y|PT+Y)zm)BgUo4xyaG{L;N@bTlIj^QFw5+Pp4#u!Ae-W?sl4RD|W z$jo(2o5nsfz{O$Ksw_N-+HpxZIcGmHK9y$@brhoHYeJH;Xb-gJU^VK7n^6zHyR)2P@`u6BM8KMmO& z>Xt-1`>{=UtQ*bgu8G1O7W8{15rAjgDMV601M%px;mUqOPp%ob*BdFlcoEUkmGaXI zVAtJcaTC6?xw0ntz7RStP|~fF+8?<&X4Dy}v}4ry)prp1Q!INsDO5XdN-#W)dsZ)) zbwFmF(gE%pF+ID@RMEqdVD$-h-O5$o2jbG31(m8rQ$3a&u4FsD^;XJ{$i zr$A4Gy|to>`Gp?;%}d9>_Zm|A&o7-=1fBj#AvxN;+qM|&I+a0w*fjLGl}6iCT}dp4 zKd8prGpY$iPK7x9-rVyc*aEoqjnE=+Cpt}Ak346}o%^~Uty9u#Xs7q^1L%Ka}oPY#odGXyeTCPI1s4cHiHxaOJ)5C4lwe=bTZEg{J zVETZOFQi2DpehQo{7m-`{rcNV7^NNK#Y;dw%b>X^tXf!}r+KV0tNmccCb9f$+8cP-o7HV_Gu-+l3a+an)Uh=kd z%bHw>p#ZX8a%qhw?6N3$$jf#7>SX_Lu{;#HOhP_|(kK-@>=w)U7ffK?0nOtljO6tK zB`v$VK{1MTWia*fiK>e21F(PTmO&m-@AnOBBD$ORoTdX=z-;{8!K1rq$Ji=E@c47GRezZzz2F9wp%lVc0y#gauDD(;SU^QDd8R?+X zA|9sSjsvs5GU4b%~snHoWSXXXO+9%E&4g(~@T6OrVT4TTmK%tMYyZXWkh~!=nmZJS!=AY>H zUz)racvskYe%m@)ficeV#03`lY7#)a0|d{Rof!gGGj7rU>k@@s;t`p`_x?FAIGyV< zgWZAJx5D7W_=LK~9p`*|nkv=b&sjo$DtOZundGlw;E`S@dNcfz7yb8lc8>`EzrHG< zXS0&|_ETAk2jqUBB$zMO@4bz8j$d`q?}2P1Z=CHjjbo$f?SeTT$NY3zb^h>F1Ht6xcUxEc(Px$Wdw_6t5y0_ ziTRhc`t@Z1!Id-G%HK8>5ZwJLm+NkM{al!Px5-cnhmbly{x|pb=swOZA}>z+j}kd~ z`ckCcZO^qvrXxCpsKYK_@shor?$ak5U{M_ZraXR1&An2C*d2$>^Wg;0axs@*<^h}W zayh>G^`D#K-y{65r9`{*Tc{`czl3`9%Ot`Ox1+mV@&X=bM|h$#+ z#nTnXV$uD|Q)}~$b?N;GPDZ*lu({DjE+r63A}&p0v0wX#O`ab2KW*~61VDO6=7?>6 z-D~OoYb0ZxC$e!{P+KL5KbV1#@#)f?&mlqf-{NlLsiFa7TJ4IFBY$enj(W4w> zK3Tv=;o-4J=_R~?n^!=8mD`}@ZMqQ&F`IswS)#daU3p$*j^?3FVz7u`2I82Xy(|TK zGm#YNakg*Pz6AG^Rd?x&e>jo>cTtUpkI+C@?j9{It)fRdkQJcD6#jb34WB-f2si;Zn^)P4;M9?rT_P&5Ck9i@FFAW) zKPg1Se_r$*5VolI<^*cW_ftIVWdHDD=|cYRFZSEUblqiGIw+2C;wnHV7cc&gNJc_h zK%<*s#x_{C!j}5`+9k9P!jLPLNvGtWH@yag&(^#R?tmJwOiumIKb(SxH~-I9^&7B? zgNCU#F(S5ssWk>D*cSN}fUg?5rN8U_WTMBTo)014&VEWoIk|&hY&C;Q06RtIVefQ zrdG(TvDBw`IDiDxZvqLJ($9`{0S)-udoD(=rVA0&FcAk=+vD9P`9J~&yH#99bkB}* zb08k!VvtMl(RJmpzRLko8an}9q!Y{jO{?&Dz~V%u7gp!5&=|d*R9$48=UCV2Xm5lB zvS?|F$#-zv7W%bk{`_$J`OVso102?ur4IhjN}!-jJ~s_u-O6^r9ClRgwEM|A^m*xQw#LN4U6M z`INwBpDtioH?Z-nY<6-am|2w-iSZe@PRT)#^>50JI-gqCZ`Y2bVQT?FTZ3x4_Zp%n z{>@@v_{zvuAbqQy*hQKs6DElil>O_ zmtD_q{4u)OXLH_XsCjzI;KHIK$$Amwc_F&Ip}w(pRuWtcqZmMF;rL6hJQCl*nL0GioVVQ?+|70SXBLsK`h zcn&bEN&=4EBv!Qvl_Ix%>tb5F$|?LmuVL>9)WNSdSLeleh>H^HhP$_ETo>@#0=e5a z^&5%3?h@wH+04v|8&x|ji8Z6`iS7}D{sY3^%(IQK=SG*#lN~lrLP5ZIMDKqP`kvl7 zMW9|1!B$11xNun%I|9~2hRisdc&D_IX|p^zEyL3c^UoJv9;)JX*1gBKFC8_Ykvb;- z=w=#>ui=Pp8C8FkT6?Q%0eep+usuV{*b2SMdMs+W= z3hxo{>GR0EMX(csb4x;Y%5fvDssCb?4_103*|lF>(%hr&PQMo~hrH=IBZv%B%Sq}g zgby&@D{9|Z@;34`u`8Z^f9;m2hZ6Ox9|2w}C+h^o=(rX4mexqHsXT7KQZ$yx_eCQ z3@LdueVxU)V|;DD7&~qvog&qwSZ?OnlWHWh&!k6{URzTV3%-59MfsTFIk6Lf$~CJz z2*G$uC8Lu@v!|<}1IJBB>Q1I-YmiqRWYCdZ^)xE%v+~$&|F^1G7Qfd1*N0D1ucg|4 zyNKBadHVAVqUA=qa!wY3?D#K<6xzFi_*80fmo>DT_zJ#mO|}fIis|Zi>GrD^;gUJZ z8EWq2k$*7i+K)(kYk<^Fv+T>Z%A)+Osb^r=i|z;ImgA#{kHLV|F*{%1s&bxTh9dS@ zH`DWN>@2W>kqM41o|K+v+vqA=y@GUmPH|#)F|_NQ?t9?$)6JD$Y>_V+?lqdNR^(Jp zw7bK+1~j%vka5OHE;EW6z@_Za1%woS^I-vvXSHr4E|aIH9^1KFIWfUE1kzv?_nW>n zjZt^OG{%cC7GzU9cIsbzV8m0DL_i)^wn+uYDj!&Aa_O35Ld{!8cv; zi6y~Y%#v~Uh?9QMi2hD2VhyoP&PF=L*qS_gbSr-$)5(SoskM*mqwc)crEGUH=WeD% z%SP3mxh%uK{w?yG=?&bU_Ar0u&DYWDkJY9Bl6qx) z^7U8#<0yN*e1vMO=Uj-$*{=vh)D8Tm*~^>Xts^zniecBe-z*ctD{ndybPY`GrN}_0 z3ugDnJ&9~gW>tXw&h>61J&$U3_efe>8`gvVI0D9wiYqEJp3-C+Z?2Eu)AQ{snsP|X zkO@~lbQu_gnC?+6D zv~~x$?WFcGq>q=V)?A6AB@uyo3TYZ?@w4%UZ6V8nNPLsc8Fzw$=U}lgr{TlaYB6wRJRnF9iI?9PfSED-r5l@bHj9&r=v530Y%n}qneKXb{ z!F04A6`$$BDAq?Kko=YNZ=`p=JWqr1(snK6i0>la(Rge%DyV=Q9wn3U)Dm?b{0UHN zn>nJ|WX~3Pan)sOz~+3(VMIPcjJ!3(v;m#UkipJ_v1Jt?tM8lSlhf`zCM3j+J%Vh_ zkm&Xw!$_BAVF;Gu4#s3o9Ze9nt^%p{*g6q=cC90_aWCg-gtr04wpc6-!8NdL$i(va zT}u?0xx*dnIGgzc2CPC@lL^him^%_Mogsl|OK?po=iRRKC?@j38K9#v^iv92iHt_j zy2mvxld7exB)ss>YZ1IDti18*jSMp&&qp!!cN1~xYHyPTBQu&>s%(%Fr?|#FZ||ER z`1F|0Fjqapsg>Dn&hL>Dx2SXwAC3QPH7B0*QkS58=uy&+V+9`H-jbe7WuOx9D=S>^#!)E8e&(gpV&kORwdZK#q+ zp3LK-8RF{-xb-gejI{!-cjqZb{n$0g;sT%wL_MpYZ8)=)KW=PxwLfpeb|wiVV9W$` z#Ts#X&##+SOt=~J>b`wvvlpjG4amMXe&%D6=N>g(H~pI~PWze=TbrUAvTob1q0^fT z&#fQ?NO$|Q&vxfChWUafB}tq`>d3vyYdYETDa;N&M){x_aUt{OaJJ+j5ZOjoTAatb z>`~nwd%>&dsgL*M)}v{iAH<*q6()j z{dZ0Ep60~#gdH$8_&12?x9WMvG1Q&_3{>HlKo377emIQ}!bgg@Pv1kX@_`zlSG9s5 z_bylizdiyvTCC%HDh_pk!RMYLIxH+L+YHC%ZapBhHPRGFYrWW@g$RX8g7N_ zj*V^Ro!e|<4cD1acfU>7seBw^6ZFvL2h{8^&Cdh+hNvc;yg&o5<;p`;o4mF& z4+u4?tVWVt4o;M`Bg580S*z8{KF`_gi)X2;P4w)mZuhot=JcimjY+(Y67zKCAXFuz zO-AXZInJ|mvC0yihJb@j$V0=X6X~Gn{c%8fe$SaaCHtw=>bYfOf2Gx_FXPj*u)fr$ z+#cy(#9+EJy$gTIzP%~hNm`j@lv0M28`H{BMWiR*saD$8|Y4(Ja zn#+ZsIiM*F(^{;8eEvAG;tYe9A$th7C43$<$FM=|peK0R(Chl8YV!q(YL~odLhGKY ztAlm_y=WSoYm5o=)vDmj?{?aCVWOXCgS|;PbU8Q1CO8j8`j2h)&4zRJb*c{Ir)`?& zaZ3i`o1SsM8o&U}nm3&O?wiv$hijB6ZFHDyx0n4O2kSYF=YG}ovqY>auO}ZEshgVF^L>W! zc~K9~x0TfOD*}skYS(ff75|9w@H{^}PdeIB8M)u2N&P023;OgSW-XD;DpbbnKuheT zx37<8A-y(>H?ddZ5aoH?RGQfYya0D1$H4JA_x9}n#$)#gf^y7QNNA)$a`YxwJv(Vk zxf^to)MJPT=RY!!OJ3jTJ|bg`QMDDymhu-r!0Nvsi`O<2!1?jmyln@d$I}7^TRKqU zQ1y8^5P5psxS1B~9bB%shQb;I)GG~@TKkOqf%@@_XH)FoNS zBdX`f4Ce*D=emd-yHuo$1&YLtM;oF_o*1nVxKrJqJ~CoH$`<=1nc)dz0>qJ`HEcb> zSj)J?j@*dnY3hNTPf)VGP2BQ;{B%Tf(+9T}UoJ6pY;q}39ovh92_}Oc%K+%HP^5(> zR%So&4Hv*V*9$sHl zD-s<7#8!(OM<%<2g_FHlppO?sSv4q;6{eESZt~`MvcV}<4+6~mWtQqKryN!$8%o+4 z$}rs5mT05cVkIM(}2MH!m4YSETVLs!Pr_Eor+?F$d zkSj!j;#XPn>%*rf_iluXCwu*K{{j!#y~2By`KPAtzh<~R7c$=g@ z&Wt**(#4a+hC?ns&MV(W8Iq>{U^A4WWAyk8*ZDq+%cKxbn_8*$E!1rX?jXOlzgjGA z_hZSXmMNvDTVb9@L4ZT$D!hTO( zvCi1k(InBs?w!2pdWf1r0Y7H4a@JFp!;ouHVD#>7k1s-=ZAUXeF-+(+os#rwYbbB5 z1gR+D4E|Z~7&^rGlw&*UREZ&2i}BNZzb~}^_`W>Kuw{;H`DN^Q6L7ido+kyaB{tp? z4O`9^>?xdGfs;phGgVRo8+>y@Nqgau*Rmh!Co?}zogXhF{9wAPc5*noyQeNz^Z&8- z)=^cqYuoQ*qex4afPl1ggR~+bNXG;P>5^^`rBga52uOD~NK1Faq?z=ja{~Lq^*yfj zuC?~~#=FPVbwXWbV;Ik(W$N0;AJe{Hx{N*SRddohYJO`0D>Y;R)9vA> zQ_JFpc@e*25Xw1f#R5R$qv_2znuqI$%N`x5-9h(jL`2dM2Q!~|a-5ociqW=!aYqqv z`Jmnk0LGlmZBmK)0mUt%T`u=?cw17D}%=PHbF7OCBG<-J)rB0sUII@+kd2;D_ z3ei1d?hxk4_MWLb@yf6`*R-$iTo`UFKTAe}@G41)7~hK4iWu0sRJ36gJJm)SwnpN|W~dXO`!%%tdN-SBgw4yezZ zym><|kc0~ptSwrtW9K9x{bIF*ExnZQJ0kQk2;A$N;A24BSsDLzx)LFO&_6u{6)z)g2IH3IXP5dIUuF2M5D(Qo#hvluyU|t#-DuIULuX(?yo?+0F^geoWb*4S4h7 za^>g3B70$Q&7_fyZY9On3vARp<%MU$z@t(g5;IjNiC#2~;qZ^+?f`y#u*$*~(z!WZ z(Y7aU$#tUy5=eLZ{G@_NMB{7uV?bF#gb&DUtL`tq_Fl~_9QVIPVLLW&(wkMqe71Xj z*>XaQdWz4ik&(aU6k+!a%3qEeQFM7`RCe3{b0Mg6yu4|{^VV!`3bl#0<$%ulLBbMM z4iH6&hv6RFj~Gzz*p)%v(QQK4Os6w$o+vZ_L^o?b4aNzlRh;~Z>LW(&Ndl8i(H;Dh z)t3btZ9a5GQG^Xwl{f07YL}A68S&~Js!+%quyWx;)@BNi84f=vbX0DE@|85>uSK@J zt6Zh^-m;Q>!=!G&iE2}qd%D!NFlIJhhY>d3N~}4hB^`t2cg>W~^!JuEsgpd! z!rGdY+bUNNo8!z9xJ*rvUur(?nPm#!x0tDVjZnE#LthZa%PV0j#Vm737GZ0b7RVNN zBKg^v<;KD$wa-m>RN~$ex)>TsF?3Ut<*;sjIV+piA@k!NYK-(Wz#AquICV;1*=yV& zdUQVb!WTH=Fi5XfjjvgJI|+$6^WK1oAXJan-xXuD%Rri7OeSbj^yFhMfsWILD7-uu zUm<6}l6=f^K-Bfl{ttnkRau50VIFnHu2h8%mS4G}Y_IV7ZswW!?7#}IvJ!EnDpQD1 z@tJTsExNUsPyrXX2KS>Jx!iGyxwf{-04cx6n*bcZs(YGHM9P2k!YdJ^#~^lFdhc!l zGKcp#e3~EDgbrSqPry&DBLGd!Ga>utZmH$Tgf`Ah*w8Al;OF=~747AB-~GnvX}p&w1BGeR?9ODUYA;=oXWJwujFNKa4UBbbt!kO*1ULroNF zi9p|5e1*Moij0b|y&0%Zi3=#su~T@C>J;S{E4=9r`8XqZaH6i)E0DAeoCzq#Q&)%K zyTEtA3>4F&P{|qeyPlLe6jD_O$9JbZFv_Wsyadd4li7;E)4cuaSMloUpzgynnb`Mf zW&fUBvLJDUqtPxhU&Fx~yVkmWVHt>m^9rPb`ahi_8!zk=hv9HAsB%P_c=yfTMe@Kr z3D2hqW4A!Op42>bXmWFd`7{gjTiD7{=9>hcQ=4aYv%8!x49A{iwp!N6M-(y~iR*`~ zkh_PMb*=RzL&o#RM~np%y@ErJ#&ryXyo`cOHX{9_3C(ytr}HL5Xk)EO5c*?z>f$AJ z?Crow$iYaTYW+6Y>t1^OD;a3SVwIxgVx!!RSLfgUlc%9}X^-dSYYguTMtz z-r+wRXM)D@d3ssp5WO#M76e0hPY5a8y_PXixs?B+xg)CKwcgDrl49f*lE#iiL;hNKw+QOzk7X!Jv z_o)fF-bHOn-L(`F^(t-~*{#}HNlIyU7l^bimu?1J-5Eo_%@W2z>%iXd_TrM!9rQ{3r*}#>M$#P*5OeI$9f~ z3Ls_R+N;dV4`8;QD{{|9ex0E2ORfN34@a9{#*0=ulp~)jh{mjZYIhOBXI4QFN-y`j zJKT11eqQjDGmqoG+kH8U+e;Ww%IB}L%6^<@KD)ObF1KEgUW=zj z6kU+6Y2uk)OD!%g@-i~g;$C|93DmH_Cf4S7X6jr0DX}mt+iW&+cCI%^e1I(c=a^r5fr!E^OoCVTD?4$d57681r`7GiPGYR=`@eV zCTl4z3t?5i@1>|sMVv+BhR0VuAI?V}MzX^*RdA;3m>((M2kgynNoi@QVayNQS@Rv-P-YV7i+I}okC1!JEQLX)ni@Ddivdi z&6f)VvQC)HIt3iX?Tb){@K-ovf_G)1`i|TBZ=07ocuBIeg|ynX)XabWRP8gVx||nr zLz6N@GRHdXp<&hBLw7}fzEvO03|2J0N}-B+9m3dn?ba1`t^L92$&Sq$VVB?R&j%Q; z<(u55Y$jtCAN>rRDlK=GwkvTu^>)wDh=tmDz;CXnSrgYbg(haN*6zANP}D#BOdx^l zguQiDAe3~vl47hp>!)^r)cuecbv&|@HP~zAPRG%WBM#SAg1A-tv^%~;=qHXzTxK(r z^@63h0)w~&(UW%B>0bgaa|J4+DGy#e7N@K`0NLLROXyd4?#rvX^A8{6 z9ncY@E}vye*qtwH~+cAF}SCUWFL2AoK9&e||ZG@Q+P1^ROlpS}Vl8{P+-RhtWfyue+L(xxKGq zpyFbB*%$>H?AWix)?|hNvef(d*$C-@;HC8>#d~eI&S#-lL>fI^mwEiQ&~uh6348U- zUk^soG-%>Na==vm2|5Y^bH^`o`y|s~Y3_%XUO#FECZnE)sLpa)4r-H4?tPvZTz`H_ zu@0*jUO$HNAC4!VyhfgBOjVd7)Vz&pvs6+AxJbAS2yn3%b!z1^v^e!7l9{{8OZA&x zgn9}-J7^K`1zj&f;aH!?$J}IFTT{N}L8|Cu+2cJ%eKzU^X4&!>t3I9yUX=oN3Km`u zp$_InxFH9}4C!4;VTRwBG!3dX#(URaeJGpa)9cIYqzS7m-+tR6Cm$Fc^1w{HbGq1# zA=kgB^i{=}s_e%a*Xnl}$*C z-e!?s9H%+;@#_+W1IFjOQTh;v?|f3e?mrhk`FYz;SLCshsF0o>^H(3xPmkwn2J+c| zLXl8*ad+rPT%KLhqxw=Tye5F4@zW2~~MHD^`eR9dGi2i01)jkgz!I+Fu+S6>~| zTPRnc+KZm1sRu4&=ED_%c}vioNt*)Zq$u3QmW3>{!k)f3&bAd7n^7OVWBTp!zCq!} zm?SPc*Q~2Eh%MAv_OWH5CRZU_qUO3NoP<{>-B@HvY_HJ;K^BEY_L-N5M4Bnm_-ekv zVf)I5%N!<2IFyOX*=V*jfW<=+zlXlMJ>B5kf0Ns|;qX_vZ01*1#nJ7SUZsc$W4frW zo{`}4crCUE>y<3X%%1oDFf_zVC|%RKhnJ4eu<)t9LqCDrhd|dxzs2h3&jp|7E;F$U zH2DFvU;wPI(p_-hakU3MHMG=l1S6e>$_JU(@CBpq$4?K5|b7OmYtt!lPnT3hY zq|LJ=sIW(I9Xy`P{AK6WB%xNh9bRK6Ku2ClKe=w6%UFTb|Sm2#G?n| zFuF98Ws=y2ZC%}8(JQ?W1`zqc{grM#w0WG|f{ZM&#Ki&TRoiE(u znR?zUY7UV24W|SmJb9^)yUQekSbS2;M}%Z4>@4DH<<8J5opHikF6drB0H~)A=HdC+ zZcO-WjAgHzosMYc(Wo}}%%|JX0VdAS@qSdUQoZDzIA)DQn_+s3dUjAkstESGfCw9i z{}^?k2VEwtV!b;8dgzhb2U1zvO#@P4<4A!ag`*1*1 zNHt=(Huv>s)AHafWCEk@u@@H0{a9A>FSHpn3BPKXk1jeUMKQ?y#9HPtuUtyMHQQqU z5+?*2Cl_v9@S~%f0iX@)arod{Ws3X3rFxMysqe`=mSmDkagJ*+F9ZF@fdgTdA3nn$j1jzZ!iY<9u3GeXf7uz3NNko!h;e z*Pf=UT|H6t0aDDznnKUExVww2^zP#{;rszo)?dXQwH}&Tqp9t$FC%tW&l9mZBEYez zyWOl0D^9ibd_!zW@3e_kPCv0TrR)Z9B~ph+geocQg-PaPsZdr!6J(vnIjL3QC;1cl z%(T>jvrUO64yz#!GO}WB*W=h7#vnre$y*ECSi95F{UReP9_RBJJe5lf=vy~wHUgHL;Jqoe?XMmtjr=yG>$Xf$jRk4zBwml zuE9-JA@WBA^XFmL?MCOjA!AGIU`(yHdKH;ZuUMR;=w#aCp;sE+lvbKcC`$UzU4ePC zD{@gLx~w-Vpv!ZXMT|RR<$|L*>b)-jD4L#0gM{&5%Zf*}@0!qV#%J1kK#+;}J~MXB z^O0iHO@Yj@5U6@Wmoua+@8pA^6y$CXnb#y$Y3YCzkJeO!US*y(X-Vw6bZNR_G~#j& zSS7PIM?$Lq^;UDKVSkoF#8^q!#?eHdc2xEiX~-|GEZtDOOA>shL7a#X6@9SX00 zX{eXGWKP`DSQAdXcRbYFypd38(dl>%3@J}}G%FZ0pec4K2Ex~5O2uCcZ(9m+ZE6KNDzw6@GMo5aq$%ibT6EmgWt zX8qs5abm7g@9SZ_slBZZOZh$)qHLxBD$4m95>w@-g%qt!It7v4z66H&@!X>C-g@_q z7tVd}Qr_$m+i$^kfv9-iOIl$V31?ud4yp&oymsfrxy0m3uZ|YYc55m){_lS{TC3aL z)aNiR5&PWRu{Kpr>UONoJp5=FL?S~~dy30El$vXd7?eV%w-PED&g9H;OB@iD_h}2@ z`5$RyN%XPHd}H8^Ot#0Z)5W??#oXiYX74w~;itEnJynbF7&O!}Aa2iIefw;^eA*i2 zKWU>DpTSTz}6|n)sIBS_c%Y>y;h~wa^Ce)v@Z+G2R*Vy zVzp0&Wr`Y(_Dx?v$^+Nb+%lWf*ai4B3z;mv`Zo9$g-%1M#Juqh-R~Fql$&y6Ur)C* z%`VKl4f)Ap>)DnFPi48X7P^Q$e!Hj!u#O4sQyIOPNSvQqWIo? zusvQF;Z(Uk#U5Ou>sQW~d;5g7Dw}Rq*nBLp!e1vICYHW%>AM{-q*sua#Ln+0)R&~3 z(H)s3)C4fC1`&gHx zlaKMqL9!+rb>1%We!|_EIfFb}QdjEh%hS3CStEJzscHhvHJ4+&8&Y05=0s_$a`3@? zlfJOOGIw)u>cO?p#w_h;Alz-&kf=N0+@zC^p_l87XvEJE%DZP-G7wiTyR|LIxL^l~ zNrDN7I5gkd9|3K%?#EL3TsNmZ`M;j_} zx5!~0APOM^rx${!LIk(6f17R?ho12fs+Kr(h{kxH3%4k6y7KnhaZhH{n zhE{6Lz}dLYTg8_4kkQ@{VWC45JM^5FCp_-&=Tw$lj}St&ZTLC$pa3}wv)xogTGT=@rjuw`1lnc&d`W^C{YZ zmLLz3$$EJ_EwL`651?LPazKVoO|hpjEe=W?!Hoeo}Q zn#l3ny~Dy1g|9HE0BI+VS?EuG3lI9b`JJ=&;yblsQuCiAJJ1AhO-iTX0fJ<1sL5*kumS z?Ck`J(8}&Me$}pYPn#Ky#V4Nd9!&Fj7uI}lJEx8r7%y>@j5|S`ov(q|&>7}heHQ=# z3^2eCM<;!tNOTZ|r!-|6wRz@-TOEDL{?(|0rwQYfbd`cz2t?Y6Z8T0b3xWCz_oKa! zLmsUWP2H-24;KB~on+C7-XQ0{BlF%g<(8q`scjKpW~ba%&FOPzWASYv}>qrzs>pfvw}(@drtR} ze@+U%R04Bf-hJgJDxCEl6(#38wJjqJdJgJ6hyfyQ>#dBOEj$)=xh2}Wom;0?RDSjJ zn~TGw(_@30mefkCWNkIimGA?nI2(X##o*Yk&HGcShV(Ly7fM^A_$MsuC^o&)=4kbHP^#6 z)+JI#hn}SJz=wlQt1{#YT}pJ%8wOr~$^y9e!$T64l+qUeafa=OjVE~;>6C!w7$@W? zj7u$ttPlv=q-8S#Cr^aoPH^_)#=fr=WhqvI-+vNf5e#LHI_J-Q=hKV zV6C*_iy9u>t=riIlDz|0q^kAJp+5Ythq_MnK`cG`+1Pu5S~2FXC>W#bNL!C3t)Im7 zecV(v*3@n;XDIXMr0SL1_sp&H?BhrFJr13?d^^U9P5XwLAu%7Pt4VDXLu^BQsrCL^ zcvXfNfHv?Se%%rQ)$yj!pBI2IgG;^Rc0YBXp<>sQPJP&{FLReI4NTDu$X}S3@(uH* z``2KJlLo^+xQ9Enh)?f)z{-C2^E(zBq-+iTghzKkm@{Op>8tNZhX*F9z-c|>{DZ?I z)0jB3$mgM`EtIya_WE7Q>~+srv|d1jUfEPXs(ByQotG3D1#w*Ba)+$>=ES!3{mlX( z5ACwJkG3VI-_3J(!nqM_cEVi#>2mWMga>O*OE){4SDy+COke`Ie|M9=0DPASy%VKx zfFsdKda^c}Y>$A^hBgU@-+z>`(GkY@gdA8hQ5=hs02w|Jm)Yn14t;&%=!xR(aIyKW z6RJ7Y_2Ep;nnmRZD#?)aQ9!|C18sWLn-EH39!cc{s{T(jk$>6ap8Cp_ZycpRobzpZ z1CPJE8jBqHgRrK5t&)K1Wogs%-btdOr_I*7R49~JxBqwBb<`yelD z*_r6)$+foIHngcHDr)gb8G=c_*j{-GiXc5DuJyWZxbMmdaz3Aj4&l50I4YT0u|=~ftwn1K#?nq}+P6?_$b z#3Y@DpLhOTM1QvG71Qs$#1TYX|8i|z<>$b8!5REtk`f0|UC6H)5Z0`7NWgm$pBnSj z6!D6`S1C#f)2r8n_r%aCez}ivINmELy_qWSZdt3$BMv$Q(dL>Refc4fT)WVJY)8;~ zPUq9%wdbzje;F9P9ZZ@zsaI~@GtypInu3p#OZ%aADVwd)9uhR`S7`}(! zM4%90nGQhBln0kSQipSo2kbwPTZ0SASwQ~7FG@H-q~P%9F3D%VbJ|0nEyfhV^V+*3 zYRFzS?gkNEGfN7+mPWt1oWQiopUcV6hhMkNShuO^W*iC&bk(d_U)x{5hX!Yo)%QuP7 zkCNGdqErWmPBIWLcw;o5y3%_=66CWn#cI{oZTp9!09EkK)mI z>BJWLQRP9VM5l3RWP63Yr^}jx-9n!r_P`2H|>6mgr|V*<|D>zF6GVPM!%{ zdQha7&IDH>X!bJk<`Wu83J8@PJwY3YHCIgKW`H`luw;wV-KyH%z*qe*gl zZZfjV09YxxyRb_ym4K)k>0kXv_Fy2hK}bDPYo~jo-KmtPs!w&a4P#Q=7T$-j-I=kB zr0SrJWPo2gzGB-AOfFf`DgVII0jkZyHBJ%-Iis6sB55Xi^+L){j*C*wOJ^ne6dBB9 z07Os@W?2Rrk-3xk^{zLdbbHKATmNUY)Ae z!%JSv9gg+A>9v>79LuQ>*qDO0sj<_s154p*RZh~E% z!kNrqU(0y875E#E!B9A9AO+ixS1<3C^59|7rRCgv_pWBSP`nXodAf({k!?a~Dd-$w z0dt`rmMEc3=jdE)%n6ko+37xf!f#-EruDP`0ZDV|w{_r6&Ea-%Z!+}!baT%}<6zb< z?xwP%%(B~%6K<6hij5Q6t8RpA>xB+&V91@!wds&1d|uCksKrPgUeCpN{$Yk-rOdF=J}viCER)7rv(9hizQ| zB=z|^dYO5RYiCHS(Cknxil1S46ZpY%Q)T^`=HHdLt)RTt!-(ILjMeqsc*p0>$mRX$L?iVp9Ra;E?s?GOk~KRSo++9tB-ki{LZis5@(*MdG(Iv1#W3yayW zkNE;U_w!ualw6oEAe1$hYL%M!=T{xK`bUhEIcZLEoASm+Jknv&Fxc`V9nc9XbTwAi z4>%L#Dea`TKUsB1Ou7$w4@-4gk`LKZ61IW6$#>abdkO0c8Jb65bK61<4$pNc6orhna*3XXAmj1Q}NgHr#6v(9nv z%OiK~uII3N)3gPS?HC<7g*2LceJI1AkPa2nBJR%S<44Q=48zEjWT}5-*i({FX)lKc z2qbiafWEcgXfMvvO8tT(h(|!yOJxPpm2o{gw z&NzqpIZ3TSg}w?)F(_-agY)HjuLF{!SQtlvEaNY74++6F3&BmauUMc<5YVS#I)#6j zP!_H;DYt+Q$aK7RK=$- z3NDD3RCCqz^R2bt)?=hBvd`1e1ZTjnDj|oh2{EMDkovr7U?Hm@j|zePSfFV7k&XZ3 ze)zcM%#SW*Z6{U?&c70FYrhz1Fn^eIr9T|kb=!-~)4E7rn07dNwxFBnUg^&O!(>$^<#4x@g5e(j@TQ=<>k zjP{m(SsrsNflw-XV}2<|w%D$-ckf<8^W&T6bdsNTt@~`*hVCoBEPLJ>{J|nL@xR8a4Zl{&$b* zr|Ls-eX!(DNEqrCHfmGZ;8>nZ&&7GZF3wAfZ+s&92t#876GLTImya}W6>kl+`7%G! zXLnZ>blbSQ&iKZ%U2loidfp*flgs`R({>MG#4^cR zhVVnU>6TUe9aL>qW}S~b-j9G(V2)5WX%W-DAGdFy1eKzc47eGxn9QXl>}d<;iNf_d zJTd@bth0dM-iV6SalK=~iG)k{aXzGW@c9^+*5Oap{<`XuR%ij4Rs;jVk2U3X)lA(+ ze>u+eG~Yn=2)IH>BS_`O)Qoz+n3|#>Tx)?tfnITG={_#W6m~;~YQC1ZE6aLQv7#Nj zdTZZ9CyLLHv?@6qsq14H=|&5bXLnx~Y(U!-6xK+_Y!++;Ty_LTacd~iYcd_w=bJh< z8<@e2!256*P$oj_6O_P@rIo0opDB7b0d7fe*1~1I39&-^p0|UDWy{=R%2Nk8q(oiq zF5g)VBEzu%I)k8j6R}X-Rwr0jreifv-#g{pw0oEZ#^3~mjkyjnTz5}FDYd;JmjYCmG_CT8k+24{B&m_>`wD@T_*> zf$63git6qLE8apj=0IbNaogOlpL;_Ic{wM~VC8y()UW1u{jyP^>aQSDC{(_o1Po+Z zI{u@l6#koh_D1FcNX^g|4FBm01U3Us=9~T^XF*H;;TnBKB4G-@W7kq06*O`hm6?Z# zpY&_Zu-7=Zp#?Bt>y)(jwP^!4{$f!%RAdCSciHVW zFpMO2Z@26eHtq~t?GlL2+aX&U6`mPG_eS-o=lW4O6F&Ez}mqezK;eKVJ& zR`{G`AS19$#M7I4HRHAYHP@1_p0AuBnq?~=7*U;mXxT{%Tw2!?Us!2m*?d6E3D!EK zI?UP4unehPpn&7VP6GLtpxqRSZdlT&n4?8;S5)CFr8nw^?8kWg$67Uxyh_!%w~t2> zdG@ALWF~O-Cae^VE{ z*!Gqq*SQk-DBxwUt+HK~o;C@%yM8N<3TK-a76tfB8RxClgXQ-}i)2^=`Pwz3dyuw1 z@pe}xnOGj>0?DyL)CTyQCoIxTnq2lZkSIQwgq2&+lU&dI6O;6SmFI<3s!_i7$Jlc@ z`Z%LKPkiC-+;&ey;y@h@{p^?sO?&n9WOR6#V02lJ{;cuj@qvX+=x|7~7K<)h(Dmyr zLKQFUw$RsDM~I7Z;iQ3>ujpzROKdv%FX=Z_r;$=5re6{LGwJSuaz4%mANbu@=M6(W zr~q<2wC2QCz|tPLR9$^OS*}6~ut_*tt_qW^c?zBb6?vmD=AWeD&jXC{Hj-QV-&%bA zlz$2KuUAGqv*DH*`2#rq{4$aTGs?T~afpMz`A89S`kMsF09A*8lkz!IKC>4qP!IJU z6MS2Y(wwBZh{{qPo_BUTx{xP1W@q(AzV`XgBR+k$1%>CSa+ksdt61~%lg-zRMk+uf z109H)j%3gb0A37|{;kZ_b8Zf?z`I4)2=5fSTta>D+Oe7N z_|?mfHh#x36%s*bnW_EL@qBeWX&H-+f$+2Ad5Vqau!G#E7yU=V=NBt;LvJ_`%ef?i z>fRGz#CkI+Tz%;ssDO4w(Bezr*9Im((T-I0$z!#Lt?YR#ubCZ<^`wsUw_1hMdc1Au zhgImsEEH>64S4ry*)O#-EsR!2rLer;V?lg`ss&x_E|B}rm0yFp-(;P93bL7Q?$c-# zmTJECcr1cGiebQ4S9Q-!ojq{Y&xVL=AH}YghlFVlkr7``Ohh)}G;W9&>VlmbKfo&hZ-(chLMM&YfMM>e z?yn?4)Rjxd$}*SPanRSY$lMI(0L|Q5V}MBF*A_A)#Y?eK;_IYsZaFwh2>oHir`L6t zEkt_hpsiTIX+N?6#nULU%4+TFiJ}6>p8SS!l?x*2iHl??9>iHeIo0Itw3OE@(3P$M z_Tk~_px%NfpF$Q03_5rYFEK_6+#G7@GxbBrKQJYZ+1v~4Ji8UIdZ!`kdCG+3GxaQT zz5+f+hyXBH*n#LW#5?Y_E_%Uj&xUX*!b6zJ=7nFh4i&VM6WaBJt4g*95Goc7l@~l6 z3+2^Y70?~bX`mUUp9jCz0h?dA% z91xeU2U|T54GH4bRt|Ifq0^@076Yh*NvakN2Xbp|6z5hvq=W+b9Vn(r_eL5~`**Ti z>8Hj0NwhcX*8>YJ@!C29aNqPXxZKxi&@G=3HBH^}Y1_hOs5__fgUl_|&uyPSelxiV zMxFH&uiLn|5rXNP4#@+h_#VTxmTeg$or?;Ld(aIAPr=7p)z-4;+~Cw~yIj1~<<;R; z4Z`{R+W)(%cC~r;^j=Bre}oACr9%RmA;#+^xIV8&Ld*-3AXyrt0rtw0lixn1$!I&^ zv&731vj5=TsDHhdDi2iB84KfzhKcqHxdzG`Q|a}u5#MhjgP9CF_60xFis1(JWq$o3 z0^Q;t5H$dUKqJF8Qq`x|3mxJ0Mwy!a`9;@WV3ocoW~~4^odAP_qFE2p=P{`SNIdPG zjqLdx;Ca3 zOWBB!j0*MZHc1^5q~aZqSVEpU&ZK{83*THpfz+^wgpZ&Mw<&CFMX=Lc&9zT-HCFU( zljo7JL;%pIKu!<7SaUn5udh<&JcPE`FuUdep;d1-@o30wrY7OpKT6L$_Gf6{^pak< zKg0cn(7M)5B*0dP2Fg;HEE@Q+x6f;;gU@$C^64>;H1>PT0?_Ml=BxmoRw#wl1>Swy zb_Cu*AaqnPawNmhU(Gvfy>`Kwc?hjv-;^zN12jTf13V57#yUsMJB{K8rqfaGr+Zm> z@$4=E`OtER(k}pez%{s~_oqqxFAsAx-@Q5t`b7VSc;ffTYV2rh&ncGm$H8Rx-c3>^ z@ZvkhUSG}Cr7r=@7xyh*%z3%7g(I)>ihc2tpew$5l!PNh`-O)OG)_uiMZu`jg0wYc zRL`h8nl$5h%eWf&+t9r2>-q^KMXoAM^tTP*s&^g)qfj$m?sv0|lt|!6$5B?Bm=Gf{ z`I-B5iY8QodMQnhBe%^S7*C@!c@uM+r}Nt26D2t-bGj@TggG3UL@9@1Y4-1l6$h3> z2m}o%@3H7m=h`ElgbaGYjd4$TU7MPzTixjT2XjIM-#ct9OXW}7hQq?aY~iZVqzh*t z)N+;$@u++JyGQcQyHE9woz_l{{3-0JyZj4r%_8wT{)ydlIC2|~wAKyLORfGb5)~f~ zqPICfLs{=}&zCqgvESuYqwVB_EldkoU7>%yz2=?39b)brsq{Cvl`4aEXMxXOZNa+q z$l0B<>MF$uC!dIOrm$nM=E0lIL4DF<>c=hMy&aiAeA8yb;B)QHhSw>q148+wl3%i~7>>)K!R3X7;qUXz<#*9z~C6TM4=p3DLj@vXGR=_!}`aA9y!4d|;AL80LN26KC^8b1NwR-Bv-oz?n;*j93(6 z*5PxF{U$?qEnqirnkqfiUo!S&S9l(1{uau|;Gij&v?y48$1Dh>N!w#Zr8u_^4%VoP zH}}dn$LU@#wpw$qSHku!9MoZP#~GL#LLSG7pwobr{whvuC(De4hvXsX_Mx2iLHUQi zvbZRYNpvo(O_?hi*c?sS)zI7Dkv&xr1yvxsAEY;eki!JW#_kT87_MNcT3pSypD(K1 zFYjM6oxcyOfj%%aB^JECKzIy%eSiU)3=WVa!;gKHME#(i*4nqoy3wfIrrYLvAf{HWmfuJV$hrCux+Q&h-kKE)+QhLdS79o=J0*& zi#@B~(iqR*b#-_i2=S}wT0VuCYab|6CMU2xCb&xp1xHy}2*Crt+jkvv^ZqKHjkL?9;^ffYp2;<&rPs{(LBH=$ z0RDC?&139;zhyv>m&)#nwr{-p3JmVp{aKO?((|%lN+fe3io(=rWbL7~34p{) z+_`h^Wf~89()dCg_iq&fpvV>{8^>RsD8E1HEAXyEV*1s=!nINKx%Kts51jWY%igFl z&p{D$q`%Qubvj1;eEt&v@4dX;pCp`F?}9PlFh`mJs-8pLjXLkn4~^;EYHUU4wZ33e z?^|qUqmXFC)}9}Y5b`gr;NzHZ$^T(L&*$ESaikphCwrt6%%005Pxsa~MmO8b?y^9ojE6H=_dmP_txG%; zqdT`hfUjjUF_}V*BK`!hSL>-B-iQhRS^q=$J}-;IXE{|TyOTl0Z*L|YMSm}fMaKuU zyOh0TdO=g4+zzq8a?u6le=wS_$S!)W3^ki>2n1$0%%%0tb}1iU&nYYWiOdVX24t@5 z<+7z8X#_WKEdld)W^DuXITtgLAAbH>WB>gD(-;ucwtkl!0q)g}UQ;0qm=JuIW9mM+NG%nM0?lgn{<3`8;_wvHqmld z`>3;3?h$N;=YBU}JwoBGmy@N2X;0|FYg+=r+wnwsT4V#an7Zf{O3U=Jr2bI=;U{!@ zcC$8~(gQFNFMZGLc7VcuFEtPT-60^e)#iAVGs-3MJmI(B@;Hj+AwUtw{e6Vu{Qt7f ztd#Kigk7!@Y2GGDn~vu>_=eoRjm$P0q_o(64?YK`&~3dAS&rEVfN;tf&HdUkPN#8` zUIIfC@L|7rrt;r>C#s)~on%6;Mo6E)qLBKv=Ne^3kxxZ#FB06kdlmRw1UB}+ecki; zQ#)TQmY92wx+FuDt4rDHh3HYCmU9ham*;5Dj#sygjV6kOI4!i2Q>8+8jBzHwE#L^2 zwjqmW6Up6|^X1@Wo|V7v5CNx=z_Z=OHdJTiX|eAZmL(pGwm<+OyFsf%5ShqNDF6W8 zniUuMAK}IQ|G?h;OOyz{@GGjDKoR{W8DMB)JIVZeIV@!{+FHyNu#*b!l`?2m*j-)K zXMOBktgw3yYK|zvV7s;SV+sPIthf+(rb=cF`Nu0}w0GZM_TSTFcCB|Bix=eS2qwm- zH0=M>w1Gx*vsfcg|9?5zq?A(*SegTE62`X07EESqcvrfjB7h`OMEDwsx;@blSYa{! zp`<&Kj;c2n>m$9`eXuAOZve?wjDtAfcfJhHH{Sh;R73Cz{J`%VToN8~jLz-=RMl!{ zwoWG*MDT8ggh7Q-x0wAl3Ud7B0-D9W`2zo|Q@OGAAA7w(mCHG=3mUb41Y+$v<6%4^ zUhANTs^bOFEZw&K3^u&G9L7Um0A1gNey&&#XRq7Uw90CUW_zMIf*H~vT4^zpI$^aa zzyAcGP?4ve^$Xvdu3@5il36C1D^UCDQrdBMkCt39tl3*8;{-9635Z7tU4X4G2}Y5_ z^)uz5cpWhP5ugx>&?q;N1k}(Bw~OQ9rmu5~$h&);jg}bFvAawyA^m&v4s*j{uq zz_A7}wvnuQ_q)QW+1(;psoY}E@3f4mFShp6wy~lQ1aES$g$nNs@gtsL>Fy~hRlTC< zqCbE5wuntO@zlI4S`Z8?9%AXAKc{L7B%jki89TVxEW9{`xxA*+?^4p0d(>%Dw)wM* zUF3_`NS1=2^X|M5>D9t>((@fmz<$elt(H8gEZp-2Dw7b zp>g)Bu>KXh_XL*_MSX62ixO?s`l&YX{?23Jt5Y1{ir}-qav5}5<44RLVs^GQ$qsIH z{Ka!1(UmVb30E)}EO0$5@#z)zeIWthYv0B@+X7?O;$sW(`*nU@4gYw})E*NxWcbRk z1RgY7KK@+<62LM6+&Q8{NOV5l?uY(IvS3O`C5#iKsF|apDiB~7fU03q%O%Ew?Z?PXYY`<}Im^D)Utas#IBDp;)ntics3-L6aR zXxUav=NWvPGZz;psh}}CG_J$KItjk5|Jyh=1;2=71>#2Z=AC!X6EK8blYzU2D7ovd zdlp~qCd@>saz}Nf>h+xfrn>Vz!Xb)Pfj5=2}YCk^RUW8DMJsjgDpI*thB%|?$;3x1xNGa_3s*`p--^4E)-R&tf4)ZA)x*l$2qqs6- z#ctEkdwVE1;Rxp74ItMjb?t9#9*pGoRR}UgAhiEyHt~NAxq!EE?w$YHHciVJZ~|KD zk;=CoIP`dm$2+sZ`^%l?oB5L;%{qIM1a%xWwLj98_sb=6L<`|2IUT*ntp%jkIPYSB zMjyqH0NosIU(HGj#JlA@qLPlq&sT1ABF%Sm{E~#+=pB!6$h<>|^!u=T0C#OY67V(* zX;Ql2J?bxkJpgIWOu_rz`CCd8i?=Rnc2#CFWFLg~6&7JWfADzCG~NN|VEQRQc?ud( zGg)g@2%F9JX!NufIz)YQ z(WMWV4daQFj)Sqi{_dbS5t`8Hq_VGQH*)zZDsZOri~ikX_Pl2P9Z0(TZv#n?5RBgX z4{<&TP@{pVH91a`kz$Fq)Gq6lF8L6W12jGq|fLS;uh z-OX=iyYrN{hye3b{wwx9M%xgw;4DaO-%atFA7{FRMwUn+P4ejDEQkl`-p>!n!(8Ja zAe~u9o}tpD&~taiu&@M^@U~W1nl6Bnr8j(WeM~pzEI46I9`FW#^g`#ZF}1U{`&pE8 zA}ip%FkhFW4Y@(W5EP(4V+{I?ffpS5h{WLVBh)30GSUD0_lGx@=9Aq(VwUAz-=5<9 zF|@;y+BGmF`IJ`)k}0>HEgcn5G6VEQIi9kfc6}Hg*LUnTVbkqHc45r6?#Ekk1giP+ zC(6-`aW@k}hb~I0kb4MlaAX;c&Rxz8{`MrAmyVuN*N9?SyA9? zn^Bj%g0&PUK|x_UvY@X8;7#j;@f{xGj>cq!JY&xY^cA-1oX|y1EEZ&W-v4~B=t5?5 zfG105pQqL;x(0Gpl#6>b31_KpM1!HZKPgv;MjIdLvwd89Nw4-)_;Rm<{0T{&D6(K0)G{l?H_`_nNC<(K~LmV{deo()Ojx)0zWL8y!Cv~em)ykBUUFUS!nejf{ zEX=H$ctWqj_!g*~@NI0Rg7nZKRi8SQ=5kfJ=`}0L7!!DHn3*)nT9pb>Q{E;Ei7{zb zaE{{RB&9#Nd)m!r2=*AJhjQ-yOXgLx%k$teJ-CG;dE#WbY2bo7-_!T6o?DK(CLit$ zM*uG|Im5n03Xf~gjLndXfjGz$_+*xq4Kvqm8(?P?5k}6%_^rZX)?(QS#$~3aAJmfd z^7c)6q50;L{Pa^y@$W^_@_Uio1$p=Xr$zG5u`+VdSz;6gG!!P;M4mQME_Lwr_#Qnb zkpos%9Ys0~lwKJ!Ne0WjgXN~Qx>z}6H79FgbL4z^_=Bl!2kUMW0W1GFLaRT8^&1cT zbf@uzgT6(U^$HP9Xcgl#Fd;z!rqenImfC|gThBsR_Y>vyj!!VYyIr6Y*fGu|2{_d) zpv5tq@)=E6;6Me=0zYKBcym2sddTl-)e*9tWil%5vjVhxnrJoBh*_^ItE{zoHsm7_0Z!rn*oSN!sJ3R8- z`z}&gCteEeI|oBtwkJD+=bMF%YlXtsKR}o|czt!^zaiJYMSTXV|9{wf>!_&LwQqbY zHX;~+fP$b%gS51gN{4_$4AKqK4JsiLB1#P@Al(d|(mg}N&{9L!Fm$~4xc70N=RD^* z=iR^coZnyXTK+MMwPcw2-f>-@>l1F0FxKYMoBSB=n>T2xyrn{{iZV1ymD24D38&qW zf*uE9?O_=v!aacLzkyzu1mv+W&0F|E@%b zEM&hdaGC^Vg*0J4@O@WrDq+W$MJ-z|^;5MU=!!}84q6JxmQe6!lhn8Gr527?)>7{n zWxapveb7fL`n9c&(J`1l5x!Qee)n)Ig{l9mu1a{g4LG%ZAC)9#1f6eC?#$L!FSkX1 zZM3FK-I~ja@@ZUIEtiEUr|!gpUOAzSg56DZXLtQ>yF6RIeuZhDMgS>Wu9YNnO||WQ zfYOdUuyeg{e63KR_5#6@65J^Gs%E%J_t(qXHSVg0iw%U6r19x@q0wNp05?`hzDm|h zv9m%v3doD4R+>c?_pa}TsLZs zJWj~1W~#KcANg({PVW%iFZ!E@_tGG@j(#|k`Zola?5yxLoVC}!2RM~+ms88Nm45j0 z%S6a-_eha3WD!lagph^=UjK<9DC62ShL=4If5_UZuK#Sm$^UBm4Lpn0FXEKfz_a*J z%50W zgW{D=*5NJ!@#aIhU&TM%AlsqfwMhN0q#rCu=eRne=d3Y*G~Fa)+8o3e%k>VJ@{EsZ z`py9=0CCcfyr7f45f-)Vpis1r>Q8lxMxg;&4g@|iTaM~~)&YXUmPI`VIuUS?Ntkdn zrw+*bPhssZYcFmG^dz_7=M7Q>(*V+zuj4zjhL~y*$GC?Rw( zF)EjRYOe^Q5_G;NbhVPpRvE^iB1wHfV5_`LU&1j^&0J}*r;swfPe6iJAD%z*Bft-f zuflo)b6io-{U-B*0ML^o>-sN_Z#FVlM)b=Ob#dJ;3;9o9q`&g4zFdBT|8%?0hB|-U ztZ=<2IcwvZ6^RMFE{vhC@!eaZhCxPqqk>})!ERdjE{}jh{jz{k36tATxxyPd+*0y{HtH>uR`?i&NHQjqpdj^@<*@o zFRK*j=_r1<(Ijigc=JxxyLa!5(zAnFey_b16NK?(;}bxHjRFO4E)UAKS$-Dv$K_vC zd?l_tb{YG6YnE*mJ*31&<3t$;&KJ7r6tl?l#IKvPVTZfuvWfd>l7)zOhZ#MZL(eC{#a#MY`&04p2nKcRI%`yy+FQbCd*2*^nlvW0L!&!>&x$J zL}Xh+;$AOixQ>A!GTfp8eM&t=l>gF|<|wnwlq$;AsoUAMTeoIcOiD2{k28)d<$MyW?9e>Le75j>xd z-sG%}muuAT#g;f*l8rtdLmH9FYkHlUfNk-#t1ll_8+Bdz^2iRz$+&>`6x@X1qq|xZ}&9Hdpqs7 z!Zx>Ub}Uk%!xWerkBdQtG!nv*)2}Nr`qVqEp<;k1H&*MFz^ImO#dtV7v%+P%^%$~~ zdMS=d*!h``TDloO61g>>Sm%8~5#(0~*=F75Px3|Vo>WPxG7fQkY`MJiM%ZahHh_dB zTFHtR3M$0p9+3~Ay!PG+G}l9nolg`8v$&9?JA5a`mhS{{=e!RL+~T^DyUo0UUMFXT zoc9z3)g?)hZ1cwTLk?S2j~5PtW41P6!tExm7o4^A1N>)p)&xEQiVjPq>*;Y=k=dM{ zN@;v7?Q#?(XedeIN83%flDp zjOt&>2Qn3xxH1g)ajt?3JqadL-*q2e=TJw$gyI04BqegRao=Heq@$lO@Y#EUy9WMY zdD@jvL7BmWtrMLeFpp4Kd0AjE3d|+@@@nHbuzg7qH>scHN&^GwBr!0(suS~zIe0bW zdF)CNpDuKLJ`*qB*kp$&=lzd{3o2*2m^j_4fpck|m8fQ^C1528d+@M83Oir$DqK>7 zoXe?CZVAbdK~vg``J3IKlLuEzwVccOm;>xG{ms$gkBeyuo-{t~Vhqr#vOZ(-BS*o{lV)|08dTFxIP$g4yH~eMXxsy$`owWCk#Mxo5u+n>rfl4S7{B9686!v2im&5D6{K)w zt|L;pD#Pvld_Ziyoy8BfD?M;JqzPc^Bvy3{W~sk_*_vJNe?1vQ!Q?QS2S6TRfX|i+ ztQ`~uRbXU8&edAbRk{?YPvTY^$xOC7Rvd#!IF!pv`b--bTI*#EaRj$se^CY`sAI;W z_uCndZ6(PHFju!6DdN7>@Ow>jEyN`iQ5?jX+F5PTcEf(IT~$*JpQJMa)2z++ds$le zZN0Z(rp5GdM|(u+c|a%$bF3I>r=%ik!F0SOBtfy|o@dq`StU51Lqb#ZIPn^(^2@ZR_Qt!@firu{p z5SC|(0E*jS`#J3F{@+2~U}V9RLh*xnlM7cOEyov>8W@nq<-rvIPE#2zi4(odM1Uug z^s&iOubNSJ|w3KIPrFL_$16pe0K9O2X^5FuHx|m zPG`CjPK&c1rt(vs9__;k6oyizWtufp!GTYL;T!7@EJc&v;(Gh?x9s{DppU~8V7_G| z`XJ!TM7N0MPv$G(hpZEG^MLw}GIn{ud|H)ie`}7IvTl`>Ctd&EPi(~+*HE(*oPGpm zWlF{%OX?%P-P_PN-@Sry%~jXqzuH{kJS%?tsm5ov^ZKR6|JV+&0@-VK`9JThpq8(| zlv*eQ0Y(2At@JI<_80G}ua=q*@r9v{RP&~f&TsWhJ;k$VssT$MvT?9MgxnFAF$8xr#gQ2uBGF4`$nR#1rnyVO{-v8~JSArB@cUe1Qd!-6WmY zet3n$sv$p>gY3tBjkY)ex*W}2v+M6z?TsjQZchL>wRD$aG=f$8GoW6}T)y#eiuo+= z;uOFA7I+HHH;FDc)hZYx<4FTW*8tA-oWXix>;;Gm54cIk(-p-Rf>RwkChUdP>`mKw zw*XQ8Q~X*eBZgMyR!WEn!l-}ogU4Y_N`uIqP>I#iVh)zE-c`?IESKeIL7?+c;Gx%e z8sH}@f>fFGqG8mJoYEUhsgkZU%{lf5oe)>`mE7*HC+han%;yewM#b!(9G!{>xh!;_ z(c^u$3&hV&IOw2M|CP6CAG-Ssoq}Ltxa3F+j^Wvz*kF67_Q+Ox8xy9SD*H6SGg|xT zQ28B5V=;V*`FIO3#CtkO*-Q^VjyGby18sk_pIo(T@f_lYWSz)|n|e}s^X` z9`2$xLFbK6MqTl2=FS(v{HPNtq_w&6<38|%k1u*2^@y6=dfwSH^!!N6HksXAaR<`f z(=xh)+;}L57u{8>zC~)k@>uzga-+g|lj9h)1_zt13fpKMubzwRX*Vz@?BVp#Ffijy z4Q{V|K(!oWz0*-XwSS^AMzQ|EEo{BCHzlAC%+oe=#R9P~PPBjCfSulMIHxgEsj6(V zkvk?93_rwE8rPU@rXRbs zJgqN7G+rWL^rqH{CDr5oRD_65ymM|0#C4)rXuh2BK|H);4ZR?W=Czi$k;K*J|KcB! z+F54De~QY)@wr8iT-GJThfrP)fe(JC=>R6)fu~GL^*+{+tYE=FVz4u;mTCC`DDk4) z_I##Nay0d8ZgR5^pb+leUWCg{m_c5Tq{_)0%-*EFKyO6?zXpa#FTd8;$wyV{9y3Os zMb@64h>Yax-ptV=)4M_P+Kc%ya421QfzHwhLo~^pc#FCh*$$W;KsVqQjMQtN-euG& zx!P2Uk{<)CVEC|0Tj49crv==zx9XR{;icRaJ>Wa_prTk~eqRF=$(%V?A#!NsF-d!B z{wwuLM}mXLZYJ!Jbb`Qii%qBIbE+)Niob-_z9b-Oc_2%SMFYskH7&D6jd@D0qymS< zAu0jScd2r5&zW5Z@!SNg`YO(^?i*2c#Vr%j*c^irPV^?-$Iw(}esfg4g7rh82X>l5 z?r^7CqKgFmfhKiFkT8BZGZhqo^G)I(5ALj1%FfXSkftECM*q%~kq%#YwI1lh{Lg>5v$HyGcH_{NOhof4x!^m%v~Nc# zQUkRSa10Tko_4q-BbWVAd~bFiz;{R3mnHq2=8vX&<7UMxg4S3@e=UFjQ9@D=-& z{;8D;?0i*@C|dM9M#-9e*O9vPgO>&T4SOy=`|aaLSoW--fG0Bk?^kH4Ev zET&o>=Guc65;1aN8Jk1F4qp>G>;aU3<)&0Fd<>4;dAWW=pt=7#$LVt%7{R;xzfd1} zJYC4f;AZ{f6)x|AboR-5)nV=}NEyaGwG_=&%T?=vb~?~LqGEDn?vYYngmuGi4~@iQ zz>xl{h>vlTlaEw`&*jx>%8_Dx1r(Ln(Nm{Jb}A89=BlWa>-BNsP=^-%8oNU$YW`}O z%RY2PyFO(xLKPC?-8s{r%>U*E0DI`V%t9S|+?{dpxp9jMdiPln=kw1_K2D3@ZYf;S zJ2n4-1e5WdZc-+r>_;|1&!8bbPKcNxYiYt^D)+sboX96mEi>yewLWQ00@I%Qn|1+V z3@MS)<5?w@!oQPfb*YI4d=M+aF1-erdh(q?5zNB@2J&zxG z`x)fUJ^()bg}NBoTD17gW?y}gGG$en)`sRMu?g!VvsZjw>&u=Vnu@dqiRM@cjNT@Q~sPrq-Dd4AZ)Ec=1P<#$_fH=hka# z*SYWSCMG=28XUM$530==E3v*e=}A^ukJrBXc7^lDafu72WP3S-voKy{uL|E^LW^)s z|nH0Gv|Lkek*d^<}4HsOQa7og4VAkMm#h{tywk+n=M-v9WL`0yEe$%X}9Y z6H&YC)L5KJxsJ4P1;p(e=h+C^1AzAeli;UFuC3UYa7R3XyF3~ZSj_rU5ZCm@6u<)3 z2;iy=zx~n$-&pD3V!iicHlr82L{u+aJRjU*}HjYulEunQ6| zfN|o-XkaV5CgBk@XrB|UnvwPLS(*q%A)N^ce0`#N$c&N^X~zYGj-z*7M9GqWJI4hU zzxgA&O2Ei zv`(rMJG;N3yirNx{FYr3w`An)rZDNOj{W{1dp>z8L4kxJB2Q85iZbL*l>>$mM9P-5 z`6Wm(spVSwXF08pJlmXU;zT{}SQ*0-V3$^j9u4KH1|N+SeRbZPmbN3|BD_VJRce8v z#)w_mP3}$Uth|`-q_?BosXq$z%&WTgPaoq$-xCsi1w9yf<%@h{&7IdBZ-(aAoO@ zlqJf9xXW>>nhLnZU?)H#03R|U@yXign!xMNanlJ;nKH!44V5VAetE@)W^PxV2teuE zwd`2VRxx65++l+$XP6=3=*oxTzSqaia+z9lOkZ4p+LK;A_q5IT)rz+E-I(AjUeHg? zL6m00c0z$Y*zZu7Oa#5;%5zN@1$C^uH8PsLt<34Z%tH3ZmgyyX)4uMU(NfcesS~{+ z*scBwxim!@6X-UPh^HVQlvYjMqccGu7|_F+;YZ_-l${5!U4~?3&Q)umv&e1bS+&Xx zG_va?S)zeu886RD=x=8faOl(J6m{USRjaotnR?IO^L`pJ-T8l64E=Uimj<_%$d~^M ztO$@vZi>HCaj2eEdcGj&DN0oE^@}%W==^#YX@2{8 z7}sKD^X3a0=Y2+UA)}BzaJ+t`^Y@xVMXL{cclahIKU*4JIEG8PHA(eWc%S93MvnV+FgKy#;5J>sd`{F^& zRr}4|{;xI<*Q*|i#as=Pgb21LDy-)=#_LsIjt8G=QGt{W8O8G!I1cuk%o8wKecPSu zOtu5Z#qC(RcR~SF!1pJ6-)PrphTJf(;=T`kc*FNM!d&-WMajauBhS~~@f6(pvOU$Y z=xC{_+;G9+F@u;me%xx6ahCq#S=7#|&$HSZRA6BtzIk>og;lNBI!}m1F+K?^HOWcV zCprara!#|TAHK02U_kVYUB8*Dj7f8RlqYg?pAEvr8#X@AGMaJvMjiUqK|Yk$9@N@o z+@>o;by=IXTFGj7Uz}WbPP!&(UCZt_qE@ilzCmPtebafy9V@_#(WI@HI5VI*m`vXH z${g<&BfHZCO*Fq{MEhsXGgZ28&RxPG(H3W>9|Afo#giH+Fw4n9-fRA)!TBG$qw7*2 zW=tEK{_oWNz%)k3Yn-(Hhx85onkpBQ-bN+)&=XK{n#~MB2BnI+A-nm4j@yricW zcRoL-R|A8p*?}dfj}o7_GgVVTFHO6%vcyVX$2ht+QC=5Uu7r%Y%hRcS3eeo7zBFX- zhyz8+n>d1paZ3aAjXwhpDfP-G(<|2bTEhROz$FyHt8GR@*mMR*m zR4Z04s8O`gyw7Vj;wK+}H1KF0m3*@)aCvX-(G`j5;SULNcGHt-0;-t?!X;KAT$gYO zxfZn3{6NQJE-b7A76DR$TK<0kiGP8|G(k9e=aM22^{KrxTvPbPZ(2GJhn_foHpun! z=l_EngX_??&Lsta`B{_9i!8_cZCL;`>CZ?`P;rdR(@I(5zC)=Dz;$3H`Pg!-Xkf7* zPW6spejm|2SEdO9D5WT{H~y?mNX7pK@Q4}vD!x_%r_BB&7k$Xu;zg0towb`!1e`o> zX129Z87J$BUe&C4VIv)XRIP~Bn#$dw?AF^5F@tAAG}yP z$tvA*a8O)VDC+@cg5~v*hJZL2pXsKO?RMGm;UymKe1o!Mz{)VK-0<|AZhS72!-vZl z2l!UVNXfFO`?vspmGhfwN}O%_=Z79ck0`jDALLBtqkzGwlv=oI)=)V9trx=1i-OWI zJOmOE-7Yf+#Hj^aHrVw?V90rVCg&2g^?8UG%vLq{{5eYA8-6*O+$pJY`5W~zc!1f0 z7=EF$F99Bt7Ky_J`W+h9O_dcJ+krH`CNHLlP)PspHhy%2a~&j#(Ogvgd=(fyl8{`@ z+R%vez!l`wc%Ausc4m!m)dJDCZ|4}EUcde{RPyP!$`td3Pit#ypC~I-_|Kguz9n&; z;rch)u&`UT>)ArN4K>DV)yCI2BTr+0lqR&GiV?zxRfiKyf>lmkZ7NVasSt9}by8L$ zGgjs#&h=`CQF-Ypg5o#qpRicwMwB@+UMMd$!FQpP_X^T3EBIianyd`4uI0?tSk&}# z4Q}f^^14ucqCuijqZdq_r|=?cWu(04lgTL$oIdbg&SX7H5+)IpC5{R9SVp+rjv=DL zeS7$_-ASN!3~qXLkbGb{S?w12P(I$OX3gU~d_%CgwJiR)i}%H|5GbBMr;7{-RlP*a zdh39@3o|I&x;5b%k;GX`y6^D7caqLk&6#Xh9o?qhKUst0{)NA(oi01DuN*M!2HhT=h;EeS?qdC2Z`d2 z<1Wnq+S_B&_X_Z>=BL=5PD0p^l$eCAhauYQvf?YoqJ~u zC0U@~q-?3p8qd3{BK_&n+dNC>pfouQheRNWTreMT+&)ccKzX&@p=-cRUe<7W$n(p| zu^ujnYz9H%T-%d+iSW0PDI+YJj!_(pZ2Y=Mm)aB9LRt4GKo{F};*4Sg1OI z2XpZ{UoUX4beS;=M`87sHTC^>rVz0e7(oMcm$E|CQ~ zg{1cI_C5;hD*2-Wh1kcT(fV~U64tEp62=apn>dS@?SY{~|E49e_t)IQxA(C-`yY}0 z=CZYPd39tzAoQgqCj!tOy*WYX;qnk`@{0^*pZ0}V8VTD%qltO!;~4>C&qzJup&Xjx z(jde^B!%-`uXXnFkm6jmng=a`p(nRWy3N%$Z`jTf#d(Bo z3Sa9%2}II!Ug_a$ze7uT?th%;{r))X<=&j08TlDj7aw`MI>JUr|3FaOxbkM*&lxvv z68(navN%Z(h+}{MV&VAn72OJZQ&SMyBy3|fT-FpOnUjS1(IAy>qiF5`;?cS?pyMH8 zISpRTOsc6@v}--Zx)zf#vE}#+eT#Pek2{)}$plzvP7C)c9PIozOiM2qCliyEStnS9 zahcu#E;*GI**GuS0#QgM?VT>Wc`Xu0ko23Io28g|6}yIN(kBq;fspF(Rc=<5_boP& z-gM2bivtF)6sTajjLlQug=}kjW@Ju_n>iJqt*qhs`SN%neuj)l&TS2{h%80#T&;5H zV&Pm#^*m_u-DWa)cZJ6?e*iI4YD=#bQJmM~y_>}*DClZTZO|i=SJruYIq}5DrV_@4 zk0sH0wYh!K#R)wCQx{5|N5CYljXI?9Nm-IVd=chGOGhwEdSBS)IqZG4SS>xk->X_& zbqb2LC%z)iG`5)8t&}>yqJ)Y2o(uWxMULYa>{d+0!Dx|6<>Va|%>1?f;&eH1_qhUUkj6%Dfb3M6t2W)Nu?~6SsGPWTJF?+}H zP~C=q1yX|wjp*mruEd4D&bxf*F(=#QPDO&l!HGQTm%RQp)7w@We$LvG)r#&DYeM=k zHEOlJtpwy!KRyR%m4*a5Yv|-#;aq0M2H^zTvt-F@5i(uTDg7f3F;O1Z@48HFut@>- zi<()k7D~CXx1e+jfK%NE*q9eQqeD8#q{4RTu|9tUGXdPai^gg~uw0eL0CA_oms5~V z_O`~oh{OF%WL)hN5i(SaO6s$x>(#sR>G^J8)^$-rCd-B-oKK(tDsDetU|lnEUD|Ab zPXk#u#6M`~&~CQ*nV8G7{zdUoEJ}spFvfbJRC#qn*eU%y3m$Vq1SszeZKm)(+*y%a!SR^Y>>OekwQi z3J%X2>}ge+QN3hL$ZFJz7_46pJnI_}(@^PTnY6b#4fF5{hQQGjqI;?pde45u_PF%> zKV9JZDM&u#iL431*F3{Xj5ciW6r^zPrzl; zKI$qsa_<%GZ#^jKMlY%)xR-D)xZqX}?l#6pP(GuUTHhN z5jtanUBZIRB_U-TPGGmqTCd0&UPNjBQKF)t)@>diW9fGm-g zm5-LoLL3@i*a2ZZh!z`jA<1cay70FV$0Yc;9fsyDNgUzv5&n5@%jBKI=amsJ(i{l} zBBNAE{M`8A1Lr+2EY@>rEG3dT#|%V}#u>2)Cj#!6 z0V3^LXzU2Ec-_ZTOGO5Ej!^JeDD~!%z2bTpM5Wt_W`93hZGP=G0pdzRS1Y;4Qorea zyn`!*ZLpbmHT#p3L+}1V2dQJV#zNbM1BW0so(Es6o0pB-`^hM$u^`wn!#;y3RO?jv zJ##&(!8^KNT_*_Ff&j%{;GUfXTSsJ9^3}z@MKKfaH#*=FIi9}EeYuD4*5`EY-+K7W z;^^XmBJp3=`}JZ^8uh)*7uX#w|Nav3N_+cfQcCF!f7v9?j-X)j(r>^6P4zM41+!-A zx$4M6ZP&d8Y9Ky~9Lyb^dOO0%k9lCsH)oNi{Bn9OsEL&A_$e5;D<#o%)W!K<;vF7uYjxC?b;FsP({)loQ+1c#&)NaI$wJJN67{t$*)E$ysn{mg7`J$__B zW%MSbS>gRPl|Ca&A)}TM3i|`*Khsa^j({5Tz~FkFo>?f3FN4gy`eIe6T=aTk0-g>Lvl+W;)3O2hNq5+}4VQUO5tQ=G{WW$4PS76BlbCWP<3gX=y|J(&)Zo0iok_6OZ)Fv3IvWbJ8K3ygmA1gC6{$3UpQk8t{S)G zX}7CMaBChVy$eZpVUHyR&;83|0!;WXow*)z%#zwNTrBZ&9{WJ8`CA0vqNce&;zSSt+YShQJD_elz~K)BY$>VL9}vufgV_V!Vv_wsdb*MD>w#RB z#BqE(Lpol`{MdKf9aSaOZJ!mGK*ZtVm2k6YR4T+`tibYa6Tmi>t5+`|b;_Q}z0)|H zy*D(Jt>pVyEDtLTntSD|*~ZrejicLNcMP4{&fYGz>TA1|YA9EYfN{QunetX8apsMt zm|GuAcjRqre)7Eb!^0}X*Ta5o((B1ODT5)R1Q?}|_vcyer1X?%la8FOo*C$h6Cjm~ z6I4qTG!y87d9TI`bc0?(cl;oj%V5C-h(nq0QL#fwwQ7coVSs+U%sI%6Ua z*|Ixia-aNgQxe8eOKBdyhUKe{<5jRCH+4^}Yjar`N zz+rLw-)!Tvl5c4Be};)lXv}ld{`t@x>E5~gY8`lQ_**1#a#lI6D$t(fK$cdP*376mi{t+!Za9 z0B%1GR$g-w77CtpF_#Iqp*vqd_7B3nnpNn)m8V?c<&un-R_fH0uYqCPkP#yGxsce0 zX{rOQfRs(#87o_QqaQ9sm-(U~*8W4&bj`6}H>gBA*{wEQunU8sNfIHs540%*FZIAU zy%Y}Zd2+Oh5x25)U}g=6V^!*v^)N3!yhoAy7$e2OA-GS4!D68*m*bcL_e2VY?nSjm ziXyup5lIhftsK|?xg8r#mP$L8T-jiLyt{_-1@V2Nfsxr$iY?&YYM5TDm*`md{?>a8 zOW#>mw&|LqQh7mw7pgwd4&+lNfKCF{!H{vLs_WVMZW(sUb`Nq#-Cy^rt52b_>1qy-_H#*K4V_|SZ3QNRx z@t1~jR~lj-hSB!~Qrfwq-+PN>9BXiz*D0+kls#({VF_W)EcW72o!BiU_@0=gTlb7mffh@R9W`ukko6(eU2I)Z?=$wuiOh ztafy3CZ13EeCRXjnVwtv+=Oq;M22d^*66K|S1Nej?!^q44~0E~0gbm*?@Y4PX*%4W zlsA_jj0KZ?D+a&-VFAQrY=3Wc>?AW$Ns%^eO;4N=)!qxkJ2J)wlAhk1Q8)lt{}oW^ z+s`j=2=A&`v3!m6oh!$ycza5MX1f`CavXHvrUu6L45qyguDfb_3Tao{2In5{)6vR( z_JO+4%B5$|oLIF&O8FWvU7Drzshc%u1pkFdx6n ztiXZ5HAOC_Gy}}karf>2`(f$220Qcsz|B+Msb@GDy=?UIapA*sX z;reNh6cuqIU~yGmuO7klo5B}>DEAhf)t&aw`>L6WHCJh$ZM-v2xJdE)^w?kH=R;EX zk36I?dj85#&Yi1&%oeS13lf#_)ZVeV!=VQtS55}QkN!=;aW3dLnPx``9B@_CX)gUI zaJ4Fb4)bTdf&0zDX?{X~Jk;-Su=h}8#{ZE|b&tPtvp*l0cKjTAIrHGYz?rLmv1ex{ z8zmNM&SmN4$YUtrcv#%|d{RTqzC_@9=dX2H77Er3Z#?<0?+@vRTST${kwpZGie{CD z_`f+npoqW^f;j&}8)n?uY?$+p2QNqgI+HjXKk#;u%SqJym20i}y1#0YfBxdq?K2TU zs{sJ=ATfmvg}o&EqA~x?I)|EnH15*;FT1R!?$KWvp<%&9-(b>L?ugya;r?$x+7o-nnG9Xz-_Bl( zOW+61WZ>kd5XZ#UXg4`=)f7E>O1mFjV#{ew)f1B$6ZL0tOHq}SzbAZSo2N(&dclaj0-DcMl zPmyYXg9}gm3~oo?rTNbt9!l0okWrJu3Al)agQW^i0l;Dh6_Oo{%OC{Mr09nmv`N5b zU0`A!2Sd4b9V}H?pbC{YVog5|Z8$u2-kmoePZvJv2-Pn#>EXaeVV4JzvLQNwOCdpM zyfd}nKtF7{2{>DJ^beHGe@n9K=T{K?>D2KrFf^a{f8nAc!R~04n^{)8{_%PLRi&fV zH&Up-_0E0sWYw6D_EX=VN`HR9ZaH4nyvYN6l%?)Ve&o$Nv>4~WP0CS(3iqqr@R8Ny z?yICaD=Dnw2kBC`aar0x?bsFKUG4p(>@qlf{D}Tzhfi^ODH^S6UjrINv*9vYZJ?n6 z^|xq)-*#YrgYUwTJv}wQ$X#A7Vh7wG;#l&ZJ8&hcg+%g_I1^@pf^QK>))<-W#p9X9 z(P_z-d-fO0eq^T0;3_{jKtcaQw+qYC>b?s}9G*ioQCz}D+*{B1Wq|}pa3&*2{u58& zZ7U=H>G3YmD9_mOgLYfHYVRWOC5kCD_VR{$3E>-haZ*(8>50~s@yV>I>#V0)*B-~S zY9Dy7O;pR^0JZH!oy5agRU0ej? zg?@~I+kGb0Y(>dUcG4m)5H%U&xH2YDwMG@m+SedOxH#9JL9gF@^+o2aR2VZvxr3M* z;wKU({GL+C_1ip$Vf!=U_Q=%QlM4BZgeT&Yp1VSE0$xvrjDfZ%U3`;}!F78faWM3) z*UA2S$JL4C`1Shzz_9LD(K9)`8GBL^19s=6?N8i?BJCHtb&G?+yw$BMx(b6~TRR!AN#HG`3;J^kZmBTd7iM& zJ74_jhjOuGxorx)L*|Pj>&eokYPXfW%Z#5y*osZZWxF~EjS4JBZ*?mcT?vCHM^(Jt*4D z4v*66QqI72FQ9{w{!D;%H{JMQL3M`no52UdU}IUTc?Y7~e7N$;@!DTKSWc&`aSa_0 zQYHx6QI^Npz;$VFy^<39D^wS8a zW~mvunP+=H3^0Q9?A%YP$Z)yLw4^cKr+|o*^Gv(42%H69weo7xf*1;X@rl0zg{)%s zOLjy5-?8HE@f8d?E`JvWf7iusYQS@@bBeET;Vt!39Q%+@@DIP}^%L+FN-WYh9z&+XdxyfhHqq@c!=1aC}q1-IU%M)c-b~ zZF=9Vw+u>89aHbx~|wk&pQCPwT9~3!d!VMBkRxRh>*Cmsyqxy@wt4nm6j6Q z@Sf1L`%|bi`EJx+UW2h>{uAg@kNF#NGG3!7YDF}s81{GzVpCPhVLyCZqtePddZf~r zqUj@X$NeCr$JE#BxKzLpo5hVoy@gLT(%TX2)g?3EZ#cfcW?5B31*;ndyg4w4I`~Yv z3gM*7Vxw`s>X+D{!$@Z8KCxk3mZuDcK?mQoWpw&(hxq*Lop1fcE)zFt`@cX~Xkr#R zA(GzFC6{j^p5nP>t#rCRGQMNgTU7j>D+{^2s>b4-4rZALL=XuyATzjx$Ic~PTh&zA zsFIL=5y5WWMW($>_BsDn7HA+s7*!e5JBz^_g-*sNlMl1HXVW0m?4qNR=IftMc@xUd zoOFRZ!y55Y!kFY48^!cvd{oU9V}450)+*N@dmtZrFU+1VB1Jyy&K;#BOt~F818_*q zWSIHjE7_!oZp{h{RXcy*S%T%r7!Ru>078(ltMAH8#P&Ch|lwrLR_C_UJ2YP--lLC@P`d|iE zFX)x{1j;0M>KEE`@RoXQX+5~g#kTLInBriq)_2~y+DV6nVKI-MCzIke?(01nDzz~> zL9ZfW7hy7A#nEjlM%_o}WjZY%!M7Lt$POP{PQ=h1Zm(q)zKwY+*>Dg&Rqiy|_B@tb z+pBuh%`tbA_Hr9PoB2lMGyChXnF;3^ho&dF#wAA@4qJxa?mbw94f;1S5cAE`$a$7Qb9I$SC*NY=4}^$#6QCMxVfBPFoBZ1 zxa53W{as_aYA_wN1mq4pb{YH8l3Z?h%0xM)G_P{G8pO7DA1YBP~d#%Z*{{DK)?AigUW##@HH?SK1^!2*FWTs5j

x_qM zq?J4>f-@&t*-3D6F3%14E!Nn{PmEN3h`36_VSwwuIwZVJ!f4JC7@kLVD&)H7U$eKx z6oQHHrIEIu$7T+sr_Cx&93obig&L7LqmIf6HQsY2nb2arGu2zt8g?tcPnJZ_X`442 zYd@UFuK>cLaK?yaU@w}nyw5$JA)}yDRa!G~^Z@}fHF*l1OF5d1LH5`Vi318Q={z8RKqY8T{0-T=2@N5r?9>Pna>KcVf8fv1q| zkD>bf#N&l=Cp%aI6|RhNSREXi!QQ^q47ZXOzNQK-H&8vx4!tSppfY<_6 zWIZ0Um-R?~uRCJY?@_j~@ABLtf=Zja$7v}a$PO%Y<8orsE7~|py0XIHrt6Y zR}*TBwXS@8_9P8=_B8;G!2do~%^TVNR zsQB|6bZRrq284IG>hJr(f1{I&77vDS@?&113|`F_T$kjTOOjMGWYsKLyGKHpcN-E_o_|ls{+`9%bCFG#%Sn;A+XChN9LFCTMQBmp;mYR?XTDAo187$oRO-p#%67|6#Q!3w8|(r+OT(G9o5WtkQwhcu z8hOa&=o80r8p$fikRrKh4X){-fG~rYG2ZKSXCZB!Q36HhTbE})Jhq_Q^+>nHUc+!p z^}?F=qu;6j;k_xisJQ_~V%14TgRQA8LrkYdwXG6hp9*paEHiIkt+<1vS@y#+(W_+> zEh-Q0Su_}tbL~AJ)S(P{*0bM@tW{L(<5_Y9;-S>$;H3Dnb3{t$m#n9f|#tKGsxx7?7P_G!Z5^i$$u#s zV_@8_haa{3vv!|wBT(hu(axTbr-RcL6H*`VsE;#4pxm_&s1t0Cpd(c_#V|gt+%K;C z0o+`!o#F%91r{|h>KJ%F#w|jo?Q@e!_nV2#<803jV{)$&8`$RJIY5t^4WvLWos=Q_ zuH%q)iodu{H$)!&oF8;yZX^$JteMLuy#Gg>7)SZjOFHgj%OlN7e~(rfSX8$sWv)XjKUOA5Qur;5Ra>Lo z+MqxNDBAP76=!NGYCpR5&9g{1^Es`(+cfV#O?{D}LXNuIr(Ab-e-UqH2)+!#in6@A zDjWOQH}}+GJk|`P4WuAHAy!7YL9&sl!36&G^AAegLRd?HF;o8^V-zA7j-c$)(9FX1 zy8F^iRiJ{;uuF3^n#{FXJ361Kk&{c-Or}t31=ZidHkYg>sH_WncTx+%GgR##~QBPa$fIyjAGlS=IjcKv&r77#B{j(j!;v#qXW#wXuxmz51AV{;0 zGslPIz0PC`rdq7bK$OPtWb37P0HSJQDlysQ_~4UBp4Dh!1n|51=A%NQP@X^RUQd)j zanaXcJzm)IEJzLT#ucjeVWwYGQPvGO0bf69iZ|GfS8X0h|Deshfdim2qj zDIBPQeD@o*m{iuzf|A(6asBx!?abzFB#ris`L_I($eHb-nr9fn39s*uX4ZK|%bx$i zh|O?(36^$7!woj}*v)vjrVIpR#W6hX0E|@1)2U<87ghgu2D~xtFw8#uRzrB5t`JiH0eTfes$FDA1k4|G ztKnc@=4~v0o^cII`C;P(cBR!Fd`(Fd;0=>{Cf>59%>^%EnJHzs-=WJ)ocm-S<7!Qe1Q!0`O!Lk1P$5XbiqVo`r_FzQ0qEi?#!Af=M)?sZ z9Ac9aUhnE-w;v zZ9s=s%g)3!an6~2(ygrNXgZ6ma6biBcm?wTRG(p>`dq&yWHWV}O{X+Spr*E>4=fxD{#TXk~AEDN2_r>1l!@(0$@4CxQv|pI3RRb!?fs=@#?e8*k zDh)rS6m2?59&b&ThZ306R%@pgRp5__B28nt9bofT=2r?HugKZezno*XH8EsYg{vg5 z=?ZCBTaI2wPpnua2O^Xif>U-UTw77})%WV3_flZUW~U1M5D=Ui$1-*XvXO*$-mqVu!S&-UpA z5nA9k(EiLic<|6)&+Q$k%Th$2mlsS{J^&%jDH6UODU?Evz1}zqCx|A}S=I@5d zTeqSWmxm8OOmv)NY-mP=gwxBxMFZ{qDNs*L-wZuNApl98E3d2v3p!xihZuCm-hY;tN%z{A*(Q+_yFh>X&$*>@D?2sW( z>sWHCdq-N0S>%=i0)9Jfg9({$0BB3&+3ti?8 z3j=TK56qIn?_JtnexMr^G^{g}rzxJToT+4qxZfRbHCN!v3w_lS^2zl zw)1+sMgs8ZznaM19gm@hh`DY1T_s{qnbvrkBxdd7EU!WSeLQa5-LAJv;&4c1*$Bx& zwlK_ZIW*M8UJtZ7vhNq9cqxG%3Doyl&%RGHivqHDsl}th`cj#=hIox^SxerX&xaRG z2iiWvkDERxWl44Jz;&@5^jLOYtGx^KQ}Xvx+Zi?9=b-z(YDF=i)m>)UAHOtstkIxS zwvP^@ZQFXq4q5KXHTTtO<4SH|*qn0>WeA}*^}npYTePvuchdL>R1gE=OiFh$aa*7^ z8XX|aec_lkUFAhAPqyjA*rMK?nMeu0B&skhn1T~~U!D8S!u`(%^KUYX(^@}-<+t*W zW~wZQ64Yi0W?;Nn$EI@adc2tD=eCfsws;n^%Y(-UyL^XZx5PYdL z`LUC4L55)Jda)Nr5=UpIuemD`vtP|9HPwsChXbZ{ye6x_p8wYw+t)*j{f^J>ro?8_ zK+MB>erC4Y@M4>UlIN|YG2l@$*qmi8kMCCFaFJG_8otn(Sal;_rp0 zN(GvPfT7hbUbC^j6`0ZIBhqbvGM zn64>d$DI{t-U_!7;f^rT1X9M}^^WEPP1~r6diUz~zJY8Q$2U-W4<uM7bhK+j! z@5S_~mMv90yS_duwb(ZsTm!mkRrsy&GHnuD!O``AxhG{3l zQ{#|fDKmE(4_)k&PthlZ`coqrU%>iZxLy3D_JW)Wh36HC#0q!cwQUUl;b3LK@Erv% z7~;(FT20km+i6K`Cw~zoAx4>@U16=f|1!PXwyyUVlW?G?1*!HFI2|wc>x~|84|8(~ zR#=XYna8`0ykew*xJnT+T>?xnA>!Y>3jeq1!>*~9s@*&hF`Je+;8myw7CkStogq^e z#hUD2i&=c6#&e?DI>Q*W!~D3-Dwi}mMBkw;aNQjwS5!^C82nrKpyo^-%R`&4*7)Fmbp!=`_ z2-74N55-5w7FR%mpm*#j(^1dS>fH;zZXC=Zo}S`<*uZ^qDwcq?HSy8lUXi}g8$+G1 z<4uOO{@fD0bbU#JKEN~U09ZBM6F_8qkyHDyHRkuCH->&52nk$P-ku({o;5>MOr`>- zy51vixfkH=q&l1m0bec_cZwOdCnCXS0e-mmAj^E^;)hGHUb$nJvMl*!lLP!g>z<$x z%F{uLnQLW0m;}l@x>~LxvEKbo5q)J8d)>gQK*YwZf11Rj%&mD229NHS*}q=?vV9q+ zT@&`%w3I*uMITRA%7*^A(1nKJUCZ%<2lK9oSW=%S3a@Nt{^UJ#!p-o7{kRFX=}!OQmuNg8oTK2nR}kOQL|4|x;| z{-)@(y`T{f4pIpbF9-r@9X6f1mw~W2WO)EbDO^OqDJEU8sZ?*zU2$gS;(WB|Hd5f^ z+R6l-qzP2fW#vN(0DMzC1QWRHb+igRxVJi!14+6wx(ZB`IKwsLg(ha5MGIof$l%AW zhi|-EgG&)uXshRte()h6yT8AkFQ3Bz?L}D}DBXtO__yg}i+$o>G!|A9#p7=5RyIT9 z(fUv1!F-;F{-Mb2R4J&QcCoX4yaHM zb~hT94%(QE}`0jMOJ4`1|TRWWgGVWJCc7>Z^6{wva1VuP6Bh`;ej4@Gf-B5R_cK4qd#yf|zlpn3s}Sg+4e?>4CO zPkZJj0hk&cXU47O>@wZV`JS#-+r*0lhr z-w~HT;s5kzYHUO~axbS9>^jY3lYm&<$0JIf(x%ZY+8q+_alID0b`R_hC}O!CUyt#r zbf=<5V5mi4U^pKSc0g6jgSy$ z^|mt^9WHLiE^U)Hh1vNa@hLIf6a~jq+s z7X)^VeKU2-1nc^(+QjRCM3mL_)gh?>O}4oe_Ss#e{`~YR)xlQ|nGnTgzJw!>`Pr_R zw;o3uVbwt9_JreQ1@^_H{>>M2%Cq)=$}x8R1qc#A7bCA-?se4hLeF+|Cm36>mpK7D zLlCi>10T;)JZILE9AY+l{EP-Jxxr{CQ=m7V<+NcqZ(O|~Y`1Vfl2>c3AIRn5dUbG8 zmSAwiGbZW|<%aiocV~k2%_k;SB<%PUlmGa+TV9@hCvRsGAAbdOVUeYVb7b7h+0W)# zb6G(q;5eXPw%KIGa*wm?6~B3sC`@FQFYe7XknCaULT?%S6lOpG%&B4?05p=bAVJXR zMqL73%?L^wsdw&0D`I^9D}@9caWYR1_XO{*;T2WcUq~Cc9EK-XGIuDeq1kj;Ym2he9QGZI)Kq_Q!d8l|roFa+j@D|0;5{Zf#oNO#Fs>pO@qSIKp{ zfPu`4tc}%Apv>98b(*ly73T`(Dr_2+LB#A@#Im$=B^HClOaOGF8-M|pRSI$GwT09z zKucMSr^&f~OF($Q6%3ccnGatssW7pzguQ#c2Q?cKo-LaMQ!~T;34H*PQosFjW9GPm zhY%+!EAAri$5d$s15a=I8Pk4ff4%#pv)2aZZ4igTSUs$DEP{hHnjTd&!|2-~Itjq? zt7b|`(m@nxb-Q`;lBZ%Y6Pqj!dz*QTA+^V|c=NCgs^FO-nZ@;Ju6@iW(%8I4-Ubu% zVYqaBo}fXa*GqrRl9k#Q8(f&!@Z5f`_LywBNY?zs&y%Q}W^DjAvy&N~;D_#VwSrl0 znzl>;u}JleJm)SF?L-NGogZwK>%Y1C|6#p&40@I>n*JQps!c6JGV$K8MRZIL-khIz z(~yKB%*tIbS_cDRmLF$3lcIgD;}EEQaO_Q`Hx8(FL_bcKjl6g0fU6sBW!gr?Yp%x6 z(JO@l%qViwb4XCqa}Nlg9_)DIyaklK7be2|+bezX-Bo6DvLL|7kFaSAKOyGq)gMUA zeU;nzq*BoD9gT~?Np=Bg?x~dfwdk&q&vr^NS~j=fAl8-*%J1>q?==>C!1_7c&bUNh za?ks|vjI%n`+Qu4B!j{L#L<^OL}yB% z$o?;K(RYCWu6a6_e}u0#Zb)+-B`GU`H@{mRv6;D{RSRZ$H%|1QBrIj`wC~n~XElI` zo0wi9u`3fFZLQDBtj{&HH~g%XtCII^NrUz)FpD=@@#eeJh|E^>uYnJV_rams2NH>?(|D|cMCNy2 z$)oGDKO{I?wap7svD;e<$T=ohXMh|Awo(9_6QLh8WZ(kwTe^?i(?t#BSmirGl(Wd+ zglJn4{e?FZk|YnTK#GQc$9w1S&oL$Mh*;FV)g~(>QAsysfv+3LAAiM8MmhpWPkmwF zG3b_81)o=cq7}?fspVjr3sijqF5HXHaSd$MXTW6okV}p^GYf&ncYCNdO#Ey@#mPe1zLu!9-BaO*>V>vy~yM%SJ z(pCbu!fHGou3lh??_{DH$SiV7W zH&`FIf62;oAKD?Qbh9PfLvKUkKKA?+RQZ{KdrGG4X0Qu#Yj@Hy`$bXl(q}9SbAM~j z-{tyZBjcNel}3rCs0UL_KZakpze%(HbNKbQ*{P?VB#IBX_m#mTk5jJmLWvSugGkp1 zxJ~<14#wO3SdCY|&Ak_}HC&9f48BdnPXfP{MBFF-%1ESLva@0A}Kx*B6J2lDmb3c?k zx0!*kn}SGz*GvTD%{BD5mXT+R#aWJ4M*;hJqB5$UbcJ)hBk7lXE#JNZ!AuDWfBf!B z1L6MoeITU!+bO1jfI=5AJY0AALZ__jClo|nb~sz%YI%~Dqvc$OeIIzu+1Yd|C96vf znwVw7Zl{dw4qnF7yML5KxNHx){t(@YHEuF~tMKZeFUyfEB+9@{4Ny1J%yOlJ;RA40 z*r2G9-#1*ci-0@uwbqpF3%BG8!e5du8zHQaN$kN2_4yP2^X2HTAZu&oDL=rz=;%F} zMovxWDp^2S&ix-+29p2JOAHj!hde8i(E4J_IK1tx@A_R*zko6Ts}-7<a<`Q}rDn=`a)?$o%>el&72tK$30AQjpfomoB#(5DP}{ zhT^|gF#wG7DZbzlhWsnoYOFYH;Z$ zcN*7qkj&POA1N2R;)H5}_Ux0w<-8 z=;3*B<;*9^79$h)4#xyscPIu6CYz0dq);I42gEy&VuSDPfVA~sGf;#F@DHn9FmO_B zxXP8KiMo=JXdeK+H2dpkYnJqArU9T5s~o;0^yUS!pA%%opfi%FyAH`t6u)+z z-+k=S`Rw(tU6OyNHarx42j2Sz5YvfE&dW>U!g3e~DBv3O9C2@@jXI+&!OZc6dz%G7 z1U^`eRT}K1zXSLLk8R^EaFkwOizx#G<+tDqm){XVJdXCjq*)foXfmoU{$Kup+Qv3T zBmbH7Yow5{tHf=Lx+S9P13b2eVTbFPYe*DmN$>!c?E4pENAILIS6 z8_23})xu8@u}S{9+3~%>eHo4=iPe0h#ml?37#?#*{IscIY9Ksxkh-4nTS8}Jq zbTl0B>n(gIjXXxnDS7M`9>Oek*DOs&zg4>JGY{r!wo=SL?t+;p@Pi2~82y$S_wxbU zA4=Ui7u6)CRdMgDPa`{sn1+P}C5*j?-eOSX+$I8bhigIyJmWQvNpQ8}ukYa?pZ!e?Upoi5LLQJ}G-mydm~lrBqPPt|3t@bYlS65_nc zGuO7k0QgZ&#xEBfZ_;jB6VWt)xExX}}qu)mL zv{I$bAI!RNCrMUvreOgrPxPa^dbP)~PwUD2C523&b=$fnap`)y_TlgSr$!`EIqC(y>0<0G@dd{1k8Oc%mrW_7r;t?HGLPbNahBHdj9Cc! zvnO8{)*KLPZHT2sx0;{dd;|T=NxP#z+f)pqqIAnZtRhjC>o6WckwxV&00R6n+#zy# zC1!Kt09zx2b*ouI$Vlz3D}~?jH(j3Rov+j;&rvH#BcN-I8$JD@+TDEQU@4ju(U73F z1!m}*F-!u*HdQ}J;HsLd6?%1{*_O-m^CeP}bh*N`(|RXEdq8%$kamNpd-r-PR)lur zj??SCYk=WAkAB(p`u+#-^5AhZ6J7o{q1{5bU4OaKV25-!6G%8TAIva5Ruzb5Pn3({ zGWN46MZR4)Vik~X{@jJD_u}%GlE`F1+s21jrtXhlj8mc{${g58`UGqDVY6{HifDN( z9*SM}0DM{HE#a?q+A?sZs@m3CyF`*+!CYZ4x#T4Y>7AA249kH{kT91FAV5W*?Zt() zI#)t5z1sWZ#LJ?-KOif+|IlkD(;bAcB1qU5fq^3p!YTLwX?bI$CTT}xU)UefwEzG` zyX7Iv$nur-3n&T~)Y{zj+DChOLo9fSm9@;`e}` zFuqrfyPcTC-#bDLtXdN78atY{;C|>ueG`q^7kpT&*yhReEN@;tz{;JY|M*Q(!v8{| z`a7c=KzavGM!sui?vFCTbu<0R&^@A(b|>Gb6t+PyLodMX`%aG! zJ>T?*Bp8jjYANHfzGg@Hlh#pVN({_a0bLiDd0%>V7YYeyI zn&afgfv_Hl?Y&!SA;6a)6_>msD-!6C7XBu|9K9D}%4H{lrE`V}!pYuE%-`&OxA1-!8 z=9C(ttjg4YlKF0{wl&-#<1uMA;DW)s#d1j#PLH-Y4mMgP?ABY8f_HZWWesYh^*h63 zI-*%Dg=KWMsxwX36ZbHkV+DG3((Mrql1JZIRf`42Ydk|`qu{wwdFp5*v(f6RVeNHS zSZ5Sv3t2<&J`Ty?Vd8K*(5Vrzs{oxArt99?d*88vMfX{VKIVNk_aX?U78PTe-}R>&ti~ zVOUC< zIL`tvxS-)n4bpg6m^N9IoJE}(&0A7Ab_r9$h^>BMzjBi^^Dwa6uEx+5|cw`B(wS`wy9ch*dFDDWD}w8G+4fbEqGK z>0z&*!MVEz;tHlbtf^ZD65p))%ejGR)$sB+YEmvkAV0N`(k&o4ymgI5CJGV*!)?GUs>a;Jwjk)rC1%>GKNi=pl#b{JO}Y{Bt8v*p$s0w`bg3`SC+mbq3i;fkPBe$cY?i#FZ7 zR&Y4O8*wZAgmp$_ql3k?TU%i6Gl^)odl&h^3qrtlM7nR$gDC6_M8dc19M7Xg>;UFt zH?-_oFV}R?fKo2Bjmv#2Dye6&A9|-bZGIRR)^6dZ*`zglw0{j!YMnMz)MDe=o+|iW z^q6HJ4eGhYo7cJ_(h^E3EeP{DF$|>g-6^5NZWUl$`JZq{qXi(1rYzw4T}$9Q5pvzB zZQNfI5)8i9#*yW)s>LhJbvyK@3GU+S1a4e_@AbFQy7UnK@ypD1%dcMw8pSRs#7YuH zKG}8vDWb^gY-h!Qx(hh2xAF}af$^*>Y_<9U)$;n54Y6{&Mt5Klp}??)kk7jf9@>DL zA>i)gNtdzSWJUYZuN!{p%*o@-!I($KXcpU(ekfrHydTrsHr=JEd;JzFIa(Tb`6-|d z9^fyU=W11j0lN7t6F2ZSq@U-lbvOWT@<4X~{Ib^%0W8-AAb_P4E+Ftcypa;2&!NpP zMzGq{sXSRzb_~3Mn`s53Cq!zVdS5jb78jkw(A%RoBq8}KMHHBo-gdhAU2bT$boWKG zTtFrOq1e|4cQ9HL)s~D{&sr<88@y1H`NpIEW5&~YUasvWeNlXNif9ITvq*ykeeQyW zC~vFr>bZ?-)IRVY7MW0XOVItk(j38{uzMM#g@ded<{EB=k8;sNZkA3r6CBYK62Pq< zqw$toHB&a`jX+bS8`b&*2_oqcxayUPo;VAyx(oBor}t-0KCAXb*CUY~p`y&P^m1O$ zpn%I8<{Zr)TzSG3A&*ozCU1>xoa8aP?ez zkx2z}$fxx(QLF`hz=FC9DNg0u*>)A7pEy+sOmgm=)V@c$>us6pQy+Nrg#3gHezzs~ zWqkGbEy1anE9ws_)s33$D!>K@^X-=(?2R@xIe;fki;CU$wa zKzsY{U0Oa#mD_fNOb?h&(KXQNYbE?6bv0fu*qu?%Uj<=^nBs=2_7c&0*R#Le+ipLT zIqNUAz&f%xs0E@nvGx(sG=tP9D|avKE@ZaMwBlt35OpfX@|b#|TY@M3r5uZy7k4a| zDd7yu;?SzL9-4Dm4tZpTdmsk-U?|H4@+s>%t5m4YyGn_}4_DjbXCG5^a1viSEW57K zSeGb0IDZkdtOVGAf{%yuCzC^cgH*()taQ&UG^w#ku9((UHUR>GC01`$WV~zjdY~iI zId|1ja)k>4k=;87DNA$k$Wskrzi7gUa|`-7>Ha=05RcV3rkNrq6kas9-FnS_C?1Wt zIc-&5wP^mq8*#9&u_s_K_7KpD&KgpAKAEJVVuFj5y!{gWy+uUsvy)pAfjTPu%@cq% zilHOc39Y-@am2Xo>*4}l48qYp0%^3x6MIz7o+En}1m6wth)MymQ4*BUL`-QixG76$ zHG=XSXv|HGPY7BotK);6k1fH5Ug*Y@x`*t2U#eIu!O%V?klb#i!nLcKs_l^x$76LL1dh(z zO*2oWNh_!jHP66BVWL6aCq-4A&BOZltPzc4o_*}_- z_n-|5lg^#^^7Y#C^+MKnkjTFt+9>7Thz zo~OI*-I~wW1Ix7t>7=NO_JE3t;H8x0jNZTYr^D^1{PQb!?dq-kL_uV_9D&c}3czSz z95;(J`hL1SWw12pn!FB^g66NV#%V5$HRyAg4fJUHs3KJ#3)!vEw&*z9sn41yj=!hO zZ%)A>_F;aI%uN8>u5%s8Dhh>%yX;M|1CyonZ$kAxLbPpSr?pbNW_17Aeg7rwh64=IsQjaMyq%}J8q<*>>6VU4`I-Z^=bz&W* zWlt6e^+(=eLFa1~J-pK7EON}gyYdY#3C~Y7PDF(iMsM9ADHn2B=RGMi?oXiFNwQb#(l8QDG>_cuoAr?y)RgSmqb_q)ZR`C7Hsr+*m!aoTwS z2Op=eTfRYN%MD@g+uHxN)s21YYeJGO$md439F(QV*5(%M?vu1;%MDL7e$i*vD6)8+ zRi2AlhYgkNo`Kjc_}Red7x?nD;e^L0z#rjBcPyU@Cm*|{?#C8+EvCtg!pQCEz^Z`{ zxW^Pn7v5dh;<5EJTA#!RiM-rvAj`9*(_sW3GP>4b^XwNw{}1y^z<+#r@2ry3irG1l z){gngu3pzg$NBylkR?`)+x!=%v?BW{L>%gDA6G^m3|kzc+&QS;!bc2K+%M z#fU;YPr^_x?oEk&HJy4LQiGZszI>JwS}Kvo2H!(g4dAJHI?6ld!2kmX2?&uC&|gR}N#SmtZi+zFn5A^8^#;XkNp=7NShh;`jmCM|N;d7Xn|$gF z(L|N@DGPP3o{EuXWm0(RVo7{+wm=1pi)nry>X)OMDNnBad0g1`AgBtK%E{N7-&oSO>#7#E6LP5fs2Q%zps40?EYvHw0lISdM%_P>rwEIAlVufjfx`{0W1Ww=e$nTB)B!2K2;SC{QHf z=m&trBA|7owI#TMyqA(`t)p+{sh?iqEd|)6lvFhq${w^%b@aZyc%39|qOMvAv}@k0 zk11SG2wj)mx(`b|z+B3nX+Y>?PTi-+xmrV^K>7$dw6Qb>w#7x4x#cG(9s~8dPG%+ow|AXDxEyK=$UfP)`qZ0PGP za?5EGyX@AC7ZPl*B+NARFXb&DErl9}2z!{M$7)@=FUImX`+PWQucsWSlhKFRPTyG+ zpGJnbJsE#%#B92YdnJP**ofH~`0FE!g+pr^_1e8rl*bkvfHn7+{d-Tzf(Y}{4I(xz zuPD~oReTs9HYK2L*TbD)J}GChzePwceMUbq`BO3#?OMo>z$#)?)k_R~;X0*gDxA30 zUfR+5!}Wzl(fR%PA|bJv>K^CyWPCV^lFos@V6bI4$zyMW@w@2W;uF~f=14)QV2n;( zab>M1aVmPUMt5#mdeCuM$ji!byQR~PxnHK7z_{OT=NdiO1RKS= zd|rt18ZL@|W@vsR=oKMV&A0o=qT=*tfVo-K$LLBLVFUxu&EG#3Adent(i-9^ML7omg^4oqg_VhC)si z-J99EH8H3A=wXYy&A#)@IVXRNdq0cX+F6^BCgnDHrp&8*jiTC)ipUv5M8r*wL*m^x z<{aql+S{n9Qv21saR|HEK;V$EPAMdXIC1~}UQ`#d6&^LSCrpPxe2J5N0M!vk?X8!( zNE}TvV#rGIBa1L*)#Gm@z$BgHp_-XqxdpF$lAt=P<1&K(Cr|m~?#Z^Kvtk`k2CYWbG(uSsYbapL=_Fu4DB`Xy8cbehbEarA&L@#NY|8ZTg`_@#0!IZBWoL-4xh&Q$%8MKJb0xYt=H`#)o?g+Z zt&2?FdgNTaH55X5D7%8JVioli)#k&vllW%YE5Px|22s(Q~< zp2r{(jh(5EkGGdcBgpvSj1qWtL?kgscf_H`sHM6kRUlCdoqJhy0FwXSxgDSNnq;*T z1$VY>`pXfxibo(^HHWqNxJs`po7uOMTNES}ny%0x7jhkcEFHIJ%vHFgad_%UN#V{tjb($QGppBZZ3 zx%CH=utVAFL?dIl&p`bTOIO(8VrZSG5{If+yuS5ouKeni?v<~K3lz5o-iCNOi{FkW z^6NgrTrJG^LRi1Q`(z6x&#$0vr2BXel)B=zT@RqsJ%`t+dPdQ;9P{eA8X+0-q?tnp z{J6(sIqK{gU?5d9rgv4SDs>f9Sp!1_H;>n66q;vj(q7u=5JlQ^+JsIa1~%V*V6L{c z9O!Lead+Q*e|a@v>@nzQh|BAJ*zJanBN#(6Dkk8!hKrxLZ@5cuA7fw}K5LgDNnV-# z{Am{|mF$CKaqmJPv1UWL>|O}B;gJ&8UJzibI`5}AW7-*I!~FC%b=kf8%byx;t>lQi zGk~BiOPDWSq?3z@^Kt@uvO&RGJ)bG$J*ZPkf#;2Lt;TWrDgp3Z()iAglR(0LHN9$! zIPgR6D@(m80D!90NxOgEd*qE!Frwb7>M{Zy);JNQ6B@hgH&�G#0lTVT3;DUR)Y9 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%0D!_Qp`U5Eb3;y1acj-7c;lYU(Ix=(3fyJE{ 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 diff --git a/docs/discover/try-esql.asciidoc b/docs/discover/try-esql.asciidoc index 7731700147e50..32718d87c955a 100644 --- a/docs/discover/try-esql.asciidoc +++ b/docs/discover/try-esql.asciidoc @@ -1,5 +1,5 @@ [[try-esql]] -== Try {esql} +== Using {esql} The Elasticsearch Query Language, {esql}, makes it easier to explore your data without leaving Discover. @@ -9,11 +9,11 @@ In this tutorial we'll use the {kib} sample web logs in Discover and Lens to exp [[prerequisite]] === Prerequisite -To be able to select **Try {esql}** from the Data views menu the `enableESQL` setting must be enabled from **Stack Management > Advanced Settings**. It is enabled by default. +To be able to select **Language {esql}** from the Data views menu the `enableESQL` setting must be enabled from **Stack Management > Advanced Settings**. It is enabled by default. [float] [[tutorial-try-esql]] -=== Trying {esql} +=== Use {esql} To load the sample data: @@ -21,7 +21,7 @@ To load the sample data: . Click **Other sample data sets**. . On the Sample web logs card, click **Add data**. . Open the main menu and select *Discover*. -. From the Data views menu, select *Try {esql}*. +. From the Data views menu, select *Language {esql}*. Let's say we want to find out what operating system users have and how much RAM is on their machine. @@ -36,7 +36,7 @@ FROM kibana_sample_data_logs | KEEP machine.os, machine.ram ---- + -. Click **Update**. +. Click **▶Run**. + [role="screenshot"] image:images/esql-machine-os-ram.png[An image of the query result] @@ -57,7 +57,7 @@ FROM kibana_sample_data_logs | LIMIT 10 ---- + -. Click **Update**. +. Click **▶Run**. + [role="screenshot"] image:images/esql-limit.png[An image of the extended query result] @@ -75,7 +75,7 @@ FROM kibana_sample_data_logs | LIMIT 10 ---- + -. Click **Update**. +. Click **▶Run**. + [role="screenshot"] image:images/esql-full-query.png[] @@ -84,6 +84,9 @@ image:images/esql-full-query.png[] To make changes to the visualization you can use the visualization drop-down. To make changes to the colors used or the axes, or click the pencil icon. This opens an in-line editor where you can change the colors and axes of the visualization. -To learn more about {esql}, try other tutorials, see more examples and reference material, refer to {ref}/esql.html[{esql}]. - +[TIP] +==== +For the complete {esql} documentation, including tutorials, examples and the full syntax reference, refer to the {ref}/esql.html[{es} documentation]. +For a more detailed overview of {esql} in {kib}, refer to {ref}/esql-kibana.html[Use {esql} in Kibana]. +==== From daeb7641ac292a3e3792ccff005d6de1d468c0c0 Mon Sep 17 00:00:00 2001 From: Konrad Szwarc Date: Fri, 17 May 2024 15:11:21 +0200 Subject: [PATCH 47/71] [EDR Workflows] Improved date picker on Protection Updates tab (#183392) This PR introduces improvements to the UX of the date picker on the Protection Updates page. From now on, when a user enters the page with a previously selected custom protection updates cutoff date, the Save button will be enabled so that the user can deploy the default date, which is set to yesterday's date upon entry. Moreover, changing tabs in this scenario won't trigger the "Unsaved Changes" modal since no changes were made by the user. https://github.com/elastic/kibana/assets/29123534/252fc8d6-9a7b-4b82-9c01-6e1827319389 --- .../cypress/e2e/policy/policy_details.cy.ts | 89 ++++++++++++++----- .../protection_updates_layout.tsx | 37 ++++++-- 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts index 0bc6e907fe827..c587c9b9cf8e2 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts @@ -38,6 +38,17 @@ describe( }, }, () => { + const getAutomaticUpdatesToggle = () => cy.getByTestSubj('protection-updates-manifest-switch'); + const clickAutomaticUpdatesToggle = () => getAutomaticUpdatesToggle().click(); + + const getProtectionUpdatesSaveButton = () => cy.getByTestSubj('protectionUpdatesSaveButton'); + const clickProtectionUpdatesSaveButton = () => getProtectionUpdatesSaveButton().click(); + + const [expectSavedButtonToBeDisabled, expectSavedButtonToBeEnabled] = [ + () => getProtectionUpdatesSaveButton().should('be.disabled'), + () => getProtectionUpdatesSaveButton().should('be.enabled'), + ]; + describe('Protection updates', () => { const loadProtectionUpdatesUrl = (policyId: string) => loadPage(`/app/security/administration/policy/${policyId}/protectionUpdates`); @@ -71,11 +82,10 @@ describe( loadProtectionUpdatesUrl(policy.id); cy.getByTestSubj('protection-updates-warning-callout'); cy.getByTestSubj('protection-updates-automatic-updates-enabled'); - cy.getByTestSubj('protection-updates-manifest-switch'); + getAutomaticUpdatesToggle(); cy.getByTestSubj('protection-updates-manifest-name-title'); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); - - cy.getByTestSubj('protection-updates-manifest-switch').click(); + expectSavedButtonToBeDisabled(); + clickAutomaticUpdatesToggle(); cy.getByTestSubj('protection-updates-manifest-name-deployed-version-title'); cy.getByTestSubj('protection-updates-deployed-version').contains('latest'); @@ -85,12 +95,12 @@ describe( }); cy.getByTestSubj('protection-updates-manifest-name-note-title'); cy.getByTestSubj('protection-updates-manifest-note'); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.enabled'); + expectSavedButtonToBeEnabled(); }); it('should display warning modal when user has unsaved changes', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protection-updates-manifest-switch').click(); + clickAutomaticUpdatesToggle(); cy.getByTestSubj('policySettingsTab').click(); cy.getByTestSubj('policyDetailsUnsavedChangesModal').within(() => { cy.getByTestSubj('confirmModalCancelButton').click(); @@ -104,17 +114,25 @@ describe( }); it('should successfully update the manifest version to custom date', () => { + const todayMinusTwoDays = moment.utc().subtract(2, 'days'); + loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); - cy.getByTestSubj('protection-updates-manifest-switch').click(); + expectSavedButtonToBeDisabled(); + clickAutomaticUpdatesToggle(); cy.getByTestSubj('protection-updates-manifest-note').type(testNote); + cy.getByTestSubj('protection-updates-version-to-deploy-picker').find('input').clear(); + + cy.getByTestSubj('protection-updates-version-to-deploy-picker') + .find('input') + .type(todayMinusTwoDays.format('MMMM DD, YYYY')); + cy.intercept('PUT', `/api/fleet/package_policies/${policy.id}`).as('policy'); cy.intercept('POST', `/api/endpoint/protection_updates_note/${policy.id}`).as('note'); - cy.getByTestSubj('protectionUpdatesSaveButton').click(); + clickProtectionUpdatesSaveButton(); cy.wait('@policy').then(({ request, response }) => { expect(request.body.inputs[0].config.policy.value.global_manifest_version).to.equal( - defaultDate.format('YYYY-MM-DD') + todayMinusTwoDays.format('YYYY-MM-DD') ); expect(response?.statusCode).to.equal(200); }); @@ -125,9 +143,23 @@ describe( }); cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); - cy.getByTestSubj('protection-updates-deployed-version').contains(formattedDefaultDate); + cy.getByTestSubj('protection-updates-deployed-version').contains( + todayMinusTwoDays.format('MMMM DD, YYYY') + ); + cy.getByTestSubj('protection-updates-manifest-note').contains(testNote); + expectSavedButtonToBeDisabled(); + + // Reload page, make sure the changes are persisted + loadProtectionUpdatesUrl(policy.id); + // Date shouldn't match today, so the button should be enabled + expectSavedButtonToBeEnabled(); + cy.getByTestSubj('protection-updates-deployed-version').contains( + todayMinusTwoDays.format('MMMM DD, YYYY') + ); cy.getByTestSubj('protection-updates-manifest-note').contains(testNote); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + + clickProtectionUpdatesSaveButton(); + expectSavedButtonToBeDisabled(); }); }); @@ -155,14 +187,23 @@ describe( } }); - it('should update manifest version to latest when enabling automatic updates', () => { + it('should NOT display warning modal when user enters the page with previously selected custom date', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + // The button is enabled because today is not the same as the date in the policy + expectSavedButtonToBeEnabled(); + + cy.getByTestSubj('policySettingsTab').click(); + cy.getByTestSubj('policyDetailsUnsavedChangesModal').should('not.exist'); + cy.url().should('include', 'settings'); + }); - cy.getByTestSubj('protection-updates-manifest-switch').click(); + it('should update manifest version to latest when enabling automatic updates', () => { + loadProtectionUpdatesUrl(policy.id); + expectSavedButtonToBeEnabled(); + clickAutomaticUpdatesToggle(); cy.intercept('PUT', `/api/fleet/package_policies/${policy.id}`).as('policy_latest'); - cy.getByTestSubj('protectionUpdatesSaveButton').click(); + clickProtectionUpdatesSaveButton(); cy.wait('@policy_latest').then(({ request, response }) => { expect(request.body.inputs[0].config.policy.value.global_manifest_version).to.equal( @@ -172,7 +213,7 @@ describe( }); cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); cy.getByTestSubj('protection-updates-automatic-updates-enabled'); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeDisabled(); }); }); @@ -203,7 +244,7 @@ describe( it('should update note on save', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeEnabled(); cy.getByTestSubj('protection-updates-manifest-note').contains(testNote); cy.getByTestSubj('protection-updates-manifest-note').clear(); @@ -212,14 +253,14 @@ describe( cy.intercept('POST', `/api/endpoint/protection_updates_note/${policy.id}`).as( 'note_updated' ); - cy.getByTestSubj('protectionUpdatesSaveButton').click(); + clickProtectionUpdatesSaveButton(); cy.wait('@note_updated').then(({ request, response }) => { expect(request.body.note).to.equal(updatedTestNote); expect(response?.statusCode).to.equal(200); }); cy.getByTestSubj('protectionUpdatesSuccessfulMessage'); cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeDisabled(); loadProtectionUpdatesUrl(policy.id); cy.getByTestSubj('protection-updates-manifest-note').contains(updatedTestNote); @@ -256,7 +297,7 @@ describe( it('should render the protection updates tab content', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protection-updates-manifest-switch').should('not.exist'); + getAutomaticUpdatesToggle().should('not.exist'); cy.getByTestSubj('protection-updates-state-view-mode'); cy.getByTestSubj('protection-updates-manifest-name-title'); @@ -271,7 +312,7 @@ describe( cy.getByTestSubj('protection-updates-manifest-name-note-title'); cy.getByTestSubj('protection-updates-manifest-note').should('not.exist'); cy.getByTestSubj('protection-updates-manifest-note-view-mode').contains(testNote); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeDisabled(); }); }); @@ -303,7 +344,7 @@ describe( it('should render the protection updates tab content', () => { loadProtectionUpdatesUrl(policy.id); - cy.getByTestSubj('protection-updates-manifest-switch').should('not.exist'); + getAutomaticUpdatesToggle().should('not.exist'); cy.getByTestSubj('protection-updates-state-view-mode'); cy.getByTestSubj('protection-updates-manifest-name-title'); @@ -318,7 +359,7 @@ describe( cy.getByTestSubj('protection-updates-manifest-name-note-title').should('not.exist'); cy.getByTestSubj('protection-updates-manifest-note').should('not.exist'); cy.getByTestSubj('protection-updates-manifest-note-view-mode').should('not.exist'); - cy.getByTestSubj('protectionUpdatesSaveButton').should('be.disabled'); + expectSavedButtonToBeDisabled(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx index 5d244884c1068..7c9b637a8b8c8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/protection_updates/protection_updates_layout.tsx @@ -72,7 +72,6 @@ export const ProtectionUpdatesLayout = React.memo( const policy = _policy as PolicyData; const deployedVersion = policy.inputs[0].config.policy.value.global_manifest_version; - const [manifestVersion, setManifestVersion] = useState(deployedVersion); const today = moment.utc(); const defaultDate = today.clone().subtract(1, 'days'); @@ -93,27 +92,47 @@ export const ProtectionUpdatesLayout = React.memo( } }, [fetchedNote, getNoteInProgress]); - const automaticUpdatesEnabled = manifestVersion === 'latest'; const internalDateFormat = 'YYYY-MM-DD'; const displayDateFormat = 'MMMM DD, YYYY'; const formattedDate = moment.utc(deployedVersion, internalDateFormat).format(displayDateFormat); const cutoffDate = getControlledArtifactCutoffDate(); // Earliest selectable date + const [selectedManifestVersion, setSelectedManifestVersion] = useState( + deployedVersion === 'latest' ? 'latest' : selectedDate.format(internalDateFormat) + ); + const automaticUpdatesEnabled = selectedManifestVersion === 'latest'; + const viewModeSwitchLabel = automaticUpdatesEnabled ? AUTOMATIC_UPDATES_CHECKBOX_LABEL : AUTOMATIC_UPDATES_OFF_CHECKBOX_LABEL; const saveButtonEnabled = (fetchedNote ? note !== fetchedNote.note : note !== '') || - manifestVersion !== deployedVersion; + selectedManifestVersion !== deployedVersion; useEffect(() => { + // Prevent unsaved changes modal from showing when the user has not made any changes + if ( + selectedDate.isSame(defaultDate.toISOString(), 'day') && + deployedVersion !== 'latest' && + !automaticUpdatesEnabled && + !moment.utc(deployedVersion, internalDateFormat).isSame(selectedDate.toISOString(), 'days') + ) { + return; + } setUnsavedChanges(saveButtonEnabled); - }, [saveButtonEnabled, setUnsavedChanges]); + }, [ + automaticUpdatesEnabled, + defaultDate, + deployedVersion, + saveButtonEnabled, + selectedDate, + setUnsavedChanges, + ]); const onSave = useCallback(() => { const update = cloneDeep(policy); - update.inputs[0].config.policy.value.global_manifest_version = manifestVersion; + update.inputs[0].config.policy.value.global_manifest_version = selectedManifestVersion; sendPolicyUpdate({ policy: update }) .then(({ item: policyItem }) => { toasts.addSuccess({ @@ -173,7 +192,7 @@ export const ProtectionUpdatesLayout = React.memo( } }, [ policy, - manifestVersion, + selectedManifestVersion, sendPolicyUpdate, fetchedNote, note, @@ -187,13 +206,13 @@ export const ProtectionUpdatesLayout = React.memo( const { checked } = event.target; if (checked && !automaticUpdatesEnabled) { - setManifestVersion('latest'); + setSelectedManifestVersion('latest'); // Clear selected date on user enabling automatic updates if (selectedDate !== defaultDate) { setSelectedDate(defaultDate); } } else { - setManifestVersion(selectedDate.format(internalDateFormat)); + setSelectedManifestVersion(selectedDate.format(internalDateFormat)); } }, [automaticUpdatesEnabled, selectedDate, defaultDate] @@ -203,7 +222,7 @@ export const ProtectionUpdatesLayout = React.memo( (date: Moment | null) => { if (date?.isAfter(cutoffDate) && date?.isSameOrBefore(defaultDate)) { setSelectedDate(date || defaultDate); - setManifestVersion(date?.format(internalDateFormat) || 'latest'); + setSelectedManifestVersion(date?.format(internalDateFormat) || 'latest'); } }, [cutoffDate, defaultDate] From 36008b09ebd0e9d2a3516ca86cf5e208e42280b9 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Fri, 17 May 2024 09:32:04 -0400 Subject: [PATCH 48/71] chore(slo): Add tests for historical summary api (#183648) --- .../apis/slos/fetch_historical_summary.ts | 130 +++++++++++++++++ .../test/api_integration/apis/slos/index.ts | 1 + x-pack/test/api_integration/services/slo.ts | 23 ++- .../api_integration/services/slo_api.ts | 21 +++ .../slos/fetch_historical_summary.ts | 131 ++++++++++++++++++ .../test_suites/observability/slos/index.ts | 1 + 6 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts create mode 100644 x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts diff --git a/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts b/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts new file mode 100644 index 0000000000000..4acdfe569c404 --- /dev/null +++ b/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts @@ -0,0 +1,130 @@ +/* + * 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 { + SLO_DESTINATION_INDEX_NAME, + SLO_DESTINATION_INDEX_PATTERN, +} from '@kbn/slo-plugin/common/constants'; +import { ALL_VALUE } from '@kbn/slo-schema'; +import moment from 'moment'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esClient = getService('es'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const sloApi = getService('slo'); + + const SLO_ID = 'slo-fake-1'; + describe('fetch historical summary', () => { + before(async () => { + const now = moment().startOf('minute'); + const curr = now.clone().subtract(30, 'days'); + const end = now.clone().add(5, 'minutes'); + + const batchOperations = []; + while (curr.isSameOrBefore(end)) { + batchOperations.push([ + { index: { _index: SLO_DESTINATION_INDEX_NAME } }, + { + '@timestamp': curr.toISOString(), + slo: { + id: SLO_ID, + revision: 1, + instanceId: ALL_VALUE, + numerator: 90, + denominator: 100, + isGoodSlice: 1, + groupings: {}, + }, + }, + ]); + curr.add(1, 'minute'); + } + + await esClient.bulk({ + index: SLO_DESTINATION_INDEX_NAME, + operations: batchOperations.flat(), + refresh: 'wait_for', + }); + + await esClient.indices.refresh({ index: SLO_DESTINATION_INDEX_NAME }); + }); + + after(async () => { + await esDeleteAllIndices(SLO_DESTINATION_INDEX_PATTERN); + }); + + it('computes the historical summary for a rolling occurrences SLO', async () => { + const response = await sloApi.fetchHistoricalSummary({ + list: [ + { + sloId: SLO_ID, + instanceId: ALL_VALUE, + timeWindow: { + duration: '7d', + type: 'rolling', + }, + budgetingMethod: 'occurrences', + objective: { + target: 0.9, + }, + groupBy: ALL_VALUE, + revision: 1, + }, + ], + }); + expect(response[0].sloId).to.eql(SLO_ID); + expect(response[0].instanceId).to.eql(ALL_VALUE); + expect(response[0].data).to.have.length(168); // 7 days * 24 hours/day * 1 bucket/hour + const last = response[0].data.pop(); + expect(last?.errorBudget).to.eql({ + consumed: 1, + initial: 0.1, + isEstimated: false, + remaining: 0, + }); + expect(last?.sliValue).to.eql(0.9); + expect(last?.status).to.eql('HEALTHY'); + }); + + it('computes the historical summary for a rolling timeslices SLO', async () => { + const response = await sloApi.fetchHistoricalSummary({ + list: [ + { + sloId: SLO_ID, + instanceId: ALL_VALUE, + timeWindow: { + duration: '7d', + type: 'rolling', + }, + budgetingMethod: 'timeslices', + objective: { + target: 0.9, + timesliceTarget: 0.8, + timesliceWindow: '1m', + }, + groupBy: ALL_VALUE, + revision: 1, + }, + ], + }); + expect(response[0].sloId).to.eql(SLO_ID); + expect(response[0].instanceId).to.eql(ALL_VALUE); + expect(response[0].data).to.have.length(168); // 7 days * 24 hours/day * 1 bucket/hour + const last = response[0].data.pop(); + expect(last?.errorBudget).to.eql({ + consumed: 0, + initial: 0.1, + isEstimated: false, + remaining: 1, + }); + expect(last?.sliValue).to.eql(1); + expect(last?.status).to.eql('HEALTHY'); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/slos/index.ts b/x-pack/test/api_integration/apis/slos/index.ts index 6276dd4a4cf6f..c80a0c58e5ecc 100644 --- a/x-pack/test/api_integration/apis/slos/index.ts +++ b/x-pack/test/api_integration/apis/slos/index.ts @@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./get_slo')); loadTestFile(require.resolve('./update_slo')); loadTestFile(require.resolve('./reset_slo')); + loadTestFile(require.resolve('./fetch_historical_summary')); }); } diff --git a/x-pack/test/api_integration/services/slo.ts b/x-pack/test/api_integration/services/slo.ts index 4d28786df56a5..c7b765dddf613 100644 --- a/x-pack/test/api_integration/services/slo.ts +++ b/x-pack/test/api_integration/services/slo.ts @@ -5,11 +5,21 @@ * 2.0. */ -import { CreateSLOInput, FindSLODefinitionsResponse } from '@kbn/slo-schema'; import { SLO_SUMMARY_DESTINATION_INDEX_NAME } from '@kbn/slo-plugin/common/constants'; +import { + CreateSLOInput, + fetchHistoricalSummaryParamsSchema, + FetchHistoricalSummaryResponse, + FindSLODefinitionsResponse, +} from '@kbn/slo-schema'; +import * as t from 'io-ts'; import { waitForIndexToBeEmpty } from '../apis/slos/helper/wait_for_index_state'; import { FtrProviderContext } from '../ftr_provider_context'; +type FetchHistoricalSummaryParams = t.OutputOf< + typeof fetchHistoricalSummaryParamsSchema.props.body +>; + export function SloApiProvider({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esClient = getService('es'); @@ -49,5 +59,16 @@ export function SloApiProvider({ getService }: FtrProviderContext) { } await waitForIndexToBeEmpty({ esClient, indexName: SLO_SUMMARY_DESTINATION_INDEX_NAME }); }, + async fetchHistoricalSummary( + params: FetchHistoricalSummaryParams + ): Promise { + const { body } = await supertest + .post(`/internal/observability/slos/_historical_summary`) + .set('kbn-xsrf', 'foo') + .set('elastic-api-version', '1') + .send(params); + + return body; + }, }; } diff --git a/x-pack/test_serverless/api_integration/services/slo_api.ts b/x-pack/test_serverless/api_integration/services/slo_api.ts index 5a5c9eb5c5ff7..7312905589cb7 100644 --- a/x-pack/test_serverless/api_integration/services/slo_api.ts +++ b/x-pack/test_serverless/api_integration/services/slo_api.ts @@ -5,6 +5,11 @@ * 2.0. */ +import { + fetchHistoricalSummaryParamsSchema, + FetchHistoricalSummaryResponse, +} from '@kbn/slo-schema'; +import * as t from 'io-ts'; import { FtrProviderContext } from '../ftr_provider_context'; type DurationUnit = 'm' | 'h' | 'd' | 'w' | 'M'; @@ -58,6 +63,10 @@ interface SloParams { groupBy: string; } +type FetchHistoricalSummaryParams = t.OutputOf< + typeof fetchHistoricalSummaryParamsSchema.props.body +>; + export function SloApiProvider({ getService }: FtrProviderContext) { const es = getService('es'); const supertest = getService('supertest'); @@ -84,6 +93,18 @@ export function SloApiProvider({ getService }: FtrProviderContext) { return response; }, + async fetchHistoricalSummary( + params: FetchHistoricalSummaryParams + ): Promise { + const { body } = await supertest + .post(`/internal/observability/slos/_historical_summary`) + .set('kbn-xsrf', 'foo') + .set('x-elastic-internal-origin', 'foo') + .send(params); + + return body; + }, + async waitForSloToBeDeleted(sloId: string) { if (!sloId) { throw new Error(`sloId is undefined`); diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts b/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts new file mode 100644 index 0000000000000..d69108007477e --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts @@ -0,0 +1,131 @@ +/* + * 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 { + SLO_DESTINATION_INDEX_NAME, + SLO_DESTINATION_INDEX_PATTERN, +} from '@kbn/slo-plugin/common/constants'; + +import { ALL_VALUE } from '@kbn/slo-schema'; +import moment from 'moment'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const esClient = getService('es'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const sloApi = getService('sloApi'); + + const SLO_ID = 'slo-fake-1'; + describe('fetch historical summary', () => { + before(async () => { + const now = moment().startOf('minute'); + const curr = now.clone().subtract(30, 'days'); + const end = now.clone().add(5, 'minutes'); + + const batchOperations = []; + while (curr.isSameOrBefore(end)) { + batchOperations.push([ + { index: { _index: SLO_DESTINATION_INDEX_NAME } }, + { + '@timestamp': curr.toISOString(), + slo: { + id: SLO_ID, + revision: 1, + instanceId: ALL_VALUE, + numerator: 90, + denominator: 100, + isGoodSlice: 1, + groupings: {}, + }, + }, + ]); + curr.add(1, 'minute'); + } + + await esClient.bulk({ + index: SLO_DESTINATION_INDEX_NAME, + operations: batchOperations.flat(), + refresh: 'wait_for', + }); + + await esClient.indices.refresh({ index: SLO_DESTINATION_INDEX_NAME }); + }); + + after(async () => { + await esDeleteAllIndices(SLO_DESTINATION_INDEX_PATTERN); + }); + + it('computes the historical summary for a rolling occurrences SLO', async () => { + const response = await sloApi.fetchHistoricalSummary({ + list: [ + { + sloId: SLO_ID, + instanceId: ALL_VALUE, + timeWindow: { + duration: '7d', + type: 'rolling', + }, + budgetingMethod: 'occurrences', + objective: { + target: 0.9, + }, + groupBy: ALL_VALUE, + revision: 1, + }, + ], + }); + expect(response[0].sloId).to.eql(SLO_ID); + expect(response[0].instanceId).to.eql(ALL_VALUE); + expect(response[0].data).to.have.length(168); // 7 days * 24 hours/day * 1 bucket/hour + const last = response[0].data.pop(); + expect(last?.errorBudget).to.eql({ + consumed: 1, + initial: 0.1, + isEstimated: false, + remaining: 0, + }); + expect(last?.sliValue).to.eql(0.9); + expect(last?.status).to.eql('HEALTHY'); + }); + + it('computes the historical summary for a rolling timeslices SLO', async () => { + const response = await sloApi.fetchHistoricalSummary({ + list: [ + { + sloId: SLO_ID, + instanceId: ALL_VALUE, + timeWindow: { + duration: '7d', + type: 'rolling', + }, + budgetingMethod: 'timeslices', + objective: { + target: 0.9, + timesliceTarget: 0.8, + timesliceWindow: '1m', + }, + groupBy: ALL_VALUE, + revision: 1, + }, + ], + }); + expect(response[0].sloId).to.eql(SLO_ID); + expect(response[0].instanceId).to.eql(ALL_VALUE); + expect(response[0].data).to.have.length(168); // 7 days * 24 hours/day * 1 bucket/hour + const last = response[0].data.pop(); + expect(last?.errorBudget).to.eql({ + consumed: 0, + initial: 0.1, + isEstimated: false, + remaining: 1, + }); + expect(last?.sliValue).to.eql(1); + expect(last?.status).to.eql('HEALTHY'); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/slos/index.ts b/x-pack/test_serverless/api_integration/test_suites/observability/slos/index.ts index 77d2b169fc7aa..8df59e6f3b624 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/slos/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/slos/index.ts @@ -10,5 +10,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('SLOs', function () { loadTestFile(require.resolve('./create_slo')); loadTestFile(require.resolve('./delete_slo')); + loadTestFile(require.resolve('./fetch_historical_summary')); }); } From d8a5e173e13ed5518306b0cac95d91654db12788 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Fri, 17 May 2024 15:51:25 +0200 Subject: [PATCH 49/71] [Obs AI Assistant] Improve dataset functions (#182542) Implements some improvements to dataset functions in order to reduce LLM hallucinations. Specifically: - adds a `get_alerts_dataset_info` function to return alert fields, in order to improve performance of the `alerts` function. - use `includeEmptyFields=false`. This means that the Field caps call only returns fields for which at least one document has values. - for `get_apm_dataset_info`, if there is no data, return a clear message that there is no APM data to prevent hallicunations. - exclude dot-prefixed (internal) indices Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/index_patterns_api_client.ts | 2 + .../get_apm_dataset_info.ts | 10 +- .../get_relevant_field_names.ts | 21 ++- .../functions/get_dataset_info/index.ts | 3 +- .../server/functions/alerts.ts | 129 ++++++++++++++---- 5 files changed, 132 insertions(+), 33 deletions(-) diff --git a/src/plugins/data_views/server/index_patterns_api_client.ts b/src/plugins/data_views/server/index_patterns_api_client.ts index 849102c46aa91..28c79836cf8e5 100644 --- a/src/plugins/data_views/server/index_patterns_api_client.ts +++ b/src/plugins/data_views/server/index_patterns_api_client.ts @@ -31,6 +31,7 @@ export class IndexPatternsApiServer implements IDataViewsApiClient { allowNoIndex, indexFilter, fields, + includeEmptyFields, }: GetFieldsOptions) { const indexPatterns = new IndexPatternsFetcher(this.esClient, { uiSettingsClient: this.uiSettingsClient, @@ -45,6 +46,7 @@ export class IndexPatternsApiServer implements IDataViewsApiClient { rollupIndex, indexFilter, fields, + includeEmptyFields, }) .catch((err) => { if ( diff --git a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts index e0f3f82128ddd..72fb4c8c7d200 100644 --- a/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts +++ b/x-pack/plugins/observability_solution/apm/server/assistant_functions/get_apm_dataset_info.ts @@ -24,7 +24,6 @@ export function registerGetApmDatasetInfoFunction({ description: `Use this function to get information about APM data.`, parameters: { type: 'object', - additionalProperties: false, properties: { start: { type: 'string', @@ -96,6 +95,15 @@ export function registerGetApmDatasetInfoFunction({ ); }); + if (!Object.values(availableIndices).flat().length) { + return { + content: { + fields: [], + description: 'There is no APM data available', + }, + }; + } + return { content: { fields: [ diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/get_relevant_field_names.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/get_relevant_field_names.ts index 543641098836f..2f32731ac3f2d 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/get_relevant_field_names.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/get_relevant_field_names.ts @@ -36,9 +36,25 @@ export async function getRelevantFieldNames({ }): Promise<{ fields: string[] }> { const dataViewsService = await dataViews.dataViewsServiceFactory(savedObjectsClient, esClient); + const hasAnyHitsResponse = await esClient.search({ + index, + _source: false, + track_total_hits: 1, + terminate_after: 1, + }); + + const hitCount = + typeof hasAnyHitsResponse.hits.total === 'number' + ? hasAnyHitsResponse.hits.total + : hasAnyHitsResponse.hits.total?.value ?? 0; + + // all fields are empty in this case, so get them all + const includeEmptyFields = hitCount === 0; + const fields = await dataViewsService.getFieldsForWildcard({ pattern: castArray(index).join(','), allowNoIndex: true, + includeEmptyFields, indexFilter: start && end ? { @@ -74,7 +90,7 @@ export async function getRelevantFieldNames({ const relevantFields = await Promise.all( chunk(fieldNames, 500).map(async (fieldsInChunk) => { const chunkResponse$ = ( - await chat('get_relevent_dataset_names', { + await chat('get_relevant_dataset_names', { signal, messages: [ { @@ -88,7 +104,8 @@ export async function getRelevantFieldNames({ CIRCUMSTANCES include fields not mentioned in this list.`, }, }, - ...messages.slice(1), + // remove the system message and the function request + ...messages.slice(1, -1), { '@timestamp': new Date().toISOString(), message: { diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts index e5b4e21195003..f016ae126ca53 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/get_dataset_info/index.ts @@ -26,7 +26,6 @@ export function registerGetDatasetInfoFunction({ 'This function allows the assistant to get information about available indices and their fields.', parameters: { type: 'object', - additionalProperties: false, properties: { index: { type: 'string', @@ -90,7 +89,7 @@ export function registerGetDatasetInfoFunction({ return { content: { indices: [index], - fields: relevantFieldNames, + fields: relevantFieldNames.fields, }, }; } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts index 7b62ca4f5a6d2..03c61843e702a 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/alerts.ts @@ -6,18 +6,49 @@ */ import datemath from '@elastic/datemath'; +import { KibanaRequest } from '@kbn/core/server'; import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { FunctionVisibility } from '@kbn/observability-ai-assistant-plugin/common'; +import { getRelevantFieldNames } from '@kbn/observability-ai-assistant-plugin/server/functions/get_dataset_info/get_relevant_field_names'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; import { ALERT_STATUS, ALERT_STATUS_ACTIVE, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; import { omit } from 'lodash'; -import { KibanaRequest } from '@kbn/core/server'; import { FunctionRegistrationParameters } from '.'; -const OMITTED_ALERT_FIELDS = [ +const defaultFields = [ + '@timestamp', + 'kibana.alert.start', + 'kibana.alert.end', + 'kibana.alert.flapping', + 'kibana.alert.group', + 'kibana.alert.instance.id', + 'kibana.alert.reason', + 'kibana.alert.rule.category', + 'kibana.alert.rule.name', + 'kibana.alert.rule.tags', + 'kibana.alert.start', + 'kibana.alert.status', + 'kibana.alert.time_range.gte', + 'kibana.alert.time_range.lte', + 'kibana.alert.workflow_status', 'tags', + // infra + 'host.name', + 'container.id', + 'kubernetes.pod.name', + // APM + 'processor.event', + 'service.environment', + 'service.name', + 'service.node.name', + 'transaction.type', + 'transaction.name', +]; + +const OMITTED_ALERT_FIELDS = [ 'event.action', 'event.kind', 'kibana.alert.rule.execution.uuid', @@ -46,22 +77,75 @@ export function registerAlertsFunction({ }: FunctionRegistrationParameters) { functions.registerFunction( { - name: 'alerts', - description: - 'Get alerts for Observability. Display the response in tabular format if appropriate.', - descriptionForUser: 'Get alerts for Observability', + name: 'get_alerts_dataset_info', + visibility: FunctionVisibility.AssistantOnly, + description: `Use this function to get information about alerts data.`, parameters: { type: 'object', properties: { - featureIds: { - type: 'array', - items: { - type: 'string', - enum: DEFAULT_FEATURE_IDS, - }, + start: { + type: 'string', description: - 'The Observability apps for which to retrieve alerts. By default it will return alerts for all apps.', + 'The start of the current time range, in datemath, like now-24h or an ISO timestamp', }, + end: { + type: 'string', + description: + 'The end of the current time range, in datemath, like now-24h or an ISO timestamp', + }, + }, + } as const, + }, + async ( + { arguments: { start, end }, chat, messages }, + signal + ): Promise<{ + content: { + fields: string[]; + }; + }> => { + const core = await resources.context.core; + + const { fields } = await getRelevantFieldNames({ + index: `.alerts-observability*`, + messages, + esClient: core.elasticsearch.client.asInternalUser, + dataViews: await resources.plugins.dataViews.start(), + savedObjectsClient: core.savedObjects.client, + signal, + chat: ( + operationName, + { messages: nextMessages, functionCall, functions: nextFunctions } + ) => { + return chat(operationName, { + messages: nextMessages, + functionCall, + functions: nextFunctions, + signal, + }); + }, + }); + + return { + content: { + fields: fields.length === 0 ? defaultFields : fields, + }, + }; + } + ); + + functions.registerFunction( + { + name: 'alerts', + description: `Get alerts for Observability. Make sure get_alerts_dataset_info was called before. + Use this to get open (and optionally recovered) alerts for Observability assets, like services, + hosts or containers. + Display the response in tabular format if appropriate. + `, + descriptionForUser: 'Get alerts for Observability', + parameters: { + type: 'object', + properties: { start: { type: 'string', description: 'The start of the time range, in Elasticsearch date math, like `now`.', @@ -72,8 +156,7 @@ export function registerAlertsFunction({ }, kqlFilter: { type: 'string', - description: - 'a KQL query to filter the data by. If no filter should be applied, leave it empty.', + description: `Filter alerts by field:value pairs`, }, includeRecovered: { type: 'boolean', @@ -85,15 +168,7 @@ export function registerAlertsFunction({ } as const, }, async ( - { - arguments: { - start: startAsDatemath, - end: endAsDatemath, - featureIds, - filter, - includeRecovered, - }, - }, + { arguments: { start: startAsDatemath, end: endAsDatemath, filter, includeRecovered } }, signal ) => { const alertsClient = await pluginsStart.ruleRegistry.getRacClientWithRequest( @@ -106,10 +181,7 @@ export function registerAlertsFunction({ const kqlQuery = !filter ? [] : [toElasticsearchQuery(fromKueryExpression(filter))]; const response = await alertsClient.find({ - featureIds: - !!featureIds && !!featureIds.length - ? featureIds - : (DEFAULT_FEATURE_IDS as unknown as string[]), + featureIds: DEFAULT_FEATURE_IDS as unknown as string[], query: { bool: { filter: [ @@ -134,6 +206,7 @@ export function registerAlertsFunction({ ], }, }, + size: 10, }); // trim some fields From 0d79f90d2c2f32af7a468a868a20dbbf04cd1f54 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Fri, 17 May 2024 07:55:56 -0600 Subject: [PATCH 50/71] [ES|QL] minor typing improvement (#183572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary I can never remember where these callbacks are and before, VSCode couldn't find them via a type search. All better now: Screenshot 2024-05-16 at 8 28 33 AM --- .../src/text_based_languages_editor.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx index 64dac6802e6fc..fd20d9c2b25ca 100644 --- a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx +++ b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx @@ -359,8 +359,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ return { cache: fn.cache, memoizedFieldsFromESQL: fn }; }, []); - const esqlCallbacks: ESQLCallbacks = useMemo( - () => ({ + const esqlCallbacks: ESQLCallbacks = useMemo(() => { + const callbacks: ESQLCallbacks = { getSources: async () => { const [remoteIndices, localIndices] = await Promise.all([ getRemoteIndicesList(dataViews), @@ -399,16 +399,16 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ } return policies.map(({ type, query: policyQuery, ...rest }) => rest); }, - }), - [ - dataViews, - expressions, - indexManagementApiService, - esqlFieldsCache, - memoizedFieldsFromESQL, - abortController, - ] - ); + }; + return callbacks; + }, [ + dataViews, + expressions, + indexManagementApiService, + esqlFieldsCache, + memoizedFieldsFromESQL, + abortController, + ]); const parseMessages = useCallback(async () => { if (editorModel.current) { From 502cbfc9c1de3b2005775003755e298b564b0417 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 17 May 2024 16:01:20 +0200 Subject: [PATCH 51/71] [ES|QL] Fixes the filtering in null values (#183700) ## Summary Closes https://github.com/elastic/kibana/issues/183640 Fixes the filtering on null values ### 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 --- .../src/utils/append_to_query.test.ts | 19 +++++++++++++++++-- .../src/utils/append_to_query.ts | 11 +++++++---- .../components/layout/discover_layout.tsx | 19 +++++++++++++++++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/kbn-esql-utils/src/utils/append_to_query.test.ts b/packages/kbn-esql-utils/src/utils/append_to_query.test.ts index cb9465cff05b6..2f3d28c467444 100644 --- a/packages/kbn-esql-utils/src/utils/append_to_query.test.ts +++ b/packages/kbn-esql-utils/src/utils/append_to_query.test.ts @@ -65,7 +65,7 @@ describe('appendToQuery', () => { 'from logstash-* // meow', 'dest', undefined, - '_exists_', + 'is_not_null', 'string' ) ).toBe( @@ -74,6 +74,21 @@ describe('appendToQuery', () => { ); }); + it('appends a where clause in an existing query checking that the value is null if the user filters a null value', () => { + expect( + appendWhereClauseToESQLQuery( + 'from logstash-* // meow', + 'dest', + undefined, + 'is_null', + 'string' + ) + ).toBe( + `from logstash-* // meow +| where \`dest\` is null` + ); + }); + it('appends an and clause in an existing query with where command as the last pipe', () => { expect( appendWhereClauseToESQLQuery( @@ -107,7 +122,7 @@ and \`dest\`=="Crete"` 'from logstash-* | where country IS NOT NULL', 'country', undefined, - '_exists_', + 'is_not_null', 'string' ) ).toBe(`from logstash-* | where country IS NOT NULL`); diff --git a/packages/kbn-esql-utils/src/utils/append_to_query.ts b/packages/kbn-esql-utils/src/utils/append_to_query.ts index d8f6ed3a44073..d1bf0afa33755 100644 --- a/packages/kbn-esql-utils/src/utils/append_to_query.ts +++ b/packages/kbn-esql-utils/src/utils/append_to_query.ts @@ -17,14 +17,17 @@ export function appendWhereClauseToESQLQuery( baseESQLQuery: string, field: string, value: unknown, - operation: '+' | '-' | '_exists_', + operation: '+' | '-' | 'is_not_null' | 'is_null', fieldType?: string ): string { let operator; switch (operation) { - case '_exists_': + case 'is_not_null': operator = ' is not null'; break; + case 'is_null': + operator = ' is null'; + break; case '-': operator = '!='; break; @@ -44,7 +47,7 @@ export function appendWhereClauseToESQLQuery( // checking that the value is not null // this is the existence filter - if (operation === '_exists_') { + if (operation === 'is_not_null' || operation === 'is_null') { fieldName = `\`${String(field)}\``; filterValue = ''; } @@ -67,7 +70,7 @@ export function appendWhereClauseToESQLQuery( const matches = whereClause.match(new RegExp(field + '(.*)' + String(filterValue))); if (matches) { const existingOperator = matches[1]?.trim().replace('`', '').toLowerCase(); - if (!['==', '!=', 'is not null'].includes(existingOperator.trim())) { + if (!['==', '!=', 'is not null', 'is null'].includes(existingOperator.trim())) { return appendToESQLQuery(baseESQLQuery, `and ${fieldName}${operator}${filterValue}`); } // the filter is the same diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 7065e511951b6..0a7524ef8bddf 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -158,6 +158,21 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { [filterManager, dataView, dataViews, trackUiMetric, capabilities] ); + const getOperator = (fieldName: string, values: unknown, operation: '+' | '-') => { + if (fieldName === '_exists_') { + return 'is_not_null'; + } + if (values == null && operation === '-') { + return 'is_not_null'; + } + + if (values == null && operation === '+') { + return 'is_null'; + } + + return operation; + }; + const onPopulateWhereClause = useCallback( (field: DataViewField | string, values: unknown, operation: '+' | '-') => { if (query && isOfAggregateQueryType(query) && 'esql' in query) { @@ -170,8 +185,8 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { const updatedQuery = appendWhereClauseToESQLQuery( query.esql, fieldName === '_exists_' ? String(values) : fieldName, - fieldName === '_exists_' ? undefined : values, - fieldName === '_exists_' ? '_exists_' : operation, + fieldName === '_exists_' || values == null ? undefined : values, + getOperator(fieldName, values, operation), fieldType ); data.query.queryString.setQuery({ From 0e112e33541cef254c27f8e3bdd92bc47750626c Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 17 May 2024 17:01:37 +0300 Subject: [PATCH 52/71] [Cases] Fix flaky hooks (#183721) ## Summary Fixes: https://github.com/elastic/kibana/issues/183144, https://github.com/elastic/kibana/issues/182845 ### 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 ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../resilient/use_get_incident_types.test.tsx | 25 +++++++++-------- .../use_get_current_user_profile.test.ts | 28 ++++++++++--------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx b/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx index 7bff41383b77a..569483e43e566 100644 --- a/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx +++ b/x-pack/plugins/cases/public/components/connectors/resilient/use_get_incident_types.test.tsx @@ -19,19 +19,18 @@ jest.mock('./api'); const useKibanaMock = useKibana as jest.Mocked; -// FLAKY: https://github.com/elastic/kibana/issues/182845 -describe.skip('useGetIncidentTypes', () => { +describe('useGetIncidentTypes', () => { const { http } = useKibanaMock().services; let appMockRender: AppMockRenderer; beforeEach(() => { - appMockRender = createAppMockRenderer(); jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); }); it('calls the api when invoked with the correct parameters', async () => { const spy = jest.spyOn(api, 'getIncidentTypes'); - const { waitForNextUpdate } = renderHook( + const { waitFor } = renderHook( () => useGetIncidentTypes({ http, @@ -40,7 +39,9 @@ describe.skip('useGetIncidentTypes', () => { { wrapper: appMockRender.AppWrapper } ); - await waitForNextUpdate(); + await waitFor(() => { + expect(spy).toHaveBeenCalled(); + }); expect(spy).toHaveBeenCalledWith({ http, @@ -71,7 +72,7 @@ describe.skip('useGetIncidentTypes', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess: jest.fn(), addError }); - const { waitForNextUpdate } = renderHook( + const { waitFor } = renderHook( () => useGetIncidentTypes({ http, @@ -80,8 +81,9 @@ describe.skip('useGetIncidentTypes', () => { { wrapper: appMockRender.AppWrapper } ); - await waitForNextUpdate(); - expect(addError).toHaveBeenCalled(); + await waitFor(() => { + expect(addError).toHaveBeenCalled(); + }); }); it('calls addError when the getIncidentTypes api returns successfully but contains an error', async () => { @@ -95,7 +97,7 @@ describe.skip('useGetIncidentTypes', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess: jest.fn(), addError }); - const { waitForNextUpdate } = renderHook( + const { waitFor } = renderHook( () => useGetIncidentTypes({ http, @@ -104,7 +106,8 @@ describe.skip('useGetIncidentTypes', () => { { wrapper: appMockRender.AppWrapper } ); - await waitForNextUpdate(); - expect(addError).toHaveBeenCalled(); + await waitFor(() => { + expect(addError).toHaveBeenCalled(); + }); }); }); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.test.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.test.ts index cbc39d183d870..c26a6af826548 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.test.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_get_current_user_profile.test.ts @@ -18,16 +18,16 @@ jest.mock('./api'); const useKibanaMock = useKibana as jest.Mock; -// FLAKY: https://github.com/elastic/kibana/issues/183144 -describe.skip('useGetCurrentUserProfile', () => { +describe('useGetCurrentUserProfile', () => { const addSuccess = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError: jest.fn() }); let appMockRender: AppMockRenderer; beforeEach(() => { - appMockRender = createAppMockRenderer(); jest.clearAllMocks(); + + appMockRender = createAppMockRenderer(); useKibanaMock.mockReturnValue({ services: { ...createStartServicesMock() }, }); @@ -36,11 +36,13 @@ describe.skip('useGetCurrentUserProfile', () => { it('calls getCurrentUserProfile with correct arguments', async () => { const spyOnGetCurrentUserProfile = jest.spyOn(api, 'getCurrentUserProfile'); - const { result, waitFor } = renderHook(() => useGetCurrentUserProfile(), { + const { waitFor } = renderHook(() => useGetCurrentUserProfile(), { wrapper: appMockRender.AppWrapper, }); - await waitFor(() => result.current.isSuccess); + await waitFor(() => { + expect(spyOnGetCurrentUserProfile).toBeCalled(); + }); expect(spyOnGetCurrentUserProfile).toBeCalledWith({ security: expect.anything(), @@ -57,13 +59,13 @@ describe.skip('useGetCurrentUserProfile', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); - const { result, waitFor } = renderHook(() => useGetCurrentUserProfile(), { + const { waitFor } = renderHook(() => useGetCurrentUserProfile(), { wrapper: appMockRender.AppWrapper, }); - await waitFor(() => result.current.isError); - - expect(addError).toHaveBeenCalled(); + await waitFor(() => { + expect(addError).toHaveBeenCalled(); + }); }); it('does not show a toast error message when a 404 error is returned', async () => { @@ -76,13 +78,13 @@ describe.skip('useGetCurrentUserProfile', () => { const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); - const { result, waitFor } = renderHook(() => useGetCurrentUserProfile(), { + const { waitFor } = renderHook(() => useGetCurrentUserProfile(), { wrapper: appMockRender.AppWrapper, }); - await waitFor(() => result.current.isError); - - expect(addError).not.toHaveBeenCalled(); + await waitFor(() => { + expect(addError).not.toHaveBeenCalled(); + }); }); }); From 2a6090ff39e1940a2f537a8423ecab40342dd72a Mon Sep 17 00:00:00 2001 From: Tiago Vila Verde Date: Fri, 17 May 2024 17:08:39 +0300 Subject: [PATCH 53/71] [Security Solution][Entity Analytics] Only request read privileges for risk score in Entity Analytics dashboard (#183632) We previously required both READ and WRITE privileges for merely displaying risk score data within the Entity Analytics dashboard. This PR ensures people with only READ access can still view the risk data. The missing privileges call out will now point out only READ privilege is required: Screenshot 2024-05-16 at 14 59 31 ## How to test 1. Create a role which has access to all security related indices and Security Permissions. 2. Ensure that the user only has READ permissions for the `.risk-score*` index 3. Create a new user with said role from step 1 4. Login with that user and navigate to **Security -> Dashboards -> Entity Analytics** --- .../entity_analytics/risk_engine/constants.ts | 3 ++- .../risk_engine/privileges.ts | 17 +++++++++---- .../entity_analytics_risk_score/index.tsx | 2 +- .../use_missing_risk_engine_privileges.ts | 24 +++++++++++++++---- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts index dc23dceadd642..5a0a6541791ba 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts @@ -23,7 +23,8 @@ export const RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES = [ export const RISK_SCORE_INDEX_PATTERN = 'risk-score.risk-score-*'; -type RiskEngineIndexPrivilege = 'read' | 'write'; +export type RiskEngineIndexPrivilege = 'read' | 'write'; + export const RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES = Object.freeze({ [RISK_SCORE_INDEX_PATTERN]: ['read', 'write'] as RiskEngineIndexPrivilege[], }); diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts index a375f5cb9195b..b0bbc39609b3b 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts @@ -5,7 +5,9 @@ * 2.0. */ +import type { NonEmptyArray } from 'fp-ts/NonEmptyArray'; import type { EntityAnalyticsPrivileges } from '../../api/entity_analytics/asset_criticality/get_asset_criticality_privileges.gen'; +import type { RiskEngineIndexPrivilege } from './constants'; import { RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, @@ -20,7 +22,8 @@ export interface MissingPrivileges { } export const getMissingIndexPrivileges = ( - privileges: EntityAnalyticsPrivileges['privileges']['elasticsearch']['index'] + privileges: EntityAnalyticsPrivileges['privileges']['elasticsearch']['index'], + required: NonEmptyArray = ['read', 'write'] ): MissingIndexPrivileges => { const missingIndexPrivileges: MissingIndexPrivileges = []; @@ -28,10 +31,10 @@ export const getMissingIndexPrivileges = ( return missingIndexPrivileges; } - for (const [indexName, requiredPrivileges] of Object.entries( + for (const [indexName, defaultRequiredPrivileges] of Object.entries( RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES )) { - const missingPrivileges = requiredPrivileges.filter( + const missingPrivileges = (required || defaultRequiredPrivileges).filter( (privilege) => !privileges[indexName][privilege] ); @@ -44,9 +47,13 @@ export const getMissingIndexPrivileges = ( }; export const getMissingRiskEnginePrivileges = ( - privileges: EntityAnalyticsPrivileges['privileges'] + privileges: EntityAnalyticsPrivileges['privileges'], + required?: NonEmptyArray ): MissingPrivileges => { - const missingIndexPrivileges = getMissingIndexPrivileges(privileges.elasticsearch.index); + const missingIndexPrivileges = getMissingIndexPrivileges( + privileges.elasticsearch.index, + required + ); const missingClusterPrivileges = RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES.filter( (privilege) => !privileges.elasticsearch.cluster?.[privilege] ); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx index 71ab20090ff01..edf7bce0a9938 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.tsx @@ -160,7 +160,7 @@ const EntityAnalyticsRiskScoresComponent = ({ riskEntity }: { riskEntity: RiskSc const refreshPage = useRefetchQueries(); - const privileges = useMissingRiskEnginePrivileges(); + const privileges = useMissingRiskEnginePrivileges(['read']); if (!isAuthorized) { return null; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts index d51c2b2dfb49d..9fa4c8d4b3881 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts @@ -6,9 +6,13 @@ */ import { useMemo } from 'react'; +import type { NonEmptyArray } from 'fp-ts/lib/NonEmptyArray'; import { useRiskEnginePrivileges } from '../api/hooks/use_risk_engine_privileges'; import { getMissingRiskEnginePrivileges } from '../../../common/entity_analytics/risk_engine'; -import type { MissingPrivileges } from '../../../common/entity_analytics/risk_engine'; +import type { + MissingPrivileges, + RiskEngineIndexPrivilege, +} from '../../../common/entity_analytics/risk_engine'; export type RiskEngineMissingPrivilegesResponse = | { isLoading: true } | { isLoading: false; hasAllRequiredPrivileges: true } @@ -18,7 +22,9 @@ export type RiskEngineMissingPrivilegesResponse = hasAllRequiredPrivileges: false; }; -export const useMissingRiskEnginePrivileges = (): RiskEngineMissingPrivilegesResponse => { +export const useMissingRiskEnginePrivileges = ( + required: NonEmptyArray = ['read', 'write'] +): RiskEngineMissingPrivilegesResponse => { const { data: privilegesResponse, isLoading } = useRiskEnginePrivileges(); return useMemo(() => { @@ -36,9 +42,19 @@ export const useMissingRiskEnginePrivileges = (): RiskEngineMissingPrivilegesRes } const { indexPrivileges, clusterPrivileges } = getMissingRiskEnginePrivileges( - privilegesResponse.privileges + privilegesResponse.privileges, + required ); + // privilegesResponse.has_all_required` is slightly misleading, it checks if it has *all* default required privileges. + // Here we check if there are no missing privileges of the provided set of required privileges + if (indexPrivileges.every(([_, missingPrivileges]) => missingPrivileges.length === 0)) { + return { + isLoading: false, + hasAllRequiredPrivileges: true, + }; + } + return { isLoading: false, hasAllRequiredPrivileges: false, @@ -47,5 +63,5 @@ export const useMissingRiskEnginePrivileges = (): RiskEngineMissingPrivilegesRes clusterPrivileges, }, }; - }, [isLoading, privilegesResponse]); + }, [isLoading, privilegesResponse, required]); }; From d6af74431c22ff837e018b71f47619f4d4c2480d Mon Sep 17 00:00:00 2001 From: Julia Rechkunova Date: Fri, 17 May 2024 16:09:09 +0200 Subject: [PATCH 54/71] [Discover][DocViewer] Adjust bottom margin in flyout (#183478) ## Summary This PR updates the bottom margin so the JSON view can be scrolled properly inside DocViewer flyout. It's a follow up for https://github.com/elastic/kibana/pull/183468 (fixes the scrolling to be limited to flyout height) and https://github.com/elastic/kibana/pull/166406 (taller footer was added with Close button in it). --- .../public/components/doc_viewer_source/get_height.test.tsx | 3 ++- .../public/components/doc_viewer_source/source.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx index 6c6ae2852e053..e28df4d89d5bc 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/get_height.test.tsx @@ -8,6 +8,7 @@ import { monaco } from '@kbn/monaco'; import { getHeight } from './get_height'; +import { MARGIN_BOTTOM } from './source'; describe('getHeight', () => { Object.defineProperty(window, 'innerHeight', { writable: true, configurable: true, value: 500 }); @@ -32,7 +33,7 @@ describe('getHeight', () => { const monacoMock = getMonacoMock(500, 0); const height = getHeight(monacoMock, true); - expect(height).toBe(475); + expect(height).toBe(500 - MARGIN_BOTTOM); }); test('when using document explorer, returning the available height in the flyout has a minimun guarenteed height', () => { diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx index 13f2ee065f504..740afc99a9c62 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer_source/source.tsx @@ -36,7 +36,7 @@ interface SourceViewerProps { // inline limitation was necessary to enable virtualized scrolling, which improves performance export const MAX_LINES_CLASSIC_TABLE = 500; // Displayed margin of the code editor to the window bottom when rendered in the document explorer flyout -export const MARGIN_BOTTOM = 25; +export const MARGIN_BOTTOM = 80; // DocViewer flyout has a footer // Minimum height for the source content to guarantee minimum space when the flyout is scrollable. export const MIN_HEIGHT = 400; From d03606738501e941e5ada860e1ee4528de034ead Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Fri, 17 May 2024 16:15:11 +0200 Subject: [PATCH 55/71] upgrades cypress to 13.6.3 (#183733) ## Summary Summarize your PR. If it involves visual changes include a screenshot or gif. --- package.json | 2 +- yarn.lock | 42 ++++++++---------------------------------- 2 files changed, 9 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 696e5aa63a5d5..78dba79f5e6ba 100644 --- a/package.json +++ b/package.json @@ -1578,7 +1578,7 @@ "cssnano": "^5.1.12", "cssnano-preset-default": "^5.2.12", "csstype": "^3.0.2", - "cypress": "13.6.2", + "cypress": "13.6.3", "cypress-axe": "^1.5.0", "cypress-file-upload": "^5.0.8", "cypress-multi-reporters": "^1.6.4", diff --git a/yarn.lock b/yarn.lock index 195b869f022c5..98ce6357eebcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10273,7 +10273,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@20.10.5", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=18.0.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^18.0.0", "@types/node@^18.11.18", "@types/node@^18.17.5": +"@types/node@*", "@types/node@20.10.5", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=18.0.0", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0", "@types/node@^18.0.0", "@types/node@^18.11.18": version "20.10.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2" integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw== @@ -14699,14 +14699,13 @@ cypress-recurse@^1.35.2: dependencies: humanize-duration "^3.27.3" -cypress@13.6.2: - version "13.6.2" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.2.tgz#c70df09db0a45063298b3cecba2fa21109768e08" - integrity sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ== +cypress@13.6.3: + version "13.6.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.3.tgz#54f03ca07ee56b2bc18211e7bd32abd2533982ba" + integrity sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA== dependencies: "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" - "@types/node" "^18.17.5" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" @@ -28751,7 +28750,7 @@ string-replace-loader@^2.2.0: loader-utils "^1.2.3" schema-utils "^1.0.0" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -28769,15 +28768,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -28887,7 +28877,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -28901,13 +28891,6 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -31780,7 +31763,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -31806,15 +31789,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From e4af2210e7b105d9ab3736f79b48ee7c746dff97 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 May 2024 15:17:44 +0100 Subject: [PATCH 56/71] skip failing on mki suite (#180481) --- .../functional/test_suites/security/ml/trained_models_list.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/test_serverless/functional/test_suites/security/ml/trained_models_list.ts b/x-pack/test_serverless/functional/test_suites/security/ml/trained_models_list.ts index 745f2b8d4a65f..6ec414787dde3 100644 --- a/x-pack/test_serverless/functional/test_suites/security/ml/trained_models_list.ts +++ b/x-pack/test_serverless/functional/test_suites/security/ml/trained_models_list.ts @@ -11,7 +11,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const svlMl = getService('svlMl'); const PageObjects = getPageObjects(['svlCommonPage']); + // failsOnMKI, see https://github.com/elastic/kibana/issues/180481 describe('Trained models list', function () { + this.tags(['failsOnMKI']); + before(async () => { await PageObjects.svlCommonPage.login(); await ml.api.syncSavedObjects(); From bcbd55088ccacc9e873675948b02b3b522969c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20=C3=81brah=C3=A1m?= Date: Fri, 17 May 2024 16:49:34 +0200 Subject: [PATCH 57/71] [EDR Workflows][main] Enable malware on write scan option feature flag for `main` (#181028) ## Summary To enable `malwareOnWriteScanOptionAvailable` on `main` branch, around the time of `8.14` release, so we release this feature around the same time for 8.14 and serverless. 8.14 feature flag PR: - https://github.com/elastic/kibana/pull/179893 original PR: - https://github.com/elastic/kibana/pull/179176 --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/security_solution/common/endpoint/types/index.ts | 2 +- .../plugins/security_solution/common/experimental_features.ts | 2 +- .../components/cards/malware_protections_card.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 7eed9b47fb2d3..7b33c5a3606a5 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -1123,7 +1123,7 @@ export interface BlocklistFields { } export interface OnWriteScanFields { - on_write_scan?: boolean; + on_write_scan: boolean; } /** Policy protection mode options */ diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 66d9cedd24933..3a1fd7ce8b734 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -250,7 +250,7 @@ export const allowedExperimentalValues = Object.freeze({ /** * Makes Elastic Defend integration's Malware On-Write Scan option available to edit. */ - malwareOnWriteScanOptionAvailable: false, + malwareOnWriteScanOptionAvailable: true, /** * Enables Security AI Assistant's Flyout mode diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/cards/malware_protections_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/cards/malware_protections_card.tsx index 65be904ec649b..f1eda1ac0915e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/cards/malware_protections_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/cards/malware_protections_card.tsx @@ -180,7 +180,7 @@ export const MalwareProtectionsCard = React.memo( mode={mode} data-test-subj={getTestId('onWriteScan')} labels={ON_WRITE_SCAN_LABELS} - checked={policy.windows.malware.on_write_scan ?? true} + checked={policy.windows.malware.on_write_scan} adjustSubfeatureOnProtectionSwitch={adjustOnWriteSettingsOnProtectionSwitch} /> From 3d1d2a5d43416aecd06744cca1a0c1242362248b Mon Sep 17 00:00:00 2001 From: Konrad Szwarc Date: Fri, 17 May 2024 16:57:12 +0200 Subject: [PATCH 58/71] [EDR Workflows] Unified Manifest - Manifest Manager (#179698) This pull request introduces a new SO used for keeping relations between artifactId and policyId. It addresses an issue where some users found the single SO structure containing too many nested entries. Originally, we planned to rewrite the existing Manifest Manager. However, during the POC implementation, it became clear that the effort required to refactor and retest the existing solution would be substantial. Therefore, this pull request can be considered as the first step in transitioning our approach from one SO to this new, distributed one. The main idea behind these changes is to modify the structure of the SO, rather than the logic of the Manifest Manager. To accomplish this, we need to retrieve the SO from the new source, translate it into the existing SO format (many SOs to one), execute the unchanged operations of the Manifest Manager on artifacts, translate the resulting SO into multiple SOs, and save them. This change is expected to be deployed with a Feature Flag, and we need to ensure that everything continues to function correctly in both cases. Therefore, I've introduced a new FTR suite with the Feature Flag enabled, which should be run alongside tests with the Feature Flag disabled. This suite contains duplicated test files that depend on SO logic. When we activate the Feature Flag, these tests should replace the existing ones, as the Unified Manifest SO will become the default approach. It appears that there is no need to introduce any kind of migrations, as the Manifest Manager is capable of recreating missing SOs (which has been tested). --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .buildkite/ftr_configs.yml | 2 + .../src/constants.ts | 1 + .../current_fields.json | 5 + .../current_mappings.json | 14 + .../check_registered_types.test.ts | 1 + .../group3/type_registrations.test.ts | 1 + .../group5/dot_kibana_split.test.ts | 1 + .../common/experimental_features.ts | 5 + .../server/endpoint/lib/artifacts/task.ts | 1 - .../schemas/artifacts/saved_objects.ts | 1 + .../manifest_manager/manifest_manager.mock.ts | 4 +- .../manifest_manager/manifest_manager.test.ts | 1209 ++++++++++++----- .../manifest_manager/manifest_manager.ts | 283 +++- .../artifacts/unified_manifes_client.test.ts | 27 +- .../artifacts/unified_manifest_client.ts | 19 +- .../endpoint/services/artifacts/utils.ts | 2 + .../security_solution/server/saved_objects.ts | 3 +- .../integrations/artifact_entries_list.ts | 4 +- .../artifact_entries_list.ts | 376 +++++ .../endpoint_exceptions.ts | 245 ++++ .../apps/integrations_feature_flag/index.ts | 48 + .../apps/integrations_feature_flag/mocks.ts | 807 +++++++++++ .../integrations_feature_flag.config.ts | 34 + ...erless.integrations_feature_flag.config.ts | 35 + .../services/endpoint_artifacts.ts | 27 +- 25 files changed, 2795 insertions(+), 360 deletions(-) create mode 100644 x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/artifact_entries_list.ts create mode 100644 x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/endpoint_exceptions.ts create mode 100644 x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts create mode 100644 x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/mocks.ts create mode 100644 x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts create mode 100644 x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index ac53265b1c979..5b1734613c0f6 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -403,7 +403,9 @@ enabled: - x-pack/test/security_solution_endpoint/endpoint.config.ts - x-pack/test/security_solution_endpoint/serverless.endpoint.config.ts - x-pack/test/security_solution_endpoint/integrations.config.ts + - x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts - x-pack/test/security_solution_endpoint/serverless.integrations.config.ts + - x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts - x-pack/test/session_view/basic/config.ts - x-pack/test/spaces_api_integration/security_and_spaces/config_basic.ts - x-pack/test/spaces_api_integration/security_and_spaces/copy_to_space_config_basic.ts diff --git a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts index a04e762eaf67d..4244a5fed91aa 100644 --- a/packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts +++ b/packages/core/saved-objects/core-saved-objects-base-server-internal/src/constants.ts @@ -157,6 +157,7 @@ export const HASH_TO_VERSION_MAP = { 'core-usage-stats|3d1b76c39bfb2cc8296b024d73854724': '7.14.1', 'csp-rule-template|6ee70dc06c0ca3ddffc18222f202ab25': '10.0.0', 'dashboard|b8aa800aa5e0d975c5e8dc57f03d41f8': '10.2.0', + 'endpoint:unified-user-artifact-manifest|393c6e4f5f16288c24ef9057e4d76a4c': '10.0.0', 'endpoint:user-artifact-manifest|7502b5c5bc923abe8aa5ccfd636e8c3d': '10.0.0', 'enterprise_search_telemetry|3d1b76c39bfb2cc8296b024d73854724': '10.0.0', 'epm-packages-assets|44621b2f6052ef966da47b7c3a00f33b': '10.0.0', diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 65fd6af2ce919..04343968ef958 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -280,6 +280,11 @@ "title", "version" ], + "endpoint:unified-user-artifact-manifest": [ + "artifactIds", + "policyId", + "semanticVersion" + ], "endpoint:user-artifact-manifest": [ "artifacts", "schemaVersion" diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 03a189b1549be..6ee1f6d13521f 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -964,6 +964,20 @@ } } }, + "endpoint:unified-user-artifact-manifest": { + "dynamic": false, + "properties": { + "artifactIds": { + "type": "keyword" + }, + "policyId": { + "type": "keyword" + }, + "semanticVersion": { + "type": "keyword" + } + } + }, "endpoint:user-artifact-manifest": { "dynamic": false, "properties": { diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 2fa22873190e5..6b8a29c70227b 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -85,6 +85,7 @@ describe('checking migration metadata changes on all registered SO types', () => "core-usage-stats": "b3c04da317c957741ebcdedfea4524049fdc79ff", "csp-rule-template": "c151324d5f85178169395eecb12bac6b96064654", "dashboard": "211e9ca30f5a95d5f3c27b1bf2b58e6cfa0c9ae9", + "endpoint:unified-user-artifact-manifest": "71c7fcb52c658b21ea2800a6b6a76972ae1c776e", "endpoint:user-artifact-manifest": "1c3533161811a58772e30cdc77bac4631da3ef2b", "enterprise_search_telemetry": "9ac912e1417fc8681e0cd383775382117c9e3d3d", "epm-packages": "f8ee125b57df31fd035dc04ad81aef475fd2f5bd", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index 43f0d03b86552..712ddd4bca932 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -49,6 +49,7 @@ const previouslyRegisteredTypes = [ 'event-annotation-group', 'endpoint:user-artifact', 'endpoint:user-artifact-manifest', + 'endpoint:unified-user-artifact-manifest', 'enterprise_search_telemetry', 'epm-packages', 'epm-packages-assets', diff --git a/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts index 22a3b9858599a..dcb40a3b07621 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts @@ -205,6 +205,7 @@ describe('split .kibana index into multiple system indices', () => { "connector_token", "core-usage-stats", "csp-rule-template", + "endpoint:unified-user-artifact-manifest", "endpoint:user-artifact-manifest", "enterprise_search_telemetry", "epm-packages", diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 3a1fd7ce8b734..da993a18debe3 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -252,6 +252,11 @@ export const allowedExperimentalValues = Object.freeze({ */ malwareOnWriteScanOptionAvailable: true, + /** + * Enables unified manifest that replaces existing user artifacts manifest SO with a new approach of creating a SO per package policy. + */ + unifiedManifestEnabled: false, + /** * Enables Security AI Assistant's Flyout mode */ diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts index 8066e6094b568..d0c4d825d57d6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts @@ -170,7 +170,6 @@ export class ManifestTask { this.logger.error( `unable to recover from error while attempting to retrieve last computed manifest` ); - return; } } diff --git a/x-pack/plugins/security_solution/server/endpoint/schemas/artifacts/saved_objects.ts b/x-pack/plugins/security_solution/server/endpoint/schemas/artifacts/saved_objects.ts index f0bc3a80d3406..4ca6d67031662 100644 --- a/x-pack/plugins/security_solution/server/endpoint/schemas/artifacts/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/endpoint/schemas/artifacts/saved_objects.ts @@ -109,6 +109,7 @@ export const internalUnifiedManifestSchema = t.intersection([ t.type({ id: identifier, created: t.union([t.string, t.undefined]), + version: t.union([t.string, t.undefined]), }) ), ]); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts index a0cbc058ba6a8..31ac67b2368d6 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts @@ -72,6 +72,7 @@ export interface ManifestManagerMockOptions { packagePolicyService: jest.Mocked; savedObjectsClient: ReturnType; productFeaturesService: ProductFeaturesService; + experimentalFeatures?: string[]; } export const buildManifestManagerMockOptions = ( @@ -98,7 +99,8 @@ export const buildManifestManagerContextMock = ( ...fullOpts, artifactClient: createEndpointArtifactClientMock(), logger: loggingSystemMock.create().get() as jest.Mocked, - experimentalFeatures: parseExperimentalConfigValue([]).features, + experimentalFeatures: parseExperimentalConfigValue([...(opts.experimentalFeatures ?? [])]) + .features, packagerTaskPackagePolicyUpdateBatchSize: 10, esClient: elasticsearchServiceMock.createElasticsearchClient(), }; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts index 7b996c7737c89..d6e82322ffa9c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts @@ -14,6 +14,7 @@ import type { InternalArtifactCompleteSchema, InternalArtifactSchema, InternalManifestSchema, + InternalUnifiedManifestSchema, } from '../../../schemas/artifacts'; import { createPackagePolicyWithConfigMock, @@ -32,6 +33,7 @@ import { mockFindExceptionListItemResponses, } from './manifest_manager.mock'; +import type { ManifestManagerContext } from './manifest_manager'; import { ManifestManager } from './manifest_manager'; import type { EndpointArtifactClientInterface } from '../artifact_client'; import { InvalidInternalManifestError } from '../errors'; @@ -107,6 +109,168 @@ describe('ManifestManager', () => { ARTIFACT_TRUSTED_APPS_WINDOWS = ARTIFACTS[4]; }); + describe('getLastComputedManifest from Unified Manifest SO', () => { + const mockGetAllUnifiedManifestsSOFromCache = jest.fn().mockImplementation(() => [ + { + policyId: '.global', + semanticVersion: '1.0.0', + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_MACOS, + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_EXCEPTIONS_LINUX, + ], + created: '20-01-2020 10:00:00.000Z', + id: '3', + }, + { + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.0', + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_TRUSTED_APPS_MACOS, + ARTIFACT_ID_TRUSTED_APPS_WINDOWS, + ], + created: '20-01-2020 10:00:00.000Z', + id: '1', + }, + { + policyId: TEST_POLICY_ID_2, + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_TRUSTED_APPS_WINDOWS], + created: '20-01-2020 10:00:00.000Z', + id: '2', + }, + ]); + + test('Retrieves empty unified manifest successfully', async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + const manifestManager = new ManifestManager( + buildManifestManagerContextMock({ + savedObjectsClient, + experimentalFeatures: ['unifiedManifestEnabled'], + }) + ); + + manifestManager.getAllUnifiedManifestsSO = jest.fn().mockImplementation(() => []); + + const manifest = await manifestManager.getLastComputedManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); + expect(manifest?.getSavedObjectVersion()).toStrictEqual('WzQ3NzAsMV0='); + expect(manifest?.getAllArtifacts()).toStrictEqual([]); + }); + + test('Retrieves empty unified manifest successfully but uses semanticVersion from existing legacy SO manifest', async () => { + const semanticVersion = '1.14.0'; + const savedObjectsClient = savedObjectsClientMock.create(); + const manifestManager = new ManifestManager( + buildManifestManagerContextMock({ + savedObjectsClient, + experimentalFeatures: ['unifiedManifestEnabled'], + }) + ); + + savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => { + if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) { + return { + attributes: { + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined }, + ], + semanticVersion, + }, + }; + } else { + return null; + } + }); + + manifestManager.getAllUnifiedManifestsSO = jest.fn().mockImplementation(() => []); + + const manifest = await manifestManager.getLastComputedManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual(semanticVersion); + expect(manifest?.getSavedObjectVersion()).toStrictEqual('WzQ3NzAsMV0='); + expect(manifest?.getAllArtifacts()).toStrictEqual([]); + }); + + test('Retrieves non empty manifest succesfully from Unified Saved Object', async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + const manifestManagerContext = buildManifestManagerContextMock({ + savedObjectsClient, + experimentalFeatures: ['unifiedManifestEnabled'], + }); + const manifestManager = new ManifestManager(manifestManagerContext); + + ( + manifestManagerContext.artifactClient as jest.Mocked + ).fetchAll.mockReturnValue(createFetchAllArtifactsIterableMock([ARTIFACTS as Artifact[]])); + + manifestManager.getAllUnifiedManifestsSO = mockGetAllUnifiedManifestsSOFromCache; + + const manifest = await manifestManager.getLastComputedManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); + expect(manifest?.getSavedObjectVersion()).toStrictEqual('WzQ3NzAsMV0='); + expect(manifest?.getAllArtifacts()).toStrictEqual(ARTIFACTS.slice(0, 5)); + expect(manifest?.isDefaultArtifact(ARTIFACT_EXCEPTIONS_MACOS)).toBe(true); + expect(manifest?.getArtifactTargetPolicies(ARTIFACT_EXCEPTIONS_MACOS)).toStrictEqual( + new Set() + ); + expect(manifest?.isDefaultArtifact(ARTIFACT_EXCEPTIONS_WINDOWS)).toBe(true); + expect(manifest?.getArtifactTargetPolicies(ARTIFACT_EXCEPTIONS_WINDOWS)).toStrictEqual( + new Set([TEST_POLICY_ID_1]) + ); + expect(manifest?.isDefaultArtifact(ARTIFACT_TRUSTED_APPS_MACOS)).toBe(false); + expect(manifest?.getArtifactTargetPolicies(ARTIFACT_TRUSTED_APPS_MACOS)).toStrictEqual( + new Set([TEST_POLICY_ID_1]) + ); + expect(manifest?.isDefaultArtifact(ARTIFACT_TRUSTED_APPS_WINDOWS)).toBe(false); + expect(manifest?.getArtifactTargetPolicies(ARTIFACT_TRUSTED_APPS_WINDOWS)).toStrictEqual( + new Set([TEST_POLICY_ID_1, TEST_POLICY_ID_2]) + ); + }); + + test("Retrieve non empty unified manifest and skips over artifacts that can't be found", async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + const manifestManagerContext = buildManifestManagerContextMock({ + savedObjectsClient, + experimentalFeatures: ['unifiedManifestEnabled'], + }); + const manifestManager = new ManifestManager(manifestManagerContext); + + manifestManager.getAllUnifiedManifestsSO = mockGetAllUnifiedManifestsSOFromCache; + + ( + manifestManagerContext.artifactClient as jest.Mocked + ).fetchAll.mockReturnValue( + createFetchAllArtifactsIterableMock([ + // report the MACOS Exceptions artifact as not found + [ + ARTIFACT_TRUSTED_APPS_MACOS, + ARTIFACT_EXCEPTIONS_WINDOWS, + ARTIFACT_TRUSTED_APPS_WINDOWS, + ARTIFACTS_BY_ID[ARTIFACT_ID_EXCEPTIONS_LINUX], + ] as Artifact[], + ]) + ); + + const manifest = await manifestManager.getLastComputedManifest(); + + expect(manifest?.getAllArtifacts()).toStrictEqual(ARTIFACTS.slice(1, 5)); + + expect(manifestManagerContext.logger.warn).toHaveBeenCalledWith( + "Missing artifacts detected! Internal artifact manifest (SavedObject version [WzQ3NzAsMV0=]) references [1] artifact IDs that don't exist.\n" + + "First 10 below (run with logging set to 'debug' to see all):\n" + + 'endpoint-exceptionlist-macos-v1-96b76a1a911662053a1562ac14c4ff1e87c2ff550d6fe52e1e0b3790526597d3' + ); + }); + }); + describe('getLastComputedManifest', () => { test('Returns null when saved object not found', async () => { const savedObjectsClient = savedObjectsClientMock.create(); @@ -282,7 +446,197 @@ describe('ManifestManager', () => { }); }); - describe('buildNewManifest', () => { + describe('commit unified manifest', () => { + test('Correctly updates, creates and deletes unified manifest so', async () => { + const context = buildManifestManagerContextMock({ + experimentalFeatures: ['unifiedManifestEnabled'], + }); + const manifestManager = new ManifestManager(context); + const manifest = ManifestManager.createDefaultManifest(); + + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_WINDOWS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); + + manifestManager.getAllUnifiedManifestsSO = jest.fn().mockImplementation(() => [ + { + policyId: '.global', + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_EXCEPTIONS_MACOS], + created: '20-01-2020 10:00:00.000Z', + id: '2', + }, + { + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_EXCEPTIONS_MACOS, ARTIFACT_ID_TRUSTED_APPS_MACOS], + created: '20-01-2020 10:00:00.000Z', + id: '3', + }, + { + policyId: 'non-existent-policy', + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS], + created: '20-01-2020 10:00:00.000Z', + id: '4', + }, + ]); + + context.savedObjectsClient.bulkCreate = jest.fn(); + context.savedObjectsClient.bulkUpdate = jest.fn(); + context.savedObjectsClient.bulkDelete = jest.fn(); + manifestManager.bumpGlobalUnifiedManifestVersion = jest.fn(); + + await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); + expect(context.savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); + // TEST_POLICY_ID_1 and .global exists, shouldn't be created + expect(context.savedObjectsClient.bulkCreate).toHaveBeenCalledWith( + [ + { + attributes: { + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS, ARTIFACT_ID_TRUSTED_APPS_MACOS], + id: undefined, + policyId: TEST_POLICY_ID_2, + semanticVersion: '1.0.0', + }, + type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE, + }, + ], + { initialNamespaces: ['*'] } + ); + + expect(context.savedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(1); + // TEST_POLICY_ID_1 is updated, global is not due to no changes + expect(context.savedObjectsClient.bulkUpdate).toHaveBeenCalledWith([ + { + attributes: { + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_MACOS, + ARTIFACT_ID_TRUSTED_APPS_WINDOWS, + ARTIFACT_ID_TRUSTED_APPS_MACOS, + ], + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.1', + }, + id: '3', + type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE, + }, + ]); + + // non-existent-policy should be deleted for not being in the manifest + expect(context.savedObjectsClient.bulkDelete).toHaveBeenCalledTimes(1); + expect(context.savedObjectsClient.bulkDelete).toHaveBeenCalledWith([ + { id: '4', type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }, + ]); + // Global manifest wasn't updated, manual bump is required + expect(manifestManager.bumpGlobalUnifiedManifestVersion).toHaveBeenCalledTimes(1); + }); + }); + + describe('commit', () => { + test('Creates new saved object if no saved object version', async () => { + const context = buildManifestManagerContextMock({}); + const manifestManager = new ManifestManager(context); + const manifest = ManifestManager.createDefaultManifest(); + + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); + + context.savedObjectsClient.create = jest + .fn() + .mockImplementation((_type: string, object: InternalManifestSchema) => object); + + await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); + + expect(context.savedObjectsClient.create).toHaveBeenCalledTimes(1); + expect(context.savedObjectsClient.create).toHaveBeenNthCalledWith( + 1, + ManifestConstants.SAVED_OBJECT_TYPE, + { + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 }, + ], + schemaVersion: 'v1', + semanticVersion: '1.0.0', + created: expect.anything(), + }, + { id: 'endpoint-manifest-v1' } + ); + }); + + test('Updates existing saved object if has saved object version', async () => { + const context = buildManifestManagerContextMock({}); + const manifestManager = new ManifestManager(context); + const manifest = new Manifest({ soVersion: '1.0.0' }); + + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); + manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); + manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); + + context.savedObjectsClient.update = jest + .fn() + .mockImplementation((_type: string, _id: string, object: InternalManifestSchema) => object); + + await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); + + expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1); + expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith( + 1, + ManifestConstants.SAVED_OBJECT_TYPE, + 'endpoint-manifest-v1', + { + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 }, + ], + schemaVersion: 'v1', + semanticVersion: '1.0.0', + }, + { version: '1.0.0' } + ); + }); + + test('Throws error when saved objects client fails', async () => { + const context = buildManifestManagerContextMock({}); + const manifestManager = new ManifestManager(context); + const manifest = new Manifest({ soVersion: '1.0.0' }); + const error = new Error(); + + context.savedObjectsClient.update = jest.fn().mockRejectedValue(error); + + await expect(manifestManager.commit(manifest)).rejects.toBe(error); + + expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1); + expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith( + 1, + ManifestConstants.SAVED_OBJECT_TYPE, + 'endpoint-manifest-v1', + { + artifacts: [], + schemaVersion: 'v1', + semanticVersion: '1.0.0', + }, + { version: '1.0.0' } + ); + }); + }); + + describe.each([true, false])('buildNewManifest', (unifiedManifestSO) => { const SUPPORTED_ARTIFACT_NAMES = [ ARTIFACT_NAME_EXCEPTIONS_MACOS, ARTIFACT_NAME_EXCEPTIONS_WINDOWS, @@ -305,8 +659,10 @@ describe('ManifestManager', () => { ...new Set(artifacts.map((artifact) => artifact.identifier)).values(), ]; - test('Fails when exception list client fails', async () => { - const context = buildManifestManagerContextMock({}); + test(`Fails when exception list client fails when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = jest.fn().mockRejectedValue(new Error()); @@ -314,8 +670,10 @@ describe('ManifestManager', () => { await expect(manifestManager.buildNewManifest()).rejects.toThrow(); }); - test('Builds fully new manifest if no baseline parameter passed and no exception list items', async () => { - const context = buildManifestManagerContextMock({}); + test(`Builds fully new manifest if no baseline parameter passed and no exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({}); @@ -323,11 +681,6 @@ describe('ManifestManager', () => { TEST_POLICY_ID_1, ]); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); const manifest = await manifestManager.buildNewManifest(); expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); @@ -348,7 +701,7 @@ describe('ManifestManager', () => { } }); - test('Builds fully new manifest if no baseline parameter passed and present exception list items', async () => { + test(`Builds fully new manifest if no baseline parameter passed and present exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -366,7 +719,9 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}); + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -378,11 +733,7 @@ describe('ManifestManager', () => { }, [ENDPOINT_ARTIFACT_LISTS.blocklists.id]: { linux: [blocklistsListItem] }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); @@ -432,7 +783,7 @@ describe('ManifestManager', () => { } }); - test('Reuses artifacts when baseline parameter passed and present exception list items', async () => { + test(`Reuses artifacts when baseline parameter passed and present exception list items when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -450,7 +801,9 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}); + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -459,11 +812,7 @@ describe('ManifestManager', () => { context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + const oldManifest = await manifestManager.buildNewManifest(); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -519,7 +868,7 @@ describe('ManifestManager', () => { } }); - test('Builds fully new manifest with single entries when they are duplicated', async () => { + test(`Builds fully new manifest with single entries when they are duplicated when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -537,7 +886,9 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}); + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const duplicatedEventFilterInDifferentPolicy = { @@ -569,11 +920,7 @@ describe('ManifestManager', () => { macos: [blocklistsListItem, blocklistsListItem], }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, TEST_POLICY_ID_2, @@ -655,7 +1002,7 @@ describe('ManifestManager', () => { } }); - test('Builds manifest with policy specific exception list items for trusted apps', async () => { + test(`Builds manifest with policy specific exception list items for trusted apps when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -668,7 +1015,9 @@ describe('ManifestManager', () => { ], tags: [`policy:${TEST_POLICY_ID_2}`], }); - const context = buildManifestManagerContextMock({}); + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -682,12 +1031,6 @@ describe('ManifestManager', () => { TEST_POLICY_ID_2, ]); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); - const manifest = await manifestManager.buildNewManifest(); expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); @@ -733,7 +1076,7 @@ describe('ManifestManager', () => { }); }); - describe('buildNewManifest when using app features', () => { + describe.each([true, false])('buildNewManifest when using app features', (unifiedManifestSO) => { const SUPPORTED_ARTIFACT_NAMES = [ ARTIFACT_NAME_EXCEPTIONS_MACOS, ARTIFACT_NAME_EXCEPTIONS_WINDOWS, @@ -756,7 +1099,7 @@ describe('ManifestManager', () => { ...new Set(artifacts.map((artifact) => artifact.identifier)).values(), ]; - test('when it has endpoint artifact management app feature it should not generate host isolation exceptions', async () => { + test(`when it has endpoint artifact management app feature it should not generate host isolation exceptions when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -774,9 +1117,10 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}, [ - ProductFeatureSecurityKey.endpointArtifactManagement, - ]); + const context = buildManifestManagerContextMock( + { ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) }, + [ProductFeatureSecurityKey.endpointArtifactManagement] + ); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -788,11 +1132,7 @@ describe('ManifestManager', () => { }, [ENDPOINT_ARTIFACT_LISTS.blocklists.id]: { linux: [blocklistsListItem] }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); @@ -840,7 +1180,7 @@ describe('ManifestManager', () => { } }); - test('when it has endpoint artifact management and response actions app features it should generate all exceptions', async () => { + test(`when it has endpoint artifact management and response actions app features it should generate all exceptions when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -858,10 +1198,13 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}, [ - ProductFeatureSecurityKey.endpointArtifactManagement, - ProductFeatureSecurityKey.endpointResponseActions, - ]); + const context = buildManifestManagerContextMock( + { ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) }, + [ + ProductFeatureSecurityKey.endpointArtifactManagement, + ProductFeatureSecurityKey.endpointResponseActions, + ] + ); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -873,11 +1216,7 @@ describe('ManifestManager', () => { }, [ENDPOINT_ARTIFACT_LISTS.blocklists.id]: { linux: [blocklistsListItem] }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); @@ -927,7 +1266,7 @@ describe('ManifestManager', () => { } }); - test('when does not have right app features, should not generate any exception', async () => { + test(`when does not have right app features, should not generate any exception when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'], @@ -945,7 +1284,10 @@ describe('ManifestManager', () => { os_types: ['macos'], tags: ['policy:all'], }); - const context = buildManifestManagerContextMock({}, []); + const context = buildManifestManagerContextMock( + { ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}) }, + [] + ); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ @@ -957,123 +1299,7 @@ describe('ManifestManager', () => { }, [ENDPOINT_ARTIFACT_LISTS.blocklists.id]: { linux: [blocklistsListItem] }, }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); - context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ - TEST_POLICY_ID_1, - ]); - - const manifest = await manifestManager.buildNewManifest(); - - expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); - expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); - expect(manifest?.getSavedObjectVersion()).toBeUndefined(); - - const artifacts = manifest.getAllArtifacts(); - - expect(artifacts.length).toBe(15); - expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES); - - expect(getArtifactObject(artifacts[0])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[1])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[2])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[3])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[4])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[5])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[8])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[11])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[12])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[13])).toStrictEqual({ entries: [] }); - expect(getArtifactObject(artifacts[14])).toStrictEqual({ entries: [] }); - - for (const artifact of artifacts) { - expect(manifest.isDefaultArtifact(artifact)).toBe(true); - expect(manifest.getArtifactTargetPolicies(artifact)).toStrictEqual( - new Set([TEST_POLICY_ID_1]) - ); - } - }); - }); - - describe('buildNewManifest when Endpoint Exceptions contain `matches`', () => { - test('when contains only `wildcard`, `event.module=endpoint` is added', async () => { - const exceptionListItem = getExceptionListItemSchemaMock({ - os_types: ['macos'], - entries: [ - { type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' }, - { type: 'wildcard', operator: 'excluded', field: 'not_path', value: '*dont_match_me*' }, - ], - }); - const expectedExceptionListItem = getExceptionListItemSchemaMock({ - os_types: ['macos'], - entries: [ - ...exceptionListItem.entries, - { type: 'match', operator: 'included', field: 'event.module', value: 'endpoint' }, - ], - }); - - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - - context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ - [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, - }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); - context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ - TEST_POLICY_ID_1, - ]); - - const manifest = await manifestManager.buildNewManifest(); - - expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); - expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); - expect(manifest?.getSavedObjectVersion()).toBeUndefined(); - - const artifacts = manifest.getAllArtifacts(); - - expect(artifacts.length).toBe(15); - - expect(getArtifactObject(artifacts[0])).toStrictEqual({ - entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'), - }); - }); - - test('when contains anything next to `wildcard`, nothing is added', async () => { - const exceptionListItem = getExceptionListItemSchemaMock({ - os_types: ['macos'], - entries: [ - { type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' }, - { type: 'wildcard', operator: 'excluded', field: 'path', value: '*dont_match_me*' }, - { type: 'match', operator: 'included', field: 'path', value: 'something' }, - ], - }); - const expectedExceptionListItem = getExceptionListItemSchemaMock({ - os_types: ['macos'], - entries: [...exceptionListItem.entries], - }); - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - - context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ - [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, - }); - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => ({ - attributes: object, - })); context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ TEST_POLICY_ID_1, ]); @@ -1087,16 +1313,129 @@ describe('ManifestManager', () => { const artifacts = manifest.getAllArtifacts(); expect(artifacts.length).toBe(15); + expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES); - expect(getArtifactObject(artifacts[0])).toStrictEqual({ - entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'), - }); + expect(getArtifactObject(artifacts[0])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[1])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[2])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[3])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[4])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[5])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[8])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[11])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[12])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[13])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[14])).toStrictEqual({ entries: [] }); + + for (const artifact of artifacts) { + expect(manifest.isDefaultArtifact(artifact)).toBe(true); + expect(manifest.getArtifactTargetPolicies(artifact)).toStrictEqual( + new Set([TEST_POLICY_ID_1]) + ); + } }); }); - describe('deleteArtifacts', () => { - test('Successfully invokes saved objects client', async () => { - const context = buildManifestManagerContextMock({}); + describe.each([true, false])( + 'buildNewManifest when Endpoint Exceptions contain `matches`', + (unifiedManifestSO) => { + test(`when contains only \`wildcard\`, \`event.module=endpoint\` is added when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const exceptionListItem = getExceptionListItemSchemaMock({ + os_types: ['macos'], + entries: [ + { type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' }, + { type: 'wildcard', operator: 'excluded', field: 'not_path', value: '*dont_match_me*' }, + ], + }); + const expectedExceptionListItem = getExceptionListItemSchemaMock({ + os_types: ['macos'], + entries: [ + ...exceptionListItem.entries, + { type: 'match', operator: 'included', field: 'event.module', value: 'endpoint' }, + ], + }); + + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); + const manifestManager = new ManifestManager(context); + + context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ + [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, + }); + + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); + + const manifest = await manifestManager.buildNewManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); + expect(manifest?.getSavedObjectVersion()).toBeUndefined(); + + const artifacts = manifest.getAllArtifacts(); + + expect(artifacts.length).toBe(15); + + expect(getArtifactObject(artifacts[0])).toStrictEqual({ + entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'), + }); + }); + + test(`when contains anything next to \`wildcard\`, nothing is added when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const exceptionListItem = getExceptionListItemSchemaMock({ + os_types: ['macos'], + entries: [ + { type: 'wildcard', operator: 'included', field: 'path', value: '*match_me*' }, + { type: 'wildcard', operator: 'excluded', field: 'path', value: '*dont_match_me*' }, + { type: 'match', operator: 'included', field: 'path', value: 'something' }, + ], + }); + const expectedExceptionListItem = getExceptionListItemSchemaMock({ + os_types: ['macos'], + entries: [...exceptionListItem.entries], + }); + + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); + const manifestManager = new ManifestManager(context); + + context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ + [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, + }); + + context.packagePolicyService.fetchAllItemIds = getMockPolicyFetchAllItemIds([ + TEST_POLICY_ID_1, + ]); + + const manifest = await manifestManager.buildNewManifest(); + + expect(manifest?.getSchemaVersion()).toStrictEqual('v1'); + expect(manifest?.getSemanticVersion()).toStrictEqual('1.0.0'); + expect(manifest?.getSavedObjectVersion()).toBeUndefined(); + + const artifacts = manifest.getAllArtifacts(); + + expect(artifacts.length).toBe(15); + + expect(getArtifactObject(artifacts[0])).toStrictEqual({ + entries: translateToEndpointExceptions([expectedExceptionListItem], 'v1'), + }); + }); + } + ); + + describe.each([true, false])('deleteArtifacts', (unifiedManifestSO) => { + test(`Successfully invokes saved objects client when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); await expect( @@ -1112,8 +1451,10 @@ describe('ManifestManager', () => { ]); }); - test('Returns errors for partial failures', async () => { - const context = buildManifestManagerContextMock({}); + test(`Returns errors for partial failures when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const artifactClient = context.artifactClient as jest.Mocked; const manifestManager = new ManifestManager(context); const error = new Error(); @@ -1140,9 +1481,11 @@ describe('ManifestManager', () => { }); }); - describe('pushArtifacts', () => { - test('Successfully invokes artifactClient', async () => { - const context = buildManifestManagerContextMock({}); + describe.each([true, false])('pushArtifacts', (unifiedManifestSO) => { + test(`Successfully invokes artifactClient when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const artifactClient = context.artifactClient as jest.Mocked; const manifestManager = new ManifestManager(context); const newManifest = ManifestManager.createDefaultManifest(); @@ -1164,8 +1507,10 @@ describe('ManifestManager', () => { ]); }); - test('Returns errors for partial failures', async () => { - const context = buildManifestManagerContextMock({}); + test(`Returns errors for partial failures when unifiedManifestEnabled feature flag is set to: ${unifiedManifestSO}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedManifestSO ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const artifactClient = context.artifactClient as jest.Mocked; const manifestManager = new ManifestManager(context); const newManifest = ManifestManager.createDefaultManifest(); @@ -1206,114 +1551,16 @@ describe('ManifestManager', () => { }); }); - describe('commit', () => { - test('Creates new saved object if no saved object version', async () => { - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - const manifest = ManifestManager.createDefaultManifest(); - - manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); - manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); - manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); - manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); - manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); - - context.savedObjectsClient.create = jest - .fn() - .mockImplementation((_type: string, object: InternalManifestSchema) => object); - - await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); - - expect(context.savedObjectsClient.create).toHaveBeenCalledTimes(1); - expect(context.savedObjectsClient.create).toHaveBeenNthCalledWith( - 1, - ManifestConstants.SAVED_OBJECT_TYPE, - { - artifacts: [ - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 }, - ], - schemaVersion: 'v1', - semanticVersion: '1.0.0', - created: expect.anything(), - }, - { id: 'endpoint-manifest-v1' } - ); - }); - - test('Updates existing saved object if has saved object version', async () => { - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - const manifest = new Manifest({ soVersion: '1.0.0' }); - - manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS); - manifest.addEntry(ARTIFACT_EXCEPTIONS_MACOS, TEST_POLICY_ID_1); - manifest.addEntry(ARTIFACT_EXCEPTIONS_WINDOWS, TEST_POLICY_ID_2); - manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_1); - manifest.addEntry(ARTIFACT_TRUSTED_APPS_MACOS, TEST_POLICY_ID_2); - - context.savedObjectsClient.update = jest - .fn() - .mockImplementation((_type: string, _id: string, object: InternalManifestSchema) => object); - - await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); - - expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1); - expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith( - 1, - ManifestConstants.SAVED_OBJECT_TYPE, - 'endpoint-manifest-v1', - { - artifacts: [ - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_2 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_2 }, - ], - schemaVersion: 'v1', - semanticVersion: '1.0.0', - }, - { version: '1.0.0' } - ); - }); - - test('Throws error when saved objects client fails', async () => { - const context = buildManifestManagerContextMock({}); - const manifestManager = new ManifestManager(context); - const manifest = new Manifest({ soVersion: '1.0.0' }); - const error = new Error(); - - context.savedObjectsClient.update = jest.fn().mockRejectedValue(error); - - await expect(manifestManager.commit(manifest)).rejects.toBe(error); - - expect(context.savedObjectsClient.update).toHaveBeenCalledTimes(1); - expect(context.savedObjectsClient.update).toHaveBeenNthCalledWith( - 1, - ManifestConstants.SAVED_OBJECT_TYPE, - 'endpoint-manifest-v1', - { - artifacts: [], - schemaVersion: 'v1', - semanticVersion: '1.0.0', - }, - { version: '1.0.0' } - ); - }); - }); - - describe('tryDispatch', () => { + describe.each([true, false])('tryDispatch', (unifiedSavedObject) => { const getMockPolicyFetchAllItems = (items: PackagePolicy[]) => jest.fn(async function* () { yield items; }); - test('Should not dispatch if no policies', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should not dispatch if no policies when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0' }); @@ -1325,8 +1572,10 @@ describe('ManifestManager', () => { expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0); }); - test('Should return errors if invalid config for package policy', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should return errors if invalid config for package policy when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0' }); @@ -1343,8 +1592,10 @@ describe('ManifestManager', () => { expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0); }); - test('Should not dispatch if semantic version has not changed', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should not dispatch if semantic version has not changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0' }); @@ -1372,8 +1623,10 @@ describe('ManifestManager', () => { expect(context.packagePolicyService.bulkUpdate).toHaveBeenCalledTimes(0); }); - test('Should dispatch to only policies where list of artifacts changed', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should dispatch to only policies where list of artifacts changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0', semanticVersion: '1.0.1' }); @@ -1441,8 +1694,10 @@ describe('ManifestManager', () => { ); }); - test('Should dispatch to only policies where artifact content changed', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should dispatch to only policies where artifact content changed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const manifest = new Manifest({ soVersion: '1.0.0', semanticVersion: '1.0.1' }); @@ -1512,8 +1767,10 @@ describe('ManifestManager', () => { ); }); - test('Should return partial errors', async () => { - const context = buildManifestManagerContextMock({}); + test(`Should return partial errors when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); const error = new Error(); @@ -1556,9 +1813,11 @@ describe('ManifestManager', () => { }); }); - describe('cleanup artifacts', () => { - test('Successfully removes orphan artifacts', async () => { - const context = buildManifestManagerContextMock({}); + describe.each([true, false])('cleanup artifacts', (unifiedSavedObject) => { + test(`Successfully removes orphan artifacts when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); (context.artifactClient.fetchAll as jest.Mock).mockReturnValue( @@ -1586,8 +1845,10 @@ describe('ManifestManager', () => { ]); }); - test('When there is no artifact to be removed', async () => { - const context = buildManifestManagerContextMock({}); + test(`When there is no artifact to be removed when unifiedManifestEnabled feature flag is set to: ${unifiedSavedObject}`, async () => { + const context = buildManifestManagerContextMock({ + ...(unifiedSavedObject ? { experimentalFeatures: ['unifiedManifestEnabled'] } : {}), + }); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({}); @@ -1625,4 +1886,336 @@ describe('ManifestManager', () => { expect(context.artifactClient.bulkDeleteArtifacts).toHaveBeenCalledTimes(0); }); }); + + describe('Unified Manifest Methods', () => { + let manifestManager: ManifestManager; + let context: ManifestManagerContext; + + beforeEach(() => { + context = buildManifestManagerContextMock({}); + manifestManager = new ManifestManager(context); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('transforms', () => { + const createLegacyManifestSO = ( + opts: { + semanticVersion?: string; + } = {} + ) => ({ + attributes: { + schemaVersion: 'v1' as const, + semanticVersion: opts.semanticVersion ?? '1.0.0', + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 }, + ], + }, + version: 'WzQ3NzAsMV0=', + }); + + const createUnifiedManifestSO = (globalSemanticVersion?: string) => [ + { + policyId: '.global', + semanticVersion: globalSemanticVersion ?? '1.0.0', + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_MACOS, + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_EXCEPTIONS_LINUX, + ], + ...(globalSemanticVersion ? { created: '20-01-2020 10:00:00.000Z' } : {}), + id: '3', + }, + { + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.0', + artifactIds: [ + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_TRUSTED_APPS_MACOS, + ARTIFACT_ID_TRUSTED_APPS_WINDOWS, + ], + ...(globalSemanticVersion ? { created: '20-01-2020 10:00:00.000Z' } : {}), + id: '1', + }, + { + policyId: TEST_POLICY_ID_2, + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_TRUSTED_APPS_WINDOWS], + ...(globalSemanticVersion ? { created: '20-01-2020 10:00:00.000Z' } : {}), + id: '2', + }, + ]; + + describe('transformUnifiedManifestSOtoLegacyManifestSO', () => { + const unifiedManifestSO = createUnifiedManifestSO('1.0.5'); + test('should transform unified manifest saved object to legacy manifest saved object', async () => { + const expectedLegacyManifestSO = createLegacyManifestSO({ semanticVersion: '1.0.5' }); + + expect( + manifestManager.transformUnifiedManifestSOtoLegacyManifestSO( + unifiedManifestSO as InternalUnifiedManifestSchema[] + ) + ).toEqual(expectedLegacyManifestSO); + }); + + test('should return empty artifacts array when unified manifest saved object is empty', async () => { + const emptyUnifiedManifestSO: InternalUnifiedManifestSchema[] = []; + const expectedEmptyLegacyManifestSO = createLegacyManifestSO(); + expect( + manifestManager.transformUnifiedManifestSOtoLegacyManifestSO(emptyUnifiedManifestSO) + ).toEqual({ + ...expectedEmptyLegacyManifestSO, + attributes: { ...expectedEmptyLegacyManifestSO.attributes, artifacts: [] }, + }); + }); + + test('should return empty artifacts array when unified manifest saved object is empty but semanticVersion was provided', async () => { + const semanticVersion = '1.14.0'; + const emptyUnifiedManifestSO: InternalUnifiedManifestSchema[] = []; + const expectedEmptyLegacyManifestSO = createLegacyManifestSO(); + expect( + manifestManager.transformUnifiedManifestSOtoLegacyManifestSO( + emptyUnifiedManifestSO, + semanticVersion + ) + ).toEqual({ + ...expectedEmptyLegacyManifestSO, + attributes: { + ...expectedEmptyLegacyManifestSO.attributes, + artifacts: [], + semanticVersion, + }, + }); + }); + }); + describe('transformLegacyManifestSOtoUnifiedManifestSO', () => { + const unifiedManifestSO = createUnifiedManifestSO(); + const expectedLegacyManifestSO = createLegacyManifestSO().attributes; + test('should properly transform legacy manifest to unified manifest saved object with empty exising unified manifest so', async () => { + expect( + manifestManager.transformLegacyManifestSOtoUnifiedManifestSO( + expectedLegacyManifestSO, + [] + ) + ).toEqual(unifiedManifestSO.map((item) => ({ ...item, id: undefined }))); + }); + + test('should properly transform legacy manifest to unified manifest saved object with empty exising unified manifest so and propagate semanticVersion from the manifest', async () => { + const semanticVersion = '1.14.0'; + const expectedLegacyManifestSOWithSemanticVersion = createLegacyManifestSO({ + semanticVersion, + }).attributes; + const unifiedManifestSOWithSemanticVersion = createUnifiedManifestSO(semanticVersion); + + expect( + manifestManager.transformLegacyManifestSOtoUnifiedManifestSO( + expectedLegacyManifestSOWithSemanticVersion, + [] + ) + ).toEqual( + unifiedManifestSOWithSemanticVersion.map((item) => ({ + ...item, + id: undefined, + created: undefined, + })) + ); + }); + + test('should properly transform legacy manifest to unified manifest saved object with existing unified manifest so', async () => { + const createUnifiedManifests = (empty = false) => [ + { + policyId: '.global', + semanticVersion: '1.0.2', + artifactIds: !empty + ? [ + ARTIFACT_ID_EXCEPTIONS_MACOS, + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_EXCEPTIONS_LINUX, + ] + : [], + id: '3', + ...(empty ? { created: '2000' } : {}), + }, + { + policyId: TEST_POLICY_ID_1, + semanticVersion: '1.0.5', + artifactIds: !empty + ? [ + ARTIFACT_ID_EXCEPTIONS_WINDOWS, + ARTIFACT_ID_TRUSTED_APPS_MACOS, + ARTIFACT_ID_TRUSTED_APPS_WINDOWS, + ] + : [], + id: '1', + ...(empty ? { created: '2000' } : {}), + }, + { + policyId: TEST_POLICY_ID_2, + semanticVersion: '1.0.1', + artifactIds: !empty ? [ARTIFACT_ID_TRUSTED_APPS_WINDOWS] : [], + id: '2', + ...(empty ? { created: '2000' } : {}), + }, + ]; + + const existingUnifiedManifest = createUnifiedManifests(true); + const output = createUnifiedManifests(); + + const legacyManifest = createLegacyManifestSO().attributes; + + expect( + manifestManager.transformLegacyManifestSOtoUnifiedManifestSO( + legacyManifest, + existingUnifiedManifest as InternalUnifiedManifestSchema[] + ) + ).toEqual(output); + }); + }); + }); + + describe('prepareUnifiedManifestsSOUpdates', () => { + const existingUnifiedManifests = ['.global', TEST_POLICY_ID_1, TEST_POLICY_ID_2].map( + (policyId, idx) => ({ + policyId, + semanticVersion: '1.0.0', + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS], + id: `${idx}`, + created: '1', + version: 'abc', + }) + ); + + const bumpSemanticVersion = ( + manifests: Array>, + semanticVersion = '1.0.1' + ) => + manifests.map((manifest) => ({ + ...manifest, + semanticVersion, + })); + + test('correctly selects manifests to create', () => { + const unifiedManifest = existingUnifiedManifests.map( + ({ id, created, ...manifest }) => manifest + ); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + manifestManager.prepareUnifiedManifestsSOUpdates(unifiedManifest, []); + + expect(unifiedManifestsToUpdate).toEqual([]); + expect(unifiedManifestsToCreate).toEqual(unifiedManifest); + expect(unifiedManifestsToDelete).toEqual([]); + }); + test('correctly selects manifests to delete', () => { + const newUnifiedManifests = existingUnifiedManifests.slice(0, 2); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + manifestManager.prepareUnifiedManifestsSOUpdates( + newUnifiedManifests, + existingUnifiedManifests + ); + + expect(unifiedManifestsToUpdate).toEqual([]); + expect(unifiedManifestsToCreate).toEqual([]); + expect(unifiedManifestsToDelete).toEqual([existingUnifiedManifests[2].id]); + }); + test('correctly selects manifests to update when artifactIds changed', () => { + const newUnifiedManifests = existingUnifiedManifests.map((manifest) => ({ + ...manifest, + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS, ARTIFACT_ID_EXCEPTIONS_WINDOWS], + })); + + const expectedUnifiedManifestsToUpdate = bumpSemanticVersion(newUnifiedManifests); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + manifestManager.prepareUnifiedManifestsSOUpdates( + newUnifiedManifests, + existingUnifiedManifests + ); + + expect(unifiedManifestsToUpdate).toEqual(expectedUnifiedManifestsToUpdate); + expect(unifiedManifestsToCreate).toEqual([]); + expect(unifiedManifestsToDelete).toEqual([]); + }); + + test('correctly combines all cases', () => { + const newUnifiedManifests = existingUnifiedManifests.slice(0, 2).map((manifest) => ({ + ...manifest, + artifactIds: [ARTIFACT_ID_EXCEPTIONS_WINDOWS, ARTIFACT_ID_EXCEPTIONS_WINDOWS], + })); + + const newUnifiedManifestsAddition = { + policyId: 'test', + semanticVersion: '1.0.0', + artifactIds: [], + }; + + const expectedUnifiedManifestsToUpdate = bumpSemanticVersion(newUnifiedManifests); + + const expectedUnifiedManifestsToCreate = [newUnifiedManifestsAddition]; + + const expectedUnifiedManifestsToDelete = [existingUnifiedManifests[2].id]; + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + manifestManager.prepareUnifiedManifestsSOUpdates( + [...newUnifiedManifests, newUnifiedManifestsAddition], + existingUnifiedManifests + ); + + expect(unifiedManifestsToUpdate).toEqual(expectedUnifiedManifestsToUpdate); + expect(unifiedManifestsToCreate).toEqual(expectedUnifiedManifestsToCreate); + expect(unifiedManifestsToDelete).toEqual(expectedUnifiedManifestsToDelete); + }); + }); + describe('bumpGlobalUnifiedManifestVersion', () => { + const createSoFindMock = (savedObjects: Array>) => + jest.fn().mockImplementation(async (objectType: { type: string }) => { + if (objectType.type === ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE) { + return { + saved_objects: savedObjects, + }; + } else { + return null; + } + }); + + test('should bump the semantic version of the global manifest', async () => { + context.savedObjectsClient.find = createSoFindMock([ + { + id: '1', + attributes: { + policyId: '.global', + semanticVersion: '1.0.1', + }, + }, + ]); + context.savedObjectsClient.bulkUpdate = jest.fn(); + await manifestManager.bumpGlobalUnifiedManifestVersion(); + expect(context.savedObjectsClient.bulkUpdate).toHaveBeenCalledWith([ + { + id: '1', + type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE, + attributes: { + policyId: '.global', + semanticVersion: '1.0.2', + }, + }, + ]); + }); + test('should make a clean return when no global manifest is found', async () => { + context.savedObjectsClient.find = createSoFindMock([]); + context.savedObjectsClient.bulkUpdate = jest.fn(); + await manifestManager.bumpGlobalUnifiedManifestVersion(); + expect(context.savedObjectsClient.bulkUpdate).toHaveBeenCalledTimes(0); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts index 663318c7459a3..3a6cfc5be280c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts @@ -6,15 +6,17 @@ */ import semver from 'semver'; -import { isEmpty, isEqual, keyBy } from 'lodash'; +import { chunk, isEmpty, isEqual, keyBy } from 'lodash'; import type { ElasticsearchClient } from '@kbn/core/server'; import { type Logger, type SavedObjectsClientContract } from '@kbn/core/server'; -import { ENDPOINT_LIST_ID, ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants'; +import { ENDPOINT_ARTIFACT_LISTS, ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; import type { PackagePolicy } from '@kbn/fleet-plugin/common'; import type { Artifact, PackagePolicyClient } from '@kbn/fleet-plugin/server'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { ProductFeatureKey } from '@kbn/security-solution-features/keys'; +import { asyncForEach } from '@kbn/std'; +import { UnifiedManifestClient } from '../unified_manifest_client'; import { stringify } from '../../../utils/stringify'; import { QueueProcessor } from '../../../utils/queue_processor'; import type { ProductFeaturesService } from '../../../../lib/product_features_service/product_features_service'; @@ -35,9 +37,15 @@ import { Manifest, } from '../../../lib/artifacts'; +import type { + InternalUnifiedManifestBaseSchema, + InternalUnifiedManifestSchema, + InternalUnifiedManifestUpdateSchema, +} from '../../../schemas/artifacts'; import { internalArtifactCompleteSchema, type InternalArtifactCompleteSchema, + type InternalManifestSchema, type WrappedTranslatedExceptionList, } from '../../../schemas/artifacts'; import type { EndpointArtifactClientInterface } from '../artifact_client'; @@ -513,7 +521,25 @@ export class ManifestManager { */ public async getLastComputedManifest(): Promise { try { - const manifestSo = await this.getManifestClient().getManifest(); + let manifestSo; + if (this.experimentalFeatures.unifiedManifestEnabled) { + const unifiedManifestsSo = await this.getAllUnifiedManifestsSO(); + // On first run, there will be no existing Unified Manifests SO, so we need to copy the semanticVersion from the legacy manifest + // This is to ensure that the first Unified Manifest created has the same semanticVersion as the legacy manifest and is not too far + // behind for package policy to pick it up. + if (unifiedManifestsSo.length === 0) { + const legacyManifestSo = await this.getManifestClient().getManifest(); + const legacySemanticVersion = legacyManifestSo?.attributes?.semanticVersion; + manifestSo = this.transformUnifiedManifestSOtoLegacyManifestSO( + unifiedManifestsSo, + legacySemanticVersion + ); + } else { + manifestSo = this.transformUnifiedManifestSOtoLegacyManifestSO(unifiedManifestsSo); + } + } else { + manifestSo = await this.getManifestClient().getManifest(); + } if (manifestSo.version === undefined) { throw new InvalidInternalManifestError( @@ -721,21 +747,25 @@ export class ManifestManager { * @returns {Promise} An error, if encountered, or null. */ public async commit(manifest: Manifest) { - const manifestClient = this.getManifestClient(); - - // Commit the new manifest const manifestSo = manifest.toSavedObject(); - const version = manifest.getSavedObjectVersion(); - if (version == null) { - await manifestClient.createManifest(manifestSo); + if (this.experimentalFeatures.unifiedManifestEnabled) { + await this.commitUnified(manifestSo); } else { - await manifestClient.updateManifest(manifestSo, { - version, - }); - } + const manifestClient = this.getManifestClient(); - this.logger.debug(`Committed manifest ${manifest.getSemanticVersion()}`); + const version = manifest.getSavedObjectVersion(); + + if (version == null) { + await manifestClient.createManifest(manifestSo); + } else { + await manifestClient.updateManifest(manifestSo, { + version, + }); + } + + this.logger.debug(`Committed manifest ${manifest.getSemanticVersion()}`); + } } private fetchAllPolicies(): AsyncIterable { @@ -835,4 +865,229 @@ export class ManifestManager { ); } } + + /** + * Unified Manifest methods + */ + + private setNewSemanticVersion(semanticVersion: string): string | null { + const newSemanticVersion = semver.inc(semanticVersion, 'patch'); + if (!semver.valid(newSemanticVersion)) { + throw new Error(`Invalid semver: ${newSemanticVersion}`); + } + return newSemanticVersion; + } + + protected getUnifiedManifestClient(): UnifiedManifestClient { + return new UnifiedManifestClient(this.savedObjectsClient); + } + + public async getAllUnifiedManifestsSO(): Promise { + return this.getUnifiedManifestClient().getAllUnifiedManifests(); + } + + public transformUnifiedManifestSOtoLegacyManifestSO( + unifiedManifestsSo: InternalUnifiedManifestSchema[], + semanticVersion?: string + ): { + version: string; + attributes: { + artifacts: Array< + { artifactId: string; policyId: undefined } | { artifactId: string; policyId: string } + >; + semanticVersion: string; + schemaVersion: ManifestSchemaVersion; + }; + } { + const globalUnifiedManifest = unifiedManifestsSo.find((a) => a.policyId === '.global'); + return { + version: 'WzQ3NzAsMV0=', // version is hardcoded since it was used only to determine whether to create a new manifest or update an existing one + attributes: { + artifacts: [ + ...(globalUnifiedManifest?.artifactIds.map((artifactId) => ({ + artifactId, + policyId: undefined, + })) ?? []), + ...unifiedManifestsSo.reduce( + (acc: Array<{ artifactId: string; policyId: string }>, unifiedManifest) => { + if (unifiedManifest.policyId === '.global') { + return acc; + } + acc.push( + ...unifiedManifest.artifactIds.map((artifactId) => ({ + policyId: unifiedManifest.policyId, + artifactId, + })) + ); + + return acc; + }, + [] + ), + ], + semanticVersion: (semanticVersion || globalUnifiedManifest?.semanticVersion) ?? '1.0.0', + schemaVersion: this.schemaVersion, + }, + }; + } + + public transformLegacyManifestSOtoUnifiedManifestSO( + manifestSo: InternalManifestSchema, + unifiedManifestsSo: InternalUnifiedManifestSchema[] + ): Array { + const manifestObject = manifestSo.artifacts.reduce( + ( + acc: Record, + { artifactId, policyId = '.global' } + ) => { + const existingPolicy = acc[policyId]; + if (existingPolicy) { + existingPolicy.artifactIds.push(artifactId); + } else { + const existingUnifiedManifestSo = unifiedManifestsSo.find( + (item) => item.policyId === policyId + ); + + // On first run, there will be no existing Unified Manifests SO, so we need to copy the semanticVersion from the legacy manifest + // This is to ensure that the first Unified Manifest created has the same semanticVersion as the legacy manifest and is not too far + // behind for package policy to pick it up. + const semanticVersion = + (policyId === '.global' && !unifiedManifestsSo.length + ? manifestSo?.semanticVersion + : existingUnifiedManifestSo?.semanticVersion) ?? '1.0.0'; + + acc[policyId] = { + policyId, + artifactIds: [artifactId], + semanticVersion, + id: existingUnifiedManifestSo?.id, + }; + } + return acc; + }, + {} + ); + return Object.values(manifestObject); + } + + public prepareUnifiedManifestsSOUpdates( + unifiedManifestsSo: Array & { id?: string }>, + existingUnifiedManifestsSo: InternalUnifiedManifestSchema[] + ) { + const existingManifestsObj: Record = {}; + existingUnifiedManifestsSo.forEach((manifest) => { + existingManifestsObj[manifest.id] = manifest; + }); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate } = unifiedManifestsSo.reduce( + ( + acc: { + unifiedManifestsToUpdate: InternalUnifiedManifestUpdateSchema[]; + unifiedManifestsToCreate: InternalUnifiedManifestBaseSchema[]; + }, + unifiedManifest + ) => { + if (unifiedManifest.id !== undefined) { + // Manifest with id exists in SO, check if it needs to be updated + const existingUnifiedManifest = existingManifestsObj[unifiedManifest.id]; + // Update SO if the artifactIds changed. + if (!isEqual(existingUnifiedManifest.artifactIds, unifiedManifest.artifactIds)) { + acc.unifiedManifestsToUpdate.push({ + ...unifiedManifest, + semanticVersion: this.setNewSemanticVersion(unifiedManifest.semanticVersion), + version: existingUnifiedManifest.version, + } as InternalUnifiedManifestUpdateSchema); + } + } else { + // Manifest with id does not exist in SO, create new SO + acc.unifiedManifestsToCreate.push(unifiedManifest); + } + + return acc; + }, + { unifiedManifestsToUpdate: [], unifiedManifestsToCreate: [] } + ); + + const unifiedManifestsToDelete = existingUnifiedManifestsSo.reduce( + (acc: string[], { policyId, id }) => { + const existingPolicy = unifiedManifestsSo.find((item) => item.policyId === policyId); + if (!existingPolicy) { + acc.push(id); + } + return acc; + }, + [] + ); + + return { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete }; + } + + public async bumpGlobalUnifiedManifestVersion(): Promise { + const globalUnifiedManifestSO = + await this.getUnifiedManifestClient().getUnifiedManifestByPolicyId('.global'); + if (!globalUnifiedManifestSO?.saved_objects?.length) { + this.logger.warn('No Global Unified Manifest found to bump version'); + return; + } + const globalUnifiedManifest = globalUnifiedManifestSO.saved_objects[0]; + + const newSemanticVersion = + this.setNewSemanticVersion(globalUnifiedManifest.attributes.semanticVersion) || '1.0.0'; + await this.getUnifiedManifestClient().updateUnifiedManifest({ + ...globalUnifiedManifest.attributes, + id: globalUnifiedManifest.id, + semanticVersion: newSemanticVersion, + }); + } + + public async commitUnified(manifestSo: InternalManifestSchema): Promise { + const existingUnifiedManifestsSo = await this.getAllUnifiedManifestsSO(); + + const unifiedManifestSO = this.transformLegacyManifestSOtoUnifiedManifestSO( + manifestSo, + existingUnifiedManifestsSo + ); + + const { unifiedManifestsToUpdate, unifiedManifestsToCreate, unifiedManifestsToDelete } = + this.prepareUnifiedManifestsSOUpdates(unifiedManifestSO, existingUnifiedManifestsSo); + + if (unifiedManifestsToCreate.length) { + await asyncForEach(chunk(unifiedManifestsToCreate, 100), async (unifiedManifestsBatch) => { + await this.getUnifiedManifestClient().createUnifiedManifests(unifiedManifestsBatch); + }); + this.logger.debug(`Created ${unifiedManifestsToCreate.length} unified manifests`); + } + + if (unifiedManifestsToUpdate.length) { + await asyncForEach(chunk(unifiedManifestsToUpdate, 100), async (unifiedManifestsBatch) => { + await this.getUnifiedManifestClient().updateUnifiedManifests(unifiedManifestsBatch); + }); + + this.logger.debug(`Updated ${unifiedManifestsToUpdate.length} unified manifests`); + } + + if (unifiedManifestsToDelete.length) { + await asyncForEach(chunk(unifiedManifestsToDelete, 100), async (unifiedManifestsBatch) => { + await this.getUnifiedManifestClient().deleteUnifiedManifestByIds(unifiedManifestsBatch); + }); + + this.logger.debug(`Deleted ${unifiedManifestsToDelete.length} unified manifests`); + } + + if ( + unifiedManifestsToCreate.length || + unifiedManifestsToUpdate.length || + unifiedManifestsToDelete.length + ) { + // If global manifest is not in the list of manifests to create or update, we need to bump its version + // We use it to set schemaVersion of the legacy manifest we are going to create so that its being picked up when populating agent policy + const hasGlobalManifest = [...unifiedManifestsToCreate, ...unifiedManifestsToUpdate].some( + (manifest) => manifest.policyId === '.global' + ); + + if (!hasGlobalManifest || unifiedManifestsToDelete.length) { + await this.bumpGlobalUnifiedManifestVersion(); + } + } + } } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifes_client.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifes_client.test.ts index 83a37e617caf7..4fde69946ccff 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifes_client.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifes_client.test.ts @@ -73,8 +73,7 @@ describe('unified_manifest_client', () => { test('can get unified manifest by id', async () => { await unifiedManifestClient.getUnifiedManifestById('123'); expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith( - expect.arrayContaining([mockSoClientCallParams({ id: '123' }, false)]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + expect.arrayContaining([mockSoClientCallParams({ id: '123' }, false)]) ); }); @@ -84,8 +83,7 @@ describe('unified_manifest_client', () => { expect.arrayContaining([ mockSoClientCallParams({ id: '123' }, false), mockSoClientCallParams({ id: '456' }, false), - ]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + ]) ); }); @@ -114,17 +112,14 @@ describe('unified_manifest_client', () => { ...mockUnifiedManifestAttributes({ policyId: `policy-${i}` }), id: `id-${i}`, created: '1', + version: '1', }; }) ); - const cbFunc = jest.fn(); - await unifiedManifestClient.getAllUnifiedManifests(cbFunc); + const result = await unifiedManifestClient.getAllUnifiedManifests(); - expect(cbFunc).toHaveBeenCalledTimes(3); - expect(cbFunc).toHaveBeenLastCalledWith([ - expect.objectContaining({ policyId: 'policy-2000', id: 'id-2000' }), - ]); + expect(result.length).toBe(2001); }); }); @@ -140,8 +135,7 @@ describe('unified_manifest_client', () => { expect(savedObjectsClient.bulkUpdate).toHaveBeenCalledWith( expect.arrayContaining([ mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false), - ]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + ]) ); }); test('can update unified manifests', async () => { @@ -153,8 +147,7 @@ describe('unified_manifest_client', () => { expect.arrayContaining([ mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false), mockSoClientCallParams({ id: '1234', version: 'abcd' }, true, false), - ]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + ]) ); }); }); @@ -162,8 +155,7 @@ describe('unified_manifest_client', () => { test('can delete unified manifest', async () => { await unifiedManifestClient.deleteUnifiedManifestById('123'); expect(savedObjectsClient.bulkDelete).toHaveBeenCalledWith( - expect.arrayContaining([{ id: '123', type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + expect.arrayContaining([{ id: '123', type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE }]) ); }); test('can delete unified manifests', async () => { @@ -172,8 +164,7 @@ describe('unified_manifest_client', () => { expect.arrayContaining([ mockSoClientCallParams({ id: '123' }, false), mockSoClientCallParams({ id: '456' }, false), - ]), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + ]) ); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifest_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifest_client.ts index db47c43b68607..d84742ed65f10 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifest_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/unified_manifest_client.ts @@ -86,20 +86,19 @@ export class UnifiedManifestClient { manifestIds: string[] ): Promise> { return this.savedObjectsClient.bulkGet( - manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })) ); } public async getAllUnifiedManifests( - cb: (unifiedManifests: InternalUnifiedManifestSchema[]) => void | Promise, options?: FetchAllUnifiedManifestsOptions - ): Promise { + ): Promise { const unifiedManifestsFetcher = this.fetchAllUnifiedManifests(this.savedObjectsClient, options); - + const allUnifiedManifests: InternalUnifiedManifestSchema[] = []; for await (const unifiedManifests of unifiedManifestsFetcher) { - await cb(unifiedManifests); + allUnifiedManifests.push(...unifiedManifests); } + return allUnifiedManifests; } /** @@ -127,8 +126,7 @@ export class UnifiedManifestClient { attributes, ...(version ? { version } : {}), }; - }), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + }) ); } @@ -144,8 +142,7 @@ export class UnifiedManifestClient { manifestIds: string[] ): Promise { return this.savedObjectsClient.bulkDelete( - manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })), - { namespace: UNIFIED_MANIFEST_ALL_NAMESPACES } + manifestIds.map((id) => ({ id, type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE })) ); } @@ -160,7 +157,7 @@ export class UnifiedManifestClient { fields = [], kuery, sortOrder = 'asc', - sortField = 'created', + sortField = 'created_at', }: FetchAllUnifiedManifestsOptions = {} ): AsyncIterable { return createSoFindIterable({ diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/utils.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/utils.ts index f52eafbdc4529..de1484bf155a1 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/utils.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/utils.ts @@ -16,6 +16,7 @@ export const mapUnifiedManifestSavedObjectToUnifiedManifest = ({ attributes: { artifactIds, policyId, semanticVersion }, // eslint-disable-next-line @typescript-eslint/naming-convention created_at, + version, }: SavedObject): InternalUnifiedManifestSchema => { return { id, @@ -23,5 +24,6 @@ export const mapUnifiedManifestSavedObjectToUnifiedManifest = ({ semanticVersion, created: created_at, artifactIds, + version, }; }; diff --git a/x-pack/plugins/security_solution/server/saved_objects.ts b/x-pack/plugins/security_solution/server/saved_objects.ts index 0b1fec5677488..3659b15a04714 100644 --- a/x-pack/plugins/security_solution/server/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/saved_objects.ts @@ -13,7 +13,7 @@ import { noteType, pinnedEventType, timelineType } from './lib/timeline/saved_ob import { legacyType as legacyRuleActionsType } from './lib/detection_engine/rule_actions_legacy'; import { prebuiltRuleAssetType } from './lib/detection_engine/prebuilt_rules'; import { type as signalsMigrationType } from './lib/detection_engine/migrations/saved_objects'; -import { manifestType } from './endpoint/lib/artifacts/saved_object_mappings'; +import { manifestType, unifiedManifestType } from './endpoint/lib/artifacts/saved_object_mappings'; import { riskEngineConfigurationType } from './lib/entity_analytics/risk_engine/saved_object'; const types = [ @@ -23,6 +23,7 @@ const types = [ prebuiltRuleAssetType, timelineType, manifestType, + unifiedManifestType, signalsMigrationType, riskEngineConfigurationType, protectionUpdatesNoteType, diff --git a/x-pack/test/security_solution_endpoint/apps/integrations/artifact_entries_list.ts b/x-pack/test/security_solution_endpoint/apps/integrations/artifact_entries_list.ts index 58ffde0d7611f..6c78dd995673f 100644 --- a/x-pack/test/security_solution_endpoint/apps/integrations/artifact_entries_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/integrations/artifact_entries_list.ts @@ -30,7 +30,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'artifactEntriesList']); const testSubjects = getService('testSubjects'); const browser = getService('browser'); - const endpointArtifactsTestResources = getService('endpointArtifactTestResources'); + const endpointArtifactTestResources = getService('endpointArtifactTestResources'); const endpointTestResources = getService('endpointTestResources'); const retry = getService('retry'); const esClient = getService('es'); @@ -76,7 +76,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Check edited artifact is in the list with new values (wait for list to be updated) let updatedArtifact: ArtifactElasticsearchProperties | undefined; await retry.waitForWithTimeout('fleet artifact is updated', 120_000, async () => { - const artifacts = await endpointArtifactsTestResources.getArtifacts(); + const artifacts = await endpointArtifactTestResources.getArtifacts(); const manifestArtifact = artifacts.find((artifact) => { return ( diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/artifact_entries_list.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/artifact_entries_list.ts new file mode 100644 index 0000000000000..46e333e10779d --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/artifact_entries_list.ts @@ -0,0 +1,376 @@ +/* + * 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 { unzip } from 'zlib'; +import { promisify } from 'util'; +import expect from '@kbn/expect'; +import { IndexedHostsAndAlertsResponse } from '@kbn/security-solution-plugin/common/endpoint/index_data'; +import { + ENDPOINT_ARTIFACT_LIST_IDS, + EXCEPTION_LIST_URL, +} from '@kbn/securitysolution-list-constants'; +import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { + ArtifactBodyType, + getArtifactsListTestsData, + ArtifactActionsType, + AgentPolicyResponseType, + getCreateMultipleData, + MultipleArtifactActionsType, +} from './mocks'; +import { PolicyTestResourceInfo } from '../../services/endpoint_policy'; +import { targetTags } from '../../target_tags'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'artifactEntriesList']); + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + const endpointArtifactsTestResources = getService('endpointArtifactTestResources'); + const endpointTestResources = getService('endpointTestResources'); + const retry = getService('retry'); + const esClient = getService('es'); + const supertest = getService('supertest'); + const find = getService('find'); + const toasts = getService('toasts'); + const policyTestResources = getService('policyTestResources'); + const unzipPromisify = promisify(unzip); + + const removeAllArtifacts = async () => { + for (const listId of ENDPOINT_ARTIFACT_LIST_IDS) { + await removeExceptionsList(listId); + } + }; + + const removeExceptionsList = async (listId: string) => { + await supertest + .delete(`${EXCEPTION_LIST_URL}?list_id=${listId}&namespace_type=agnostic`) + .set('kbn-xsrf', 'true'); + }; + + describe('For each artifact list under management', function () { + targetTags(this, ['@ess', '@serverless']); + + this.timeout(60_000 * 5); + let indexedData: IndexedHostsAndAlertsResponse; + let policyInfo: PolicyTestResourceInfo; + + before(async () => { + indexedData = await endpointTestResources.loadEndpointData(); + }); + after(async () => { + await endpointTestResources.unloadEndpointData(indexedData); + }); + + const checkFleetArtifacts = async ( + identifier: string, + expectedArtifact: ArtifactElasticsearchProperties, + expectedDecodedBodyArtifact: ArtifactBodyType, + policy?: PolicyTestResourceInfo + ) => { + // Check edited artifact is in the list with new values (wait for list to be updated) + let updatedArtifact: ArtifactElasticsearchProperties | undefined; + await retry.waitForWithTimeout('fleet artifact is updated', 120_000, async () => { + const artifacts = await endpointArtifactsTestResources.getArtifactsFromUnifiedManifestSO(); + + // This expects manifest artifact to come from unified so + const manifestArtifact = artifacts.find((artifact) => { + return ( + artifact.artifactIds.includes( + `${expectedArtifact.identifier}-${expectedArtifact.decoded_sha256}` + ) && artifact.policyId === policy?.packagePolicy.id + ); + }); + + if (!manifestArtifact) return false; + + // Get fleet artifact + const windowsArtifactResult = await esClient.get({ + index: '.fleet-artifacts-7', + id: `endpoint:${expectedArtifact.identifier}-${expectedArtifact.decoded_sha256}`, + }); + + const windowsArtifact = windowsArtifactResult._source as ArtifactElasticsearchProperties; + + // Get agent policy + const { + hits: { hits: policiesResults }, + } = await esClient.search({ + index: '.fleet-policies*', + query: { + bool: { + filter: [ + { + match: { + policy_id: policy?.agentPolicy.id, + }, + }, + ], + }, + }, + sort: [{ revision_idx: { order: 'desc' } }], + size: 1, + }); + + const agentPolicyResults = policiesResults[0] as AgentPolicyResponseType; + const policyArtifactManifest = agentPolicyResults._source.data.inputs[0] + ? agentPolicyResults._source.data.inputs[0].artifact_manifest + : undefined; + + let isUpdated: boolean = false; + if (policyArtifactManifest) { + // Compare artifacts from fleet artifacts and agent policy are the expecteds + isUpdated = + windowsArtifact.encoded_sha256 === expectedArtifact.encoded_sha256 && + policyArtifactManifest.artifacts[identifier].encoded_sha256 === + expectedArtifact.encoded_sha256; + } + + if (isUpdated) updatedArtifact = windowsArtifact; + return isUpdated; + }); + + updatedArtifact!.created = expectedArtifact.created; + const bodyFormBuffer = Buffer.from(updatedArtifact!.body, 'base64'); + const unzippedBody = await unzipPromisify(bodyFormBuffer); + + // Check decoded body first to detect possible body changes + expect(JSON.parse(unzippedBody.toString())).eql(expectedDecodedBodyArtifact); + expect(updatedArtifact).eql(expectedArtifact); + }; + + const performActions = async ( + actions: + | ArtifactActionsType['create']['formFields'] + | ArtifactActionsType['update']['formFields'], + suffix?: string + ) => { + for (const formAction of actions) { + if (formAction.type === 'customClick') { + await find.clickByCssSelector(formAction.selector, testSubjects.FIND_TIME); + } else if (formAction.type === 'click') { + await testSubjects.click(formAction.selector); + } else if (formAction.type === 'input') { + await testSubjects.setValue( + formAction.selector, + (formAction.value || '') + (suffix ? suffix : '') + ); + } else if (formAction.type === 'clear') { + await ( + await (await testSubjects.find(formAction.selector)).findByCssSelector('button') + ).click(); + } + } + }; + + const deleteArtifact = async (actions: ArtifactActionsType) => { + await pageObjects.artifactEntriesList.clickCardActionMenu(actions.pagePrefix); + await testSubjects.click(`${actions.pagePrefix}-card-cardDeleteAction`); + await testSubjects.click(`${actions.pagePrefix}-deleteModal-submitButton`); + await testSubjects.waitForDeleted(actions.delete.confirmSelector); + }; + + const createArtifact = async ( + actions: ArtifactActionsType | MultipleArtifactActionsType, + options?: { policyId?: string; suffix?: string; createButton?: string } + ) => { + // Opens add flyout + if (options?.createButton) { + await testSubjects.click(`${actions.pagePrefix}-${options.createButton}`); + } else { + await testSubjects.click(`${actions.pagePrefix}-emptyState-addButton`); + } + + await performActions(actions.create.formFields, options?.suffix); + + if (options?.policyId) { + await testSubjects.click(`${actions.pageObject}-form-effectedPolicies-perPolicy`); + await testSubjects.click(`policy-${options.policyId}-checkbox`); + } + + // Submit create artifact form + await testSubjects.click(`${actions.pagePrefix}-flyout-submitButton`); + }; + + const updateArtifact = async ( + actions: ArtifactActionsType, + options?: { policyId?: string; suffix?: string } + ) => { + // Opens edit flyout + await pageObjects.artifactEntriesList.clickCardActionMenu(actions.pagePrefix); + await testSubjects.click(`${actions.pagePrefix}-card-cardEditAction`); + + await performActions(actions.update.formFields); + + if (options?.policyId) { + await testSubjects.click(`${actions.pageObject}-form-effectedPolicies-perPolicy`); + await testSubjects.click(`policy-${options.policyId}-checkbox`); + } + + // Submit edit artifact form + await testSubjects.click(`${actions.pagePrefix}-flyout-submitButton`); + }; + + for (const testData of getArtifactsListTestsData()) { + describe(`When on the ${testData.title} entries list`, function () { + beforeEach(async () => { + policyInfo = await policyTestResources.createPolicy(); + await removeAllArtifacts(); + await browser.refresh(); + await pageObjects.artifactEntriesList.navigateToList(testData.urlPath); + }); + + afterEach(async () => { + await removeAllArtifacts(); + if (policyInfo) { + await policyInfo.cleanup(); + } + }); + + it(`should not show page title if there is no ${testData.title} entry`, async () => { + await testSubjects.missingOrFail('header-page-title'); + }); + + it(`should be able to add a new ${testData.title} entry`, async () => { + await createArtifact(testData, { policyId: policyInfo.packagePolicy.id }); + // Check new artifact is in the list + for (const checkResult of testData.create.checkResults) { + expect(await testSubjects.getVisibleText(checkResult.selector)).to.equal( + checkResult.value + ); + } + await toasts.dismiss(); + + // Title is shown after adding an item + expect(await testSubjects.getVisibleText('header-page-title')).to.equal(testData.title); + + // Checks if fleet artifact has been updated correctly + await checkFleetArtifacts( + testData.fleetArtifact.identifier, + testData.fleetArtifact.getExpectedUpdatedtArtifactWhenCreate(), + testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreate(), + policyInfo + ); + }); + + it(`should be able to update an existing ${testData.title} entry`, async () => { + await createArtifact(testData); + await updateArtifact(testData, { policyId: policyInfo.packagePolicy.id }); + + // Check edited artifact is in the list with new values (wait for list to be updated) + await retry.waitForWithTimeout('entry is updated in list', 20000, async () => { + const currentValue = await testSubjects.getVisibleText( + `${testData.pagePrefix}-card-criteriaConditions${ + testData.pagePrefix === 'EventFiltersListPage' ? '-condition' : '' + }` + ); + return currentValue === testData.update.waitForValue; + }); + + for (const checkResult of testData.update.checkResults) { + expect(await testSubjects.getVisibleText(checkResult.selector)).to.equal( + checkResult.value + ); + } + + await toasts.dismiss(); + + // Title still shown after editing an item + expect(await testSubjects.getVisibleText('header-page-title')).to.equal(testData.title); + + // Checks if fleet artifact has been updated correctly + await checkFleetArtifacts( + testData.fleetArtifact.identifier, + testData.fleetArtifact.getExpectedUpdatedArtifactWhenUpdate(), + testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenUpdate(), + policyInfo + ); + }); + + it(`should be able to delete the existing ${testData.title} entry`, async () => { + await createArtifact(testData); + await deleteArtifact(testData); + // We only expect one artifact to have been visible + await testSubjects.missingOrFail(testData.delete.card); + // Header has gone because there is no artifact + await testSubjects.missingOrFail('header-page-title'); + }); + }); + } + + describe('Should check artifacts are correctly generated when multiple entries', function () { + let firstPolicy: PolicyTestResourceInfo; + let secondPolicy: PolicyTestResourceInfo; + + const firstSuffix = 'first'; + const secondSuffix = 'second'; + const thirdSuffix = 'third'; + + beforeEach(async () => { + firstPolicy = await policyTestResources.createPolicy(); + secondPolicy = await policyTestResources.createPolicy(); + await removeAllArtifacts(); + await browser.refresh(); + await pageObjects.artifactEntriesList.navigateToList(testData.urlPath); + }); + + afterEach(async () => { + await removeAllArtifacts(); + if (firstPolicy) { + await firstPolicy.cleanup(); + } + if (secondPolicy) { + await secondPolicy.cleanup(); + } + }); + + const testData = getCreateMultipleData(); + it(`should get correct atifact when multiple entries are created`, async () => { + // Create first trusted app + await createArtifact(testData, { + policyId: firstPolicy.packagePolicy.id, + suffix: firstSuffix, + }); + await toasts.dismiss(); + + // Create second trusted app + await createArtifact(testData, { + policyId: secondPolicy.packagePolicy.id, + suffix: secondSuffix, + createButton: 'pageAddButton', + }); + await toasts.dismiss(); + + // Create third trusted app + await createArtifact(testData, { suffix: thirdSuffix, createButton: 'pageAddButton' }); + await toasts.dismiss(); + + // Checks if fleet artifact has been updated correctly + await checkFleetArtifacts( + testData.fleetArtifact.identifier, + testData.fleetArtifact.getExpectedUpdatedArtifactWhenCreateMultipleFirst(), + testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreateMultipleFirst( + thirdSuffix, + firstSuffix + ), + firstPolicy + ); + + // Checks if fleet artifact has been updated correctly + await checkFleetArtifacts( + testData.fleetArtifact.identifier, + testData.fleetArtifact.getExpectedUpdatedArtifactWhenCreateMultipleSecond(), + testData.fleetArtifact.getExpectedUpdatedArtifactBodyWhenCreateMultipleSecond( + thirdSuffix, + secondSuffix + ), + secondPolicy + ); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/endpoint_exceptions.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/endpoint_exceptions.ts new file mode 100644 index 0000000000000..ece54cecb47f1 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/endpoint_exceptions.ts @@ -0,0 +1,245 @@ +/* + * 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 { unzip } from 'zlib'; +import { promisify } from 'util'; +import expect from '@kbn/expect'; +import { IndexedHostsAndAlertsResponse } from '@kbn/security-solution-plugin/common/endpoint/index_data'; +import { EXCEPTION_LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; +import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services'; +import { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { targetTags } from '../../target_tags'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'header']); + const queryBar = getService('queryBar'); + const testSubjects = getService('testSubjects'); + const endpointTestResources = getService('endpointTestResources'); + const endpointArtifactTestResources = getService('endpointArtifactTestResources'); + const retry = getService('retry'); + const esClient = getService('es'); + const supertest = getService('supertest'); + const find = getService('find'); + const unzipPromisify = promisify(unzip); + const comboBox = getService('comboBox'); + const toasts = getService('toasts'); + + describe('Endpoint Exceptions', function () { + targetTags(this, ['@ess', '@serverless']); + + this.timeout(10 * 60_000); + + const clearPrefilledEntries = async () => { + const entriesContainer = await testSubjects.find('exceptionEntriesContainer'); + + let deleteButtons: WebElementWrapper[]; + do { + deleteButtons = await testSubjects.findAllDescendant( + 'builderItemEntryDeleteButton', + entriesContainer + ); + + await deleteButtons[0].click(); + } while (deleteButtons.length > 1); + }; + + const openNewEndpointExceptionFlyout = async () => { + await testSubjects.click('timeline-context-menu-button'); + await testSubjects.click('add-endpoint-exception-menu-item'); + await testSubjects.existOrFail('addExceptionFlyout'); + + await retry.waitFor('entries should be loaded', () => + testSubjects.exists('exceptionItemEntryContainer') + ); + }; + + const setLastFieldsValue = async ({ + testSubj, + value, + }: { + testSubj: string; + value: string; + optionSelector?: string; + }) => { + const fields = await find.allByCssSelector(`[data-test-subj="${testSubj}"]`); + + const lastField = fields[fields.length - 1]; + await lastField.click(); + + await retry.try( + async () => { + await comboBox.setElement(lastField, value); + }, + async () => { + // If the above fails due to an option not existing, create the value custom instead + await comboBox.setFilterValue(lastField, value); + await pageObjects.common.pressEnterKey(); + } + ); + }; + + const setLastEntry = async ({ + field, + operator, + value, + }: { + field: string; + operator: 'matches' | 'is'; + value: string; + }) => { + await setLastFieldsValue({ testSubj: 'fieldAutocompleteComboBox', value: field }); + await setLastFieldsValue({ testSubj: 'operatorAutocompleteComboBox', value: operator }); + await setLastFieldsValue({ + testSubj: operator === 'matches' ? 'valuesAutocompleteWildcard' : 'valuesAutocompleteMatch', + value, + }); + }; + + const checkArtifact = (expectedArtifact: object) => { + return retry.tryForTime(120_000, async () => { + const artifacts = await endpointArtifactTestResources.getArtifactsFromUnifiedManifestSO(); + + const foundArtifactId = artifacts + .flatMap((artifact) => artifact.artifactIds) + .find((artifactId) => artifactId.startsWith('endpoint-exceptionlist-macos-v1')); + + expect(foundArtifactId).to.not.be(undefined); + + // Get fleet artifact + const artifactResult = await esClient.get({ + index: '.fleet-artifacts-7', + id: `endpoint:${foundArtifactId!}`, + }); + + const artifact = artifactResult._source as ArtifactElasticsearchProperties; + + const zippedBody = Buffer.from(artifact.body, 'base64'); + const artifactBody = await unzipPromisify(zippedBody); + + expect(JSON.parse(artifactBody.toString())).to.eql(expectedArtifact); + }); + }; + + let indexedData: IndexedHostsAndAlertsResponse; + before(async () => { + indexedData = await endpointTestResources.loadEndpointData(); + + const waitForAlertsToAppear = async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`); + await pageObjects.header.waitUntilLoadingHasFinished(); + await retry.waitForWithTimeout('alerts to appear', 10 * 60_000, async () => { + await queryBar.clickQuerySubmitButton(); + return testSubjects.exists('timeline-context-menu-button'); + }); + }; + + await waitForAlertsToAppear(); + }); + + after(async () => { + await endpointTestResources.unloadEndpointData(indexedData); + }); + + beforeEach(async () => { + const deleteEndpointExceptions = async () => { + const { body } = await supertest + .get(`${EXCEPTION_LIST_ITEM_URL}/_find?list_id=endpoint_list&namespace_type=agnostic`) + .set('kbn-xsrf', 'true'); + + for (const exceptionListItem of (body as FoundExceptionListItemSchema).data) { + await supertest + .delete(`${EXCEPTION_LIST_ITEM_URL}?id=${exceptionListItem.id}&namespace_type=agnostic`) + .set('kbn-xsrf', 'true'); + } + }; + + await deleteEndpointExceptions(); + }); + + it('should add `event.module=endpoint` to entry if only wildcard operator is present', async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`); + + await openNewEndpointExceptionFlyout(); + await clearPrefilledEntries(); + + await testSubjects.setValue('exceptionFlyoutNameInput', 'test exception'); + await setLastEntry({ field: 'file.path', operator: 'matches', value: '*/cheese/*' }); + await testSubjects.click('exceptionsAndButton'); + await setLastEntry({ field: 'process.executable', operator: 'matches', value: 'ex*' }); + + await testSubjects.click('addExceptionConfirmButton'); + await toasts.dismiss(); + + await checkArtifact({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'file.path', + operator: 'included', + type: 'wildcard_cased', + value: '*/cheese/*', + }, + { + field: 'process.executable', + operator: 'included', + type: 'wildcard_cased', + value: 'ex*', + }, + { + // this additional entry should be added + field: 'event.module', + operator: 'included', + type: 'exact_cased', + value: 'endpoint', + }, + ], + }, + ], + }); + }); + + it('should NOT add `event.module=endpoint` to entry if there is another operator', async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('security', `/alerts`); + + await openNewEndpointExceptionFlyout(); + await clearPrefilledEntries(); + + await testSubjects.setValue('exceptionFlyoutNameInput', 'test exception'); + await setLastEntry({ field: 'file.path', operator: 'matches', value: '*/cheese/*' }); + await testSubjects.click('exceptionsAndButton'); + await setLastEntry({ field: 'process.executable', operator: 'is', value: 'something' }); + + await testSubjects.click('addExceptionConfirmButton'); + await toasts.dismiss(); + + await checkArtifact({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'file.path', + operator: 'included', + type: 'wildcard_cased', + value: '*/cheese/*', + }, + { + field: 'process.executable', + operator: 'included', + type: 'exact_cased', + value: 'something', + }, + ], + }, + ], + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts new file mode 100644 index 0000000000000..5464cf07f02e3 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.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 { getRegistryUrl as getRegistryUrlFromIngest } from '@kbn/fleet-plugin/server'; +import { isServerlessKibanaFlavor } from '@kbn/security-solution-plugin/scripts/endpoint/common/stack_services'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { + getRegistryUrlFromTestEnv, + isRegistryEnabled, +} from '../../../security_solution_endpoint_api_int/registry'; + +export default function (providerContext: FtrProviderContext) { + const { loadTestFile, getService, getPageObjects } = providerContext; + + describe('endpoint', function () { + const ingestManager = getService('ingestManager'); + const log = getService('log'); + const endpointTestResources = getService('endpointTestResources'); + const kbnClient = getService('kibanaServer'); + + if (!isRegistryEnabled()) { + log.warning('These tests are being run with an external package registry'); + } + + const registryUrl = getRegistryUrlFromTestEnv() ?? getRegistryUrlFromIngest(); + log.info(`Package registry URL for tests: ${registryUrl}`); + + before(async () => { + log.info('calling Fleet setup'); + await ingestManager.setup(); + + log.info('installing/upgrading Endpoint fleet package'); + await endpointTestResources.installOrUpgradeEndpointFleetPackage(); + + if (await isServerlessKibanaFlavor(kbnClient)) { + log.info('login for serverless environment'); + const pageObjects = getPageObjects(['svlCommonPage']); + await pageObjects.svlCommonPage.login(); + } + }); + loadTestFile(require.resolve('./artifact_entries_list')); + loadTestFile(require.resolve('./endpoint_exceptions')); + }); +} diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/mocks.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/mocks.ts new file mode 100644 index 0000000000000..47523694b4349 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/mocks.ts @@ -0,0 +1,807 @@ +/* + * 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 { FullAgentPolicy } from '@kbn/fleet-plugin/common/types'; +import { ArtifactElasticsearchProperties } from '@kbn/fleet-plugin/server/services/artifacts/types'; +import { InternalUnifiedManifestBaseSchema } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts'; +import { TranslatedExceptionListItem } from '@kbn/security-solution-plugin/server/endpoint/schemas/artifacts/lists'; + +export interface AgentPolicyResponseType { + _index: string; + _id: string; + _score: number; + _source: { data: FullAgentPolicy }; +} + +export interface InternalUnifiedManifestSchemaResponseType { + _index: string; + _id: string; + _score: number; + _source: { + 'endpoint:unified-user-artifact-manifest': InternalUnifiedManifestBaseSchema; + }; +} + +export interface ArtifactBodyType { + entries: TranslatedExceptionListItem[]; +} + +export type ArtifactActionsType = ReturnType[0]; +export type MultipleArtifactActionsType = ReturnType; + +export const getArtifactsListTestsData = () => [ + { + title: 'Trusted applications', + pagePrefix: 'trustedAppsListPage', + create: { + formFields: [ + { + type: 'input', + selector: 'trustedApps-form-descriptionField', + value: 'This is the trusted application description', + }, + { + type: 'input', + selector: 'trustedApps-form-nameTextField', + value: 'Trusted application name', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Hash', + }, + { + type: 'input', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value', + value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + ], + checkResults: [ + { + selector: 'trustedAppsListPage-card-criteriaConditions', + value: + 'OSIS Windows\nAND process.hash.*IS a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + ], + }, + update: { + formFields: [ + { + type: 'input', + selector: 'trustedApps-form-descriptionField', + value: 'This is the trusted application description edited', + }, + { + type: 'input', + selector: 'trustedApps-form-nameTextField', + value: 'Trusted application name edited', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Path', + }, + { + type: 'input', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value', + value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + ], + checkResults: [ + { + selector: 'trustedAppsListPage-card-criteriaConditions', + value: + 'OSIS Windows\nAND process.executable.caselessIS c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + { + selector: 'trustedAppsListPage-card-header-title', + value: 'Trusted application name edited', + }, + { + selector: 'trustedAppsListPage-card-description', + value: 'This is the trusted application description edited', + }, + ], + waitForValue: + 'OSIS Windows\nAND process.executable.caselessIS c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + delete: { + confirmSelector: 'trustedAppsListPage-deleteModal-submitButton', + card: 'trustedAppsListPage-card', + }, + urlPath: 'trusted_apps', + pageObject: 'trustedApps', + fleetArtifact: { + identifier: 'endpoint-trustlist-windows-v1', + type: 'trustedApplications', + getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({ + type: 'trustlist', + identifier: 'endpoint-trustlist-windows-v1', + body: 'eJxVzNEKgyAUgOF3OdcxNMvMVxkxTp4jCa5EbWxE7z422MVuvx/+A3itOXABez2gvhKDhRLuKTI0f80HjgQWUt4cl3JZsCyXsmDba2hgS5yxbhkshNXFnZig+f34ia7eHJYvPjDuH8VODcIJ543URjsx61F71K2WbiTFgowUyIPocDZKSKNG8p566qVsfTdoOKdzOt89hz0Q', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-trustlist-windows-v1/016bec11c5b1d6f8609fd3525202aa12baf0132484abf368d5011100d5ec1ec4', + compression_algorithm: 'zlib', + decoded_size: 193, + decoded_sha256: '016bec11c5b1d6f8609fd3525202aa12baf0132484abf368d5011100d5ec1ec4', + encryption_algorithm: 'none', + encoded_sha256: '814aabc04d674ccdeb7c1acfe74120cb52ad1392d6924a7d813e08f8b6cd0f0f', + encoded_size: 153, + }), + getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'process.hash.sha256', + operator: 'included', + type: 'exact_cased', + value: 'a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + ], + }, + ], + }), + getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({ + type: 'trustlist', + identifier: 'endpoint-trustlist-windows-v1', + body: 'eJx9jEEKwjAUBa8ibx1cuMwBvIQtEpMnBH6TkJ9KpeTuEkHBjcthhtnB1Gqkwl52tGchLDQuRQjz4+6REmBRavZUPXKjX5u7vcNcWF3LFRYxeVkDA8xnx835dvVOKVSFwcPJOoS301RdCnk5ZwmsX4rC8TeHf8VpJOhzn/sLJpZG8A==', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-trustlist-windows-v1/ac2bf74a73885f9a5a1700c328bf1a5a8f6cb72f2465a575335ea99dac0d4c10', + compression_algorithm: 'zlib', + decoded_size: 198, + decoded_sha256: 'ac2bf74a73885f9a5a1700c328bf1a5a8f6cb72f2465a575335ea99dac0d4c10', + encryption_algorithm: 'none', + encoded_sha256: '28d81b2787cea23fcb88d02b1c09940858963a62c60cdfd7a2b7564cfc251708', + encoded_size: 130, + }), + getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + ], + }, + ], + }), + }, + }, + { + title: 'Event Filters', + pagePrefix: 'EventFiltersListPage', + create: { + formFields: [ + { + type: 'input', + selector: 'eventFilters-form-name-input', + value: 'Event filter name', + }, + { + type: 'input', + selector: 'eventFilters-form-description-input', + value: 'This is the event filter description', + }, + { + type: 'click', + selector: 'fieldAutocompleteComboBox', + }, + { + type: 'customClick', + selector: 'button[title="agent.ephemeral_id"]', + }, + { + type: 'click', + selector: 'valuesAutocompleteMatch', + }, + { + type: 'input', + selector: 'valuesAutocompleteMatch', + value: 'endpoint', + }, + ], + checkResults: [ + { + selector: 'EventFiltersListPage-card-criteriaConditions-condition', + value: 'AND agent.ephemeral_idIS endpoint', + }, + ], + }, + update: { + formFields: [ + { + type: 'input', + selector: 'eventFilters-form-name-input', + value: 'Event filter name edited', + }, + { + type: 'input', + selector: 'eventFilters-form-description-input', + value: 'This is the event filter description edited', + }, + { + type: 'click', + selector: 'fieldAutocompleteComboBox', + }, + { + type: 'input', + selector: 'fieldAutocompleteComboBox', + value: 'agent.id', + }, + { + type: 'customClick', + selector: 'button[title="agent.id"]', + }, + { + type: 'input', + selector: 'valuesAutocompleteMatch', + value: 'test super large value', + }, + { + type: 'click', + selector: 'eventFilters-form-description-input', + }, + ], + checkResults: [ + { + selector: 'EventFiltersListPage-card-criteriaConditions-condition', + value: 'AND agent.idIS test super large value', + }, + { + selector: 'EventFiltersListPage-card-header-title', + value: 'Event filter name edited', + }, + { + selector: 'EventFiltersListPage-card-description', + value: 'This is the event filter description edited', + }, + ], + waitForValue: 'AND agent.idIS test super large value', + }, + delete: { + confirmSelector: 'EventFiltersListPage-deleteModal-submitButton', + card: 'EventFiltersListPage-card', + }, + urlPath: 'event_filters', + pageObject: 'eventFilters', + fleetArtifact: { + identifier: 'endpoint-eventfilterlist-windows-v1', + type: 'eventfilterlist', + getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({ + type: 'eventfilterlist', + identifier: 'endpoint-eventfilterlist-windows-v1', + body: 'eJxVzFEKwjAQRdG9vO/iArKVUsqQPHVgmoRkWpSSvYvFH3/PhXuC2ZuyI8wn/F2JgK5bNWL6a3elJQTIg9lvrE9ubGKrJkwolU28NARojrYnfvW340uir1H6hYfYfmlOtWh2jGUs4wOrCC+X', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-eventfilterlist-windows-v1/b3373c93ffc795d954f22c625c084dc5874a156ec0cb3d4af1c3dab0b965fa30', + compression_algorithm: 'zlib', + decoded_size: 136, + decoded_sha256: 'b3373c93ffc795d954f22c625c084dc5874a156ec0cb3d4af1c3dab0b965fa30', + encryption_algorithm: 'none', + encoded_sha256: 'cc9bc4e3cc2c2767c3f56b17ebf4901dbe7e82f15720d48c745370e028c5e887', + encoded_size: 108, + }), + getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'agent.ephemeral_id', + operator: 'included', + type: 'exact_cased', + value: 'endpoint', + }, + ], + }, + ], + }), + getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({ + type: 'eventfilterlist', + identifier: 'endpoint-eventfilterlist-windows-v1', + body: 'eJxVzEEKwyAURdGtyBuHLsCtlFA++hoEa+T7LQnBvZc0nXR6LtwDLKaJDf5+wPZKeLT0qpmY/tozMUd4yMJitxQxYa1UsVXhkUrIPfLU34SbBHsEaV98S+6nGpu51ivVZdGF7gpjHvP4ADqUMJs=', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-eventfilterlist-windows-v1/e4f00c88380d2c429eeb2741ad19383b94d76f79744b098b095befc24003e158', + compression_algorithm: 'zlib', + decoded_size: 140, + decoded_sha256: 'e4f00c88380d2c429eeb2741ad19383b94d76f79744b098b095befc24003e158', + encryption_algorithm: 'none', + encoded_sha256: 'e371e2a28b59bd942ca7ef9665dae7c9b27409ad6f2ca3bff6357a98deb23c12', + encoded_size: 110, + }), + getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'agent.id', + operator: 'included', + type: 'exact_cased', + value: 'test super large value', + }, + ], + }, + ], + }), + }, + }, + { + title: 'Blocklist', + pagePrefix: 'blocklistPage', + create: { + formFields: [ + { + type: 'input', + selector: 'blocklist-form-name-input', + value: 'Blocklist name', + }, + { + type: 'input', + selector: 'blocklist-form-description-input', + value: 'This is the blocklist description', + }, + { + type: 'click', + selector: 'blocklist-form-field-select', + }, + { + type: 'click', + selector: 'blocklist-form-file.hash.*', + }, + { + type: 'input', + selector: 'blocklist-form-values-input', + value: + 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476,aedb279e378BED6C2DB3C9DC9e12ba635e0b391c,741462ab431a22233C787BAAB9B653C7', + }, + { + type: 'click', + selector: 'blocklist-form-name-input', + }, + ], + checkResults: [ + { + selector: 'blocklistPage-card-criteriaConditions', + value: + 'OSIS Windows\nAND file.hash.*IS ONE OF\n741462ab431a22233c787baab9b653c7\naedb279e378bed6c2db3c9dc9e12ba635e0b391c\na4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + ], + }, + update: { + formFields: [ + { + type: 'input', + selector: 'blocklist-form-name-input', + value: 'Blocklist name edited', + }, + { + type: 'input', + selector: 'blocklist-form-description-input', + value: 'This is the blocklist description edited', + }, + { + type: 'click', + selector: 'blocklist-form-field-select', + }, + { + type: 'click', + selector: 'blocklist-form-file.path.caseless', + }, + { + type: 'clear', + selector: + 'blocklist-form-values-input-a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + { + type: 'clear', + selector: 'blocklist-form-values-input-741462ab431a22233c787baab9b653c7', + }, + { + type: 'clear', + selector: 'blocklist-form-values-input-aedb279e378bed6c2db3c9dc9e12ba635e0b391c', + }, + { + type: 'input', + selector: 'blocklist-form-values-input', + value: 'c:\\randomFolder\\randomFile.exe, c:\\randomFolder\\randomFile2.exe', + }, + { + type: 'click', + selector: 'blocklist-form-name-input', + }, + ], + checkResults: [ + { + selector: 'blocklistPage-card-criteriaConditions', + value: + 'OSIS Windows\nAND file.path.caselessIS ONE OF\nc:\\randomFolder\\randomFile.exe\nc:\\randomFolder\\randomFile2.exe', + }, + { + selector: 'blocklistPage-card-header-title', + value: 'Blocklist name edited', + }, + { + selector: 'blocklistPage-card-description', + value: 'This is the blocklist description edited', + }, + ], + waitForValue: + 'OSIS Windows\nAND file.path.caselessIS ONE OF\nc:\\randomFolder\\randomFile.exe\nc:\\randomFolder\\randomFile2.exe', + }, + delete: { + confirmSelector: 'blocklistDeletionConfirm', + card: 'blocklistCard', + }, + pageObject: 'blocklist', + urlPath: 'blocklist', + fleetArtifact: { + identifier: 'endpoint-blocklist-windows-v1', + type: 'blocklist', + getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({ + type: 'blocklist', + identifier: 'endpoint-blocklist-windows-v1', + relative_url: + '/api/fleet/artifacts/endpoint-blocklist-windows-v1/637f1e8795406904980ae2ab4a69cea967756571507f6bd7fc94cde0add20df2', + body: 'eJylzk1qw0AMQOG7aG3C/GpmfJVggkbSYIPjmNgpDcF3LxS66LLN+sHje4Eu+33SDfrzC/bnqtDDNl3XWaH71dqks0APbZr1NNI2nq4SoYPbqnfab3foYVp4fogKdD8n/STeL0ybyoWWJ3TwQfNDoT9DCjagoxq8Jeec95xyqkS1VIyeEwzHcHR/NW0j2TdQpFJdKupTrirITqrnIlzUukroo5rqi+V/41zEd3jBJ8OGW7aYkU3Fgo3QoeUiXo1ka0iTCVSzNzb7Iq1JlGitayHhN3s4vgDTjqDt', + encryption_algorithm: 'none', + package_name: 'endpoint', + encoded_size: 219, + encoded_sha256: 'e803c1ee6aec0885092bfd6c288839f42b31107dd6d0bb2c8e2d2b9f8fc8b293', + decoded_size: 501, + decoded_sha256: '637f1e8795406904980ae2ab4a69cea967756571507f6bd7fc94cde0add20df2', + compression_algorithm: 'zlib', + created: '2000-01-01T00:00:00.000Z', + }), + getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'file.hash.md5', + operator: 'included', + type: 'exact_cased_any', + value: ['741462ab431a22233c787baab9b653c7'], + }, + ], + }, + { + type: 'simple', + entries: [ + { + field: 'file.hash.sha1', + operator: 'included', + type: 'exact_cased_any', + value: ['aedb279e378bed6c2db3c9dc9e12ba635e0b391c'], + }, + ], + }, + { + type: 'simple', + entries: [ + { + field: 'file.hash.sha256', + operator: 'included', + type: 'exact_cased_any', + value: ['a4370c0cf81686c0b696fa6261c9d3e0d810ae704ab8301839dffd5d5112f476'], + }, + ], + }, + ], + }), + getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({ + type: 'blocklist', + identifier: 'endpoint-blocklist-windows-v1', + relative_url: + '/api/fleet/artifacts/endpoint-blocklist-windows-v1/3ead6ce4e34cb4411083a44bfe813d9442d296981ee8d56e727e6cff14dea0f0', + body: 'eJx9jUEKwjAURK8isw4uXOYAXqKV8kmmGPhNQpJKS/HuEkHBjcxqmMebA4ytBFbY4UDbM2FRw5KVMD/bHKgeFnNQnrO0OwxSZpGWCixCdLp6epiPhZu4NjmpVNY6Sdxh8BBdCTvA2XEsEn1arkk9y7d1Pbf+fvrHXN7Q7dnzAojqRb8=', + encryption_algorithm: 'none', + package_name: 'endpoint', + encoded_size: 131, + encoded_sha256: 'f0e2dc2aa8d968b704baa11bf3100db91a85991d5de431f8c389b7417335a701', + decoded_size: 197, + decoded_sha256: '3ead6ce4e34cb4411083a44bfe813d9442d296981ee8d56e727e6cff14dea0f0', + compression_algorithm: 'zlib', + created: '2000-01-01T00:00:00.000Z', + }), + getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'file.path', + operator: 'included', + type: 'exact_caseless_any', + value: ['c:\\randomFolder\\randomFile.exe', ' c:\\randomFolder\\randomFile2.exe'], + }, + ], + }, + ], + }), + }, + }, + { + title: 'Host isolation exceptions', + pagePrefix: 'hostIsolationExceptionsListPage', + create: { + formFields: [ + { + type: 'input', + selector: 'hostIsolationExceptions-form-name-input', + value: 'Host Isolation exception name', + }, + { + type: 'input', + selector: 'hostIsolationExceptions-form-description-input', + value: 'This is the host isolation exception description', + }, + { + type: 'input', + selector: 'hostIsolationExceptions-form-ip-input', + value: '1.1.1.1', + }, + ], + checkResults: [ + { + selector: 'hostIsolationExceptionsListPage-card-criteriaConditions', + value: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 1.1.1.1', + }, + ], + }, + update: { + formFields: [ + { + type: 'input', + selector: 'hostIsolationExceptions-form-name-input', + value: 'Host Isolation exception name edited', + }, + { + type: 'input', + selector: 'hostIsolationExceptions-form-description-input', + value: 'This is the host isolation exception description edited', + }, + { + type: 'input', + selector: 'hostIsolationExceptions-form-ip-input', + value: '2.2.2.2/24', + }, + ], + checkResults: [ + { + selector: 'hostIsolationExceptionsListPage-card-criteriaConditions', + value: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 2.2.2.2/24', + }, + { + selector: 'hostIsolationExceptionsListPage-card-header-title', + value: 'Host Isolation exception name edited', + }, + { + selector: 'hostIsolationExceptionsListPage-card-description', + value: 'This is the host isolation exception description edited', + }, + ], + waitForValue: 'OSIS Windows, Linux, Mac\nAND destination.ipIS 2.2.2.2/24', + }, + delete: { + confirmSelector: 'hostIsolationExceptionsDeletionConfirm', + card: 'hostIsolationExceptionsCard', + }, + pageObject: 'hostIsolationExceptions', + urlPath: 'host_isolation_exceptions', + fleetArtifact: { + identifier: 'endpoint-hostisolationexceptionlist-windows-v1', + type: 'hostisolationexceptionlist', + getExpectedUpdatedtArtifactWhenCreate: (): ArtifactElasticsearchProperties => ({ + type: 'hostisolationexceptionlist', + identifier: 'endpoint-hostisolationexceptionlist-windows-v1', + relative_url: + '/api/fleet/artifacts/endpoint-hostisolationexceptionlist-windows-v1/2c3ee2b5e7f86f8c336a3df7e59a1151b11d7eec382442032e69712d6a6459e0', + body: 'eJxVjEEKgzAUBe/y1kFwm6uIyCd5hQ9pEpKvWCR3LxVclNnNwFxgtqbs8MsF+1TCo+u7JsL9tZcyRXhEdtMspiVPWuFQKptYafDQHNIeGeGeFU8JtgXptzwk7T87TzcY61jHF647LBE=', + encryption_algorithm: 'none', + package_name: 'endpoint', + encoded_size: 104, + encoded_sha256: 'f958ada742a0be63d136901317c6bfd04b2ab5f52cdd0e872461089b0009bb3e', + decoded_size: 131, + decoded_sha256: '2c3ee2b5e7f86f8c336a3df7e59a1151b11d7eec382442032e69712d6a6459e0', + compression_algorithm: 'zlib', + created: '2000-01-01T00:00:00.000Z', + }), + getExpectedUpdatedArtifactBodyWhenCreate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'destination.ip', + operator: 'included', + type: 'exact_cased', + value: '1.1.1.1', + }, + ], + }, + ], + }), + getExpectedUpdatedArtifactWhenUpdate: (): ArtifactElasticsearchProperties => ({ + type: 'hostisolationexceptionlist', + identifier: 'endpoint-hostisolationexceptionlist-windows-v1', + relative_url: + '/api/fleet/artifacts/endpoint-hostisolationexceptionlist-windows-v1/4b62473b4cf057277b3297896771cc1061c3bea2c4f7ec1ef5c2546f33d5d9e8', + body: 'eJxVjEEKwyAUBe/y1pJC6MqrlBA++gofrIr+hJbg3UsCXZTZzcAcYLam7PCPA/aphEfXV02E+2tPZYrwiOymWUxLnrTCoVQ2sdLgoTmkLTLC/VZ8S7A1SL/kLmk77Txd3OY7xjKW8QUwWyyq', + encryption_algorithm: 'none', + package_name: 'endpoint', + encoded_size: 108, + encoded_sha256: '84df618343078f43a54299bcebef03010f3ec4ffdf7160448882fee9bafa1adb', + decoded_size: 134, + decoded_sha256: '4b62473b4cf057277b3297896771cc1061c3bea2c4f7ec1ef5c2546f33d5d9e8', + compression_algorithm: 'zlib', + created: '2000-01-01T00:00:00.000Z', + }), + getExpectedUpdatedArtifactBodyWhenUpdate: (): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'destination.ip', + operator: 'included', + type: 'exact_cased', + value: '2.2.2.2/24', + }, + ], + }, + ], + }), + }, + }, +]; + +export const getCreateMultipleData = () => ({ + title: 'Trusted applications', + pagePrefix: 'trustedAppsListPage', + create: { + formFields: [ + { + type: 'input', + selector: 'trustedApps-form-descriptionField', + value: 'This is the trusted application description', + }, + { + type: 'input', + selector: 'trustedApps-form-nameTextField', + value: 'Trusted application name', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field', + }, + { + type: 'click', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-field-type-Path', + }, + { + type: 'input', + selector: 'trustedApps-form-conditionsBuilder-group1-entry0-value', + value: 'c:\\randomFolder\\randomFile.exe', + }, + ], + }, + + urlPath: 'trusted_apps', + pageObject: 'trustedApps', + fleetArtifact: { + identifier: 'endpoint-trustlist-windows-v1', + type: 'trustedApplications', + getExpectedUpdatedArtifactWhenCreateMultipleFirst: (): ArtifactElasticsearchProperties => ({ + type: 'trustlist', + identifier: 'endpoint-trustlist-windows-v1', + body: 'eJzNjlEKwjAQBe+y38ED5ABewhaJySsubJuwu5VK6d0lgoI38PMxj2F2wuLKMIqXnfzZQJGM5yag8MMmhhSK1LRmmJ2wIa+ebu9jbdDkVSkSL1nWgkLho8OWsl9zMgjMKNAjydpBjsOgaSl1Plcp0O9iQff7nbXQMR7h79ImVvOeNh4vUR5zdA==', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-trustlist-windows-v1/329fc9176a24d64f4376d2c25d5db5b31cf86b288dac83c8a004dfe5bbfdc7d0', + compression_algorithm: 'zlib', + decoded_size: 323, + decoded_sha256: '329fc9176a24d64f4376d2c25d5db5b31cf86b288dac83c8a004dfe5bbfdc7d0', + encryption_algorithm: 'none', + encoded_sha256: '4d9eecb830948eabd721563fd2473900207d043126e66eac2ef78f9e05a80adb', + encoded_size: 136, + }), + getExpectedUpdatedArtifactBodyWhenCreateMultipleFirst: ( + firstSuffix: string, + secondSuffix: string + ): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: `c:\\randomFolder\\randomFile.exe${firstSuffix}`, + }, + ], + }, + { + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: `c:\\randomFolder\\randomFile.exe${secondSuffix}`, + }, + ], + type: 'simple', + }, + ], + }), + getExpectedUpdatedArtifactWhenCreateMultipleSecond: (): ArtifactElasticsearchProperties => ({ + type: 'trustlist', + identifier: 'endpoint-trustlist-windows-v1', + body: 'eJzNjlEKwjAQRO8y38ED5ABewhaJyYiBbRJ2U6mU3l1aUPAGfg5veLwVLF0zDf6yor8a4WF5akK4H3bPlASPpjXS7MSFce7hdhxro4ZeFR65RJkTE9xHxyXEfo3BKDSDwzPIvIPoh0FDSXU6V0nU78rC3d8fWRO2cXN/l2aMtRxt4/YGxIFzyA==', + package_name: 'endpoint', + created: '2000-01-01T00:00:00.000Z', + relative_url: + '/api/fleet/artifacts/endpoint-trustlist-windows-v1/3be2ce848f9b49d6531e6dc80f43579e00adbc640d3f785c14c8f9fa2652500a', + compression_algorithm: 'zlib', + decoded_size: 324, + decoded_sha256: '3be2ce848f9b49d6531e6dc80f43579e00adbc640d3f785c14c8f9fa2652500a', + encryption_algorithm: 'none', + encoded_sha256: '68304c35bbe863d0fbb15cf7e5ae5c84bad17aa7a3bc26828f9f0b20e0df6ed8', + encoded_size: 136, + }), + getExpectedUpdatedArtifactBodyWhenCreateMultipleSecond: ( + firstSuffix: string, + secondSuffix: string + ): ArtifactBodyType => ({ + entries: [ + { + type: 'simple', + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: `c:\\randomFolder\\randomFile.exe${firstSuffix}`, + }, + ], + }, + { + entries: [ + { + field: 'process.executable', + operator: 'included', + type: 'exact_caseless', + value: `c:\\randomFolder\\randomFile.exe${secondSuffix}`, + }, + ], + type: 'simple', + }, + ], + }), + }, +}); diff --git a/x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts b/x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts new file mode 100644 index 0000000000000..275f984307bff --- /dev/null +++ b/x-pack/test/security_solution_endpoint/integrations_feature_flag.config.ts @@ -0,0 +1,34 @@ +/* + * 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 { resolve } from 'path'; +import { FtrConfigProviderContext } from '@kbn/test'; +import { generateConfig } from './config.base'; +import { services } from './services'; + +export default async function (ftrConfigProviderContext: FtrConfigProviderContext) { + const { readConfigFile } = ftrConfigProviderContext; + + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); + + return generateConfig({ + ftrConfigProviderContext, + baseConfig: xpackFunctionalConfig, + testFiles: [resolve(__dirname, './apps/integrations_feature_flag')], + junitReportName: + 'X-Pack Endpoint Integrations With Feature Flags turned on Functional Tests on ESS', + target: 'ess', + kbnServerArgs: [ + // set the packagerTaskInterval to 5s in order to speed up test executions when checking fleet artifacts + '--xpack.securitySolution.packagerTaskInterval=5s', + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['unifiedManifestEnabled'])}`, + ], + services, + }); +} diff --git a/x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts b/x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.ts new file mode 100644 index 0000000000000..7ac35bbe3a101 --- /dev/null +++ b/x-pack/test/security_solution_endpoint/serverless.integrations_feature_flag.config.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 { resolve } from 'path'; +import { FtrConfigProviderContext } from '@kbn/test'; +import { generateConfig } from './config.base'; +import { svlServices } from './services'; + +export default async function (ftrConfigProviderContext: FtrConfigProviderContext) { + const { readConfigFile } = ftrConfigProviderContext; + + const svlBaseConfig = await readConfigFile( + require.resolve('../../test_serverless/shared/config.base.ts') + ); + + return generateConfig({ + ftrConfigProviderContext, + baseConfig: svlBaseConfig, + testFiles: [resolve(__dirname, './apps/integrations_feature_flag')], + junitReportName: + 'X-Pack Endpoint Integrations With Feature Flags turned on Functional Tests on ESS', + target: 'serverless', + kbnServerArgs: [ + '--serverless=security', + // set the packagerTaskInterval to 5s in order to speed up test executions when checking fleet artifacts + '--xpack.securitySolution.packagerTaskInterval=5s', + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['unifiedManifestEnabled'])}`, + ], + services: svlServices, + }); +} diff --git a/x-pack/test/security_solution_endpoint/services/endpoint_artifacts.ts b/x-pack/test/security_solution_endpoint/services/endpoint_artifacts.ts index a8fcbf3f982fa..0a914226c9280 100644 --- a/x-pack/test/security_solution_endpoint/services/endpoint_artifacts.ts +++ b/x-pack/test/security_solution_endpoint/services/endpoint_artifacts.ts @@ -6,9 +6,9 @@ */ import type { - ExceptionListItemSchema, - CreateExceptionListSchema, CreateExceptionListItemSchema, + CreateExceptionListSchema, + ExceptionListItemSchema, } from '@kbn/securitysolution-io-ts-list-types'; import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; import { Response } from 'superagent'; @@ -19,8 +19,10 @@ import { EVENT_FILTER_LIST_DEFINITION } from '@kbn/security-solution-plugin/publ import { HOST_ISOLATION_EXCEPTIONS_LIST_DEFINITION } from '@kbn/security-solution-plugin/public/management/pages/host_isolation_exceptions/constants'; import { BLOCKLISTS_LIST_DEFINITION } from '@kbn/security-solution-plugin/public/management/pages/blocklist/constants'; import { ManifestConstants } from '@kbn/security-solution-plugin/server/endpoint/lib/artifacts'; + import { FtrService } from '../../functional/ftr_provider_context'; import { InternalManifestSchemaResponseType } from '../apps/integrations/mocks'; +import { InternalUnifiedManifestSchemaResponseType } from '../apps/integrations_feature_flag/mocks'; export interface ArtifactTestData { artifact: ExceptionListItemSchema; @@ -132,8 +134,25 @@ export class EndpointArtifactsTestResources extends FtrService { }); const manifestResult = manifestResults[0] as InternalManifestSchemaResponseType; - const artifacts = manifestResult._source['endpoint:user-artifact-manifest'].artifacts; + return manifestResult._source['endpoint:user-artifact-manifest'].artifacts; + } + + async getArtifactsFromUnifiedManifestSO(): Promise< + Array< + InternalUnifiedManifestSchemaResponseType['_source']['endpoint:unified-user-artifact-manifest'] + > + > { + const { + hits: { hits: manifestResults }, + } = await this.esClient.search({ + index: '.kibana*', + query: { + bool: { filter: [{ term: { type: ManifestConstants.UNIFIED_SAVED_OBJECT_TYPE } }] }, + }, + }); - return artifacts; + return manifestResults.map( + (result) => result._source!['endpoint:unified-user-artifact-manifest'] + ); } } From ba8234e6ee081690f0f189ab980e3cef6e4f8e1b Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Fri, 17 May 2024 09:59:03 -0500 Subject: [PATCH 59/71] fix lint error --- .../functional/test_suites/security/ml/trained_models_list.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test_serverless/functional/test_suites/security/ml/trained_models_list.ts b/x-pack/test_serverless/functional/test_suites/security/ml/trained_models_list.ts index 6ec414787dde3..8e6928b9817a2 100644 --- a/x-pack/test_serverless/functional/test_suites/security/ml/trained_models_list.ts +++ b/x-pack/test_serverless/functional/test_suites/security/ml/trained_models_list.ts @@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // failsOnMKI, see https://github.com/elastic/kibana/issues/180481 describe('Trained models list', function () { this.tags(['failsOnMKI']); - + before(async () => { await PageObjects.svlCommonPage.login(); await ml.api.syncSavedObjects(); From 68849d439eba5017fc1c527c699a7ded08a1cb64 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 17 May 2024 11:23:02 -0400 Subject: [PATCH 60/71] skip failing test suite (#183748) --- .../test_suites/observability/slos/fetch_historical_summary.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts b/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts index d69108007477e..32992e5a3b07b 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/slos/fetch_historical_summary.ts @@ -21,7 +21,8 @@ export default function ({ getService }: FtrProviderContext) { const sloApi = getService('sloApi'); const SLO_ID = 'slo-fake-1'; - describe('fetch historical summary', () => { + // Failing: See https://github.com/elastic/kibana/issues/183748 + describe.skip('fetch historical summary', () => { before(async () => { const now = moment().startOf('minute'); const curr = now.clone().subtract(30, 'days'); From 3d443c84266739f822c4335051907671f57bd977 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 17 May 2024 11:24:07 -0400 Subject: [PATCH 61/71] skip failing test suite (#183713) --- .../detection_engine/value_lists/value_list_items.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/value_lists/value_list_items.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/value_lists/value_list_items.cy.ts index 9e9a6ec8fdf61..11fb0aa197450 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/value_lists/value_list_items.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine/value_lists/value_list_items.cy.ts @@ -41,7 +41,8 @@ import { import { RULES_MANAGEMENT_URL } from '../../../../urls/rules_management'; import { getDefaultUsername } from '../../../../tasks/common/users'; -describe( +// Failing: See https://github.com/elastic/kibana/issues/183713 +describe.skip( 'Value list items', { tags: ['@ess', '@serverless'], From 8f9c4ab4277c0745d1185733fe1791d09dbacdba Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 17 May 2024 11:24:59 -0400 Subject: [PATCH 62/71] skip failing test suite (#183750) --- .../test/api_integration/apis/slos/fetch_historical_summary.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts b/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts index 4acdfe569c404..94cccf1e14938 100644 --- a/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts +++ b/x-pack/test/api_integration/apis/slos/fetch_historical_summary.ts @@ -20,7 +20,8 @@ export default function ({ getService }: FtrProviderContext) { const sloApi = getService('slo'); const SLO_ID = 'slo-fake-1'; - describe('fetch historical summary', () => { + // Failing: See https://github.com/elastic/kibana/issues/183750 + describe.skip('fetch historical summary', () => { before(async () => { const now = moment().startOf('minute'); const curr = now.clone().subtract(30, 'days'); From 60cf299af8cb44e3f87ac4f88c3927973ebad1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20=C3=81brah=C3=A1m?= Date: Fri, 17 May 2024 18:05:41 +0200 Subject: [PATCH 63/71] [EDR Workflows] Add new backfill function to set `on_write_scan` to `false` if Malware protection is off (#182598) ## Summary This PR adds an additional backfill for `on_write_scan`. This is a fix, because it has been backfilled with a `true` value, but it should be `false` when Malware protection is off. This should be a safe backfill, because: - the feature is behind feature flag, so users did not have the opportunity to modify the value, - and if they would have, still, `malware=off && on_write_scan=true` is an invalid combination, the user cannot achieve it, so there is no chance of destroying user created settings. Additionally, the new model version is added to a different folder to help transitioning our mindsets from migrations to model versions. ### 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 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../check_registered_types.test.ts | 2 +- .../fleet/server/saved_objects/index.ts | 9 + .../model_versions/security_solution/index.ts | 8 + .../v10_on_write_scan_fix.test.ts | 183 ++++++++++++++++++ .../v10_on_write_scan_fix.ts | 42 ++++ 5 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 6b8a29c70227b..015c517e9a6b8 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -112,7 +112,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-agent-policies": "803dc27e106440c41e8f3c3d8ee8bbb0821bcde2", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", "ingest-outputs": "daafff49255ab700e07491376fe89f04fc998b91", - "ingest-package-policies": "d63e091b2b3cf2eecaa46ae2533bdd5214a983fc", + "ingest-package-policies": "e6da7d0ee2996241ade23b3a7811fe5d3e449cb2", "ingest_manager_settings": "91445219e7115ff0c45d1dabd5d614a80b421797", "inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83", "kql-telemetry": "93c1d16c1a0dfca9c8842062cf5ef8f62ae401ad", diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 22b5aa3f5fddb..f74366d924c96 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -85,6 +85,7 @@ import { migratePackagePolicyEvictionsFromV81102, } from './migrations/security_solution/to_v8_11_0_2'; import { settingsV1 } from './model_versions/v1'; +import { packagePolicyV10OnWriteScanFix } from './model_versions/security_solution'; /* * Saved object types and mappings @@ -540,6 +541,14 @@ export const getSavedObjectTypes = ( }, ], }, + '10': { + changes: [ + { + type: 'data_backfill', + backfillFn: packagePolicyV10OnWriteScanFix, + }, + ], + }, }, migrations: { '7.10.0': migratePackagePolicyToV7100, diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts new file mode 100644 index 0000000000000..780bc55f9cd3e --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { packagePolicyV10OnWriteScanFix } from './v10_on_write_scan_fix'; diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts new file mode 100644 index 0000000000000..37ca3dd6d4b0d --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts @@ -0,0 +1,183 @@ +/* + * 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 { SavedObject } from '@kbn/core-saved-objects-api-server'; +import type { ModelVersionTestMigrator } from '@kbn/core-test-helpers-model-versions'; +import { createModelVersionTestMigrator } from '@kbn/core-test-helpers-model-versions'; + +import { getSavedObjectTypes } from '../..'; + +import type { PackagePolicy } from '../../../../common'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../common'; + +describe('backfill for modelVersion 10 - fix on_write_scan field', () => { + let migrator: ModelVersionTestMigrator; + let policyConfigSO: SavedObject; + + beforeEach(() => { + migrator = createModelVersionTestMigrator({ + type: getSavedObjectTypes()[PACKAGE_POLICY_SAVED_OBJECT_TYPE], + }); + + policyConfigSO = { + id: 'mock-saved-object-id', + attributes: { + name: 'Some Policy Name', + package: { + name: 'endpoint', + title: '', + version: '', + }, + id: 'endpoint', + policy_id: '', + enabled: true, + namespace: '', + revision: 0, + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', + inputs: [ + { + type: 'endpoint', + enabled: true, + streams: [], + config: { + policy: { + value: { + windows: { + malware: { + mode: 'detect', + }, + antivirus_registration: { + enabled: true, + }, + }, + mac: { + malware: { + mode: 'detect', + }, + }, + linux: { + malware: { + mode: 'detect', + }, + }, + }, + }, + }, + }, + ], + }, + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + references: [], + }; + }); + + describe('when updating to model version 10', () => { + it('should change `on_write_scan` from `true` to `false` if Malware is off', () => { + setMalwareMode(policyConfigSO, 'off'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(false, migratedPolicyConfigSO); + }); + + it('should not change `on_write_scan` if Malware is detect', () => { + setMalwareMode(policyConfigSO, 'detect'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + + it('should not change `on_write_scan` if Malware is prevent', () => { + setMalwareMode(policyConfigSO, 'prevent'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + }); + + describe('additional test: when updating from model version 5 to model version 10', () => { + it('should add `on_write_scan=false` if Malware is off', () => { + setMalwareMode(policyConfigSO, 'off'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(false, migratedPolicyConfigSO); + }); + + it('should add `on_write_scan=true` if Malware is detect', () => { + setMalwareMode(policyConfigSO, 'detect'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + + it('should add `on_write_scan=true` if Malware is prevent', () => { + setMalwareMode(policyConfigSO, 'prevent'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + }); + + const setMalwareMode = (so: SavedObject, level: 'off' | 'detect' | 'prevent') => { + const config = so.attributes.inputs[0].config?.policy.value; + + config.windows.malware.mode = level; + config.mac.malware.mode = level; + config.linux.malware.mode = level; + }; + + const setOnWriteScan = (so: SavedObject, value: boolean) => { + const config = so.attributes.inputs[0].config?.policy.value; + + config.windows.malware.on_write_scan = value; + config.mac.malware.on_write_scan = value; + config.linux.malware.on_write_scan = value; + }; + + const expectOnWriteScanToBe = (expectedValue: boolean, so: SavedObject) => { + const config = so.attributes.inputs[0].config?.policy.value; + + expect(config.windows.malware.on_write_scan).toBe(expectedValue); + expect(config.mac.malware.on_write_scan).toBe(expectedValue); + expect(config.linux.malware.on_write_scan).toBe(expectedValue); + }; +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts new file mode 100644 index 0000000000000..7f793f7980164 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts @@ -0,0 +1,42 @@ +/* + * 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 { + SavedObjectModelDataBackfillFn, + SavedObjectUnsanitizedDoc, +} from '@kbn/core-saved-objects-server'; + +import type { PackagePolicy } from '../../../../common'; + +export const packagePolicyV10OnWriteScanFix: SavedObjectModelDataBackfillFn< + PackagePolicy, + PackagePolicy +> = (packagePolicyDoc) => { + if (packagePolicyDoc.attributes.package?.name !== 'endpoint') { + return { attributes: packagePolicyDoc.attributes }; + } + + const updatedPackagePolicyDoc: SavedObjectUnsanitizedDoc = packagePolicyDoc; + + const input = updatedPackagePolicyDoc.attributes.inputs[0]; + + if (input && input.config) { + const policy = input.config.policy.value; + + if (policy.windows.malware.mode === 'off') { + policy.windows.malware.on_write_scan = false; + } + if (policy.mac.malware.mode === 'off') { + policy.mac.malware.on_write_scan = false; + } + if (policy.linux.malware.mode === 'off') { + policy.linux.malware.on_write_scan = false; + } + } + + return { attributes: updatedPackagePolicyDoc.attributes }; +}; From 3c4b33b9897a400cdbe034ee6f8eaa31ad587a9c Mon Sep 17 00:00:00 2001 From: Karen Grigoryan Date: Fri, 17 May 2024 19:06:08 +0200 Subject: [PATCH 64/71] [Security Solution][DQD][API] update results API to use path params in place of query params (#183696) - Changed the `RESULTS_API_ROUTE` to `RESULTS_INDICES_LATEST_ROUTE` with path parameter `{pattern}`. - Updated `getStorageResults` function to use the new route. - Modified tests to reflect the new route and parameter usage. - Updated server route validation to use path parameters instead of query parameters. closes #182868 **This is an internal route api change, so no breaking changes** **Before** ![image](https://github.com/elastic/kibana/assets/1625373/248e07e0-2a10-4658-8541-24330e2dc2ad) **After:** ![image](https://github.com/elastic/kibana/assets/1625373/d0469b33-d240-4de0-9a39-4ab510aa342b) --- .../impl/data_quality/helpers.test.ts | 3 +- .../impl/data_quality/helpers.ts | 6 ++-- .../common/constants.ts | 1 + ....ts => get_results_indices_latest.test.ts} | 28 +++++++++---------- ...sults.ts => get_results_indices_latest.ts} | 16 +++++++---- .../server/routes/results/index.ts | 4 +-- .../server/schemas/result.ts | 4 +-- 7 files changed, 34 insertions(+), 28 deletions(-) rename x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/{get_results.test.ts => get_results_indices_latest.test.ts} (91%) rename x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/{get_results.ts => get_results_indices_latest.ts} (92%) diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts index 48a83974ba94d..8cec06b28d3a3 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.test.ts @@ -1483,10 +1483,9 @@ describe('helpers', () => { }); expect(fetch).toHaveBeenCalledWith( - '/internal/ecs_data_quality_dashboard/results', + '/internal/ecs_data_quality_dashboard/results/indices_latest/auditbeat-*', expect.objectContaining({ method: 'GET', - query: { pattern: 'auditbeat-*' }, }) ); }); diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts index 368a1d62ac72e..c34f8e35ed289 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/helpers.ts @@ -457,6 +457,8 @@ export const getErrorSummaries = ( }; export const RESULTS_API_ROUTE = '/internal/ecs_data_quality_dashboard/results'; +export const RESULTS_INDICES_LATEST_ROUTE = + '/internal/ecs_data_quality_dashboard/results/indices_latest/{pattern}'; export interface StorageResult { batchId: string; @@ -565,11 +567,11 @@ export async function getStorageResults({ abortController: AbortController; }): Promise { try { - const results = await httpFetch(RESULTS_API_ROUTE, { + const route = RESULTS_INDICES_LATEST_ROUTE.replace('{pattern}', pattern); + const results = await httpFetch(route, { method: 'GET', signal: abortController.signal, version: INTERNAL_API_VERSION, - query: { pattern }, }); return results; } catch (err) { diff --git a/x-pack/plugins/ecs_data_quality_dashboard/common/constants.ts b/x-pack/plugins/ecs_data_quality_dashboard/common/constants.ts index d979d22b12b88..7c35cd9b2fbbd 100755 --- a/x-pack/plugins/ecs_data_quality_dashboard/common/constants.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/common/constants.ts @@ -14,4 +14,5 @@ export const GET_INDEX_MAPPINGS = `${BASE_PATH}/mappings/{pattern}`; export const GET_UNALLOWED_FIELD_VALUES = `${BASE_PATH}/unallowed_field_values`; export const GET_ILM_EXPLAIN = `${BASE_PATH}/ilm_explain/{pattern}`; export const RESULTS_ROUTE_PATH = `${BASE_PATH}/results`; +export const RESULTS_INDICES_LATEST_ROUTE_PATH = `${BASE_PATH}/results/indices_latest/{pattern}`; export const INTERNAL_API_VERSION = '1'; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.test.ts similarity index 91% rename from x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts rename to x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.test.ts index 05a714a27275a..7aad4c347a8cb 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.test.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.test.ts @@ -4,13 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { RESULTS_ROUTE_PATH } from '../../../common/constants'; +import { RESULTS_INDICES_LATEST_ROUTE_PATH } from '../../../common/constants'; import { serverMock } from '../../__mocks__/server'; import { requestMock } from '../../__mocks__/request'; import { requestContextMock } from '../../__mocks__/request_context'; -import type { LatestAggResponseBucket } from './get_results'; -import { getResultsRoute, getQuery } from './get_results'; +import type { LatestAggResponseBucket } from './get_results_indices_latest'; +import { getResultsIndicesLatestRoute, getQuery } from './get_results_indices_latest'; import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; import { resultDocument } from './results.mock'; import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; @@ -41,7 +41,7 @@ jest.mock('./privileges', () => ({ mockCheckIndicesPrivileges(params), })); -describe('getResultsRoute route', () => { +describe('getResultsIndicesLatestRoute route', () => { describe('querying', () => { let server: ReturnType; let { context } = requestContextMock.createTools(); @@ -49,8 +49,8 @@ describe('getResultsRoute route', () => { const req = requestMock.create({ method: 'get', - path: RESULTS_ROUTE_PATH, - query: { pattern: 'logs-*' }, + path: RESULTS_INDICES_LATEST_ROUTE_PATH, + params: { pattern: 'logs-*' }, }); beforeEach(() => { @@ -65,7 +65,7 @@ describe('getResultsRoute route', () => { [resultDocument.indexName]: {}, }); - getResultsRoute(server.router, logger); + getResultsIndicesLatestRoute(server.router, logger); }); it('gets result', async () => { @@ -114,8 +114,8 @@ describe('getResultsRoute route', () => { const req = requestMock.create({ method: 'get', - path: RESULTS_ROUTE_PATH, - query: { pattern: 'logs-*' }, + path: RESULTS_INDICES_LATEST_ROUTE_PATH, + params: { pattern: 'logs-*' }, }); beforeEach(() => { @@ -132,7 +132,7 @@ describe('getResultsRoute route', () => { [resultDocument.indexName]: {}, }); - getResultsRoute(server.router, logger); + getResultsIndicesLatestRoute(server.router, logger); }); it('should authorize indices from pattern', async () => { @@ -225,14 +225,14 @@ describe('getResultsRoute route', () => { beforeEach(() => { server = serverMock.create(); logger = loggerMock.create(); - getResultsRoute(server.router, logger); + getResultsIndicesLatestRoute(server.router, logger); }); - test('disallows invalid query param', () => { + test('disallows invalid path param', () => { const req = requestMock.create({ method: 'get', - path: RESULTS_ROUTE_PATH, - query: {}, + path: RESULTS_INDICES_LATEST_ROUTE_PATH, + params: {}, }); const result = server.validate(req); diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.ts similarity index 92% rename from x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts rename to x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.ts index 6c410e88f3626..a4508e1471b3b 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/get_results_indices_latest.ts @@ -7,10 +7,10 @@ import type { IRouter, Logger } from '@kbn/core/server'; -import { RESULTS_ROUTE_PATH, INTERNAL_API_VERSION } from '../../../common/constants'; +import { INTERNAL_API_VERSION, RESULTS_INDICES_LATEST_ROUTE_PATH } from '../../../common/constants'; import { buildResponse } from '../../lib/build_response'; import { buildRouteValidation } from '../../schemas/common'; -import { GetResultQuery } from '../../schemas/result'; +import { GetResultParams } from '../../schemas/result'; import type { ResultDocument } from '../../schemas/result'; import { API_DEFAULT_ERROR_MESSAGE } from '../../translations'; import type { DataQualityDashboardRequestHandlerContext } from '../../types'; @@ -33,20 +33,24 @@ export interface LatestAggResponseBucket { latest_doc: { hits: { hits: Array<{ _source: ResultDocument }> } }; } -export const getResultsRoute = ( +export const getResultsIndicesLatestRoute = ( router: IRouter, logger: Logger ) => { router.versioned .get({ - path: RESULTS_ROUTE_PATH, + path: RESULTS_INDICES_LATEST_ROUTE_PATH, access: 'internal', options: { tags: ['access:securitySolution'] }, }) .addVersion( { version: INTERNAL_API_VERSION, - validate: { request: { query: buildRouteValidation(GetResultQuery) } }, + validate: { + request: { + params: buildRouteValidation(GetResultParams), + }, + }, }, async (context, request, response) => { const services = await context.resolve(['core', 'dataQualityDashboard']); @@ -65,7 +69,7 @@ export const getResultsRoute = ( try { const { client } = services.core.elasticsearch; - const { pattern } = request.query; + const { pattern } = request.params; // Discover all indices for the pattern using internal user const indicesResponse = await client.asInternalUser.indices.get({ diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/index.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/index.ts index 25d4a913a1946..d403ece283b1d 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/index.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/routes/results/index.ts @@ -8,7 +8,7 @@ import type { IRouter, Logger } from '@kbn/core/server'; import { postResultsRoute } from './post_results'; -import { getResultsRoute } from './get_results'; +import { getResultsIndicesLatestRoute } from './get_results_indices_latest'; import type { DataQualityDashboardRequestHandlerContext } from '../../types'; export const resultsRoutes = ( @@ -16,5 +16,5 @@ export const resultsRoutes = ( logger: Logger ) => { postResultsRoute(router, logger); - getResultsRoute(router, logger); + getResultsIndicesLatestRoute(router, logger); }; diff --git a/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts b/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts index 9e8b7540bf2e5..055930d82cefb 100644 --- a/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts +++ b/x-pack/plugins/ecs_data_quality_dashboard/server/schemas/result.ts @@ -39,5 +39,5 @@ export type ResultDocument = t.TypeOf; export const PostResultBody = ResultDocument; -export const GetResultQuery = t.type({ pattern: t.string }); -export type GetResultQuery = t.TypeOf; +export const GetResultParams = t.type({ pattern: t.string }); +export type GetResultParams = t.TypeOf; From a81138efece7ac4cfbe0b59cd35e2bec9d38964c Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Fri, 17 May 2024 18:12:17 +0100 Subject: [PATCH 65/71] Revert "[Entity Analytics] Move scripted metric painless scripts to static file & remove category based weighting" (#183759) Reverts elastic/kibana#182038 --- .../api/entity_analytics/common/common.gen.ts | 32 ++++- .../common/common.schema.yaml | 55 +++++++- .../common/risk_weights.schema.test.ts | 81 +++++++++++- .../risk_score/calculate_risk_scores.ts | 106 ++++++++++++--- .../entity_analytics/risk_score/constants.ts | 5 + .../risk_score/painless/index.test.ts | 24 ---- .../risk_score/painless/index.ts | 40 ------ .../painless/risk_scoring_combine.painless | 1 - .../painless/risk_scoring_init.painless | 1 - .../painless/risk_scoring_map.painless | 8 -- .../painless/risk_scoring_reduce.painless | 38 ------ .../risk_score/risk_weights.test.ts | 123 ++++++++++++++++++ .../risk_score/risk_weights.ts | 103 +++++++++++++++ .../risk_score/routes/preview.test.ts | 15 ++- .../risk_score_preview.ts | 50 +++++++ 15 files changed, 542 insertions(+), 140 deletions(-) delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts index f17cb2a5ee0fb..b037534248042 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts @@ -155,8 +155,8 @@ export const RiskScoreWeightGlobalShared = z.object({ type: z.literal('global_identifier'), }); -export type RiskScoreWeight = z.infer; -export const RiskScoreWeight = z.union([ +export type RiskScoreWeightGlobal = z.infer; +export const RiskScoreWeightGlobal = z.union([ RiskScoreWeightGlobalShared.merge( z.object({ host: RiskScoreEntityIdentifierWeights, @@ -171,6 +171,34 @@ export const RiskScoreWeight = z.union([ ), ]); +export type RiskScoreWeightCategoryShared = z.infer; +export const RiskScoreWeightCategoryShared = z.object({ + type: z.literal('risk_category'), + value: RiskScoreCategories, +}); + +export type RiskScoreWeightCategory = z.infer; +export const RiskScoreWeightCategory = z.union([ + RiskScoreWeightCategoryShared.merge( + z.object({ + host: RiskScoreEntityIdentifierWeights, + user: RiskScoreEntityIdentifierWeights.optional(), + }) + ), + RiskScoreWeightCategoryShared.merge( + z.object({ + host: RiskScoreEntityIdentifierWeights.optional(), + user: RiskScoreEntityIdentifierWeights, + }) + ), +]); + +/** + * Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1'). + */ +export type RiskScoreWeight = z.infer; +export const RiskScoreWeight = z.union([RiskScoreWeightGlobal, RiskScoreWeightCategory]); + /** * A list of weights to be applied to the scoring calculation. */ diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml index 63aa739d2133d..7b6634876d8a6 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml @@ -201,7 +201,7 @@ components: enum: - global_identifier - RiskScoreWeight: + RiskScoreWeightGlobal: oneOf: - allOf: - $ref: '#/components/schemas/RiskScoreWeightGlobalShared' @@ -225,12 +225,65 @@ components: user: $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + RiskScoreWeightCategoryShared: + x-inline: true + type: object + required: + - type + - value + properties: + type: + type: string + enum: + - risk_category + value: + $ref: '#/components/schemas/RiskScoreCategories' + + RiskScoreWeightCategory: + oneOf: + - allOf: + - $ref: '#/components/schemas/RiskScoreWeightCategoryShared' + - type: object + required: + - host + properties: + host: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + user: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + + - allOf: + - $ref: '#/components/schemas/RiskScoreWeightCategoryShared' + - type: object + required: + - user + properties: + host: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + user: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + + RiskScoreWeight: + description: "Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1')." + oneOf: + - $ref: '#/components/schemas/RiskScoreWeightGlobal' + - $ref: '#/components/schemas/RiskScoreWeightCategory' + example: + type: 'risk_category' + value: 'category_1' + host: 0.8 + user: 0.4 + RiskScoreWeights: description: 'A list of weights to be applied to the scoring calculation.' type: array items: $ref: '#/components/schemas/RiskScoreWeight' example: + - type: 'risk_category' + value: 'category_1' + host: 0.8 + user: 0.4 - type: 'global_identifier' host: 0.5 user: 0.1 diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts index e4afc38badd24..59b0859300f88 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts @@ -59,7 +59,9 @@ describe('risk weight schema', () => { const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; expect(decoded.success).toBeFalsy(); - expect(stringifyZodError(decoded.error)).toContain('host: Required, user: Required'); + expect(stringifyZodError(decoded.error)).toEqual( + 'host: Required, user: Required, type: Invalid literal value, expected "risk_category", value: Invalid literal value, expected "category_1", host: Required, and 3 more' + ); }); it('allows a single host weight', () => { @@ -121,10 +123,44 @@ describe('risk weight schema', () => { expect(decoded.success).toBeFalsy(); expect(stringifyZodError(decoded.error)).toEqual( - 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier"' + 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier", value: Invalid literal value, expected "category_1", host: Required, and 1 more' ); }); + it('rejects if neither host nor user weight are specified', () => { + const payload = { type, value: RiskCategories.category_1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(stringifyZodError(decoded.error)).toEqual( + 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier", user: Required, host: Required, and 1 more' + ); + }); + + it('allows a single host weight', () => { + const payload = { type, value: RiskCategories.category_1, host: 0.1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows a single user weight', () => { + const payload = { type, value: RiskCategories.category_1, user: 0.1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows both a host and user weight', () => { + const payload = { type, value: RiskCategories.category_1, user: 0.1, host: 0.5 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + it('rejects a weight outside of 0-1', () => { const payload = { type, value: RiskCategories.category_1, host: -5 }; const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; @@ -134,6 +170,47 @@ describe('risk weight schema', () => { `host: Number must be greater than or equal to 0` ); }); + + it('removes extra keys if specified', () => { + const payload = { + type, + value: RiskCategories.category_1, + host: 0.1, + extra: 'even more', + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual({ type, value: RiskCategories.category_1, host: 0.1 }); + }); + + describe('allowed category values', () => { + it('allows the alerts type for a category', () => { + const payload = { + type, + value: RiskCategories.category_1, + host: 0.1, + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('rejects an unknown category value', () => { + const payload = { + type, + value: 'unknown', + host: 0.1, + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(stringifyZodError(decoded.error)).toContain( + 'type: Invalid literal value, expected "global_identifier", type: Invalid literal value, expected "global_identifier", user: Required, value: Invalid literal value, expected "category_1", value: Invalid literal value, expected "category_1", and 1 more' + ); + }); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts index 9f99a9ae4a561..3c930ec07e666 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts @@ -13,7 +13,10 @@ import type { import type { ElasticsearchClient, Logger } from '@kbn/core/server'; import { ALERT_RISK_SCORE, + ALERT_RULE_NAME, + ALERT_UUID, ALERT_WORKFLOW_STATUS, + EVENT_KIND, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; import type { RiskScoresPreviewResponse } from '../../../../common/api/entity_analytics/risk_engine/preview_route.gen'; import type { @@ -25,7 +28,6 @@ import { type IdentifierType, getRiskLevel, RiskCategories, - RiskWeightTypes, } from '../../../../common/entity_analytics/risk_engine'; import { withSecuritySpan } from '../../../utils/with_security_span'; import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics'; @@ -36,13 +38,24 @@ import { normalize, } from '../asset_criticality/helpers'; import { getAfterKeyForIdentifierType, getFieldForIdentifier } from './helpers'; +import { + buildCategoryCountDeclarations, + buildCategoryAssignment, + buildCategoryScoreDeclarations, + buildWeightingOfScoreByCategory, + getGlobalWeightForIdentifierType, +} from './risk_weights'; import type { CalculateRiskScoreAggregations, CalculateScoresParams, RiskScoreBucket, } from '../types'; -import { RISK_SCORING_SUM_MAX, RISK_SCORING_SUM_VALUE } from './constants'; -import { getPainlessScripts, type PainlessScripts } from './painless'; +import { + MAX_INPUTS_COUNT, + RISK_SCORING_INPUTS_COUNT_MAX, + RISK_SCORING_SUM_MAX, + RISK_SCORING_SUM_VALUE, +} from './constants'; const formatForResponse = ({ bucket, @@ -105,22 +118,67 @@ const filterFromRange = (range: CalculateScoresParams['range']): QueryDslQueryCo range: { '@timestamp': { lt: range.end, gte: range.start } }, }); +const buildReduceScript = ({ + globalIdentifierTypeWeight, +}: { + globalIdentifierTypeWeight?: number; +}): string => { + return ` + Map results = new HashMap(); + List inputs = []; + for (state in states) { + inputs.addAll(state.inputs) + } + Collections.sort(inputs, (a, b) -> b.get('weighted_score').compareTo(a.get('weighted_score'))); + + double num_inputs_to_score = Math.min(inputs.length, params.max_risk_inputs_per_identity); + results['notes'] = []; + if (num_inputs_to_score == params.max_risk_inputs_per_identity) { + results['notes'].add('Number of risk inputs (' + inputs.length + ') exceeded the maximum allowed (' + params.max_risk_inputs_per_identity + ').'); + } + + ${buildCategoryScoreDeclarations()} + ${buildCategoryCountDeclarations()} + + double total_score = 0; + double current_score = 0; + List risk_inputs = []; + for (int i = 0; i < num_inputs_to_score; i++) { + current_score = inputs[i].weighted_score / Math.pow(i + 1, params.p); + + if (i < ${MAX_INPUTS_COUNT}) { + inputs[i]["contribution"] = 100 * current_score / params.risk_cap; + risk_inputs.add(inputs[i]); + } + + ${buildCategoryAssignment()} + total_score += current_score; + } + + ${globalIdentifierTypeWeight != null ? `total_score *= ${globalIdentifierTypeWeight};` : ''} + double score_norm = 100 * total_score / params.risk_cap; + results['score'] = total_score; + results['normalized_score'] = score_norm; + results['risk_inputs'] = risk_inputs; + + return results; + `; +}; + const buildIdentifierTypeAggregation = ({ afterKeys, identifierType, pageSize, weights, alertSampleSizePerShard, - scriptedMetricPainless, }: { afterKeys: AfterKeys; identifierType: IdentifierType; pageSize: number; weights?: RiskScoreWeights; alertSampleSizePerShard: number; - scriptedMetricPainless: PainlessScripts; }): AggregationsAggregationContainer => { - const globalIdentifierTypeWeight = getGlobalWeightForIdentifierType(identifierType, weights); + const globalIdentifierTypeWeight = getGlobalWeightForIdentifierType({ identifierType, weights }); const identifierField = getFieldForIdentifier(identifierType); return { @@ -146,15 +204,33 @@ const buildIdentifierTypeAggregation = ({ aggs: { risk_details: { scripted_metric: { - init_script: scriptedMetricPainless.init, - map_script: scriptedMetricPainless.map, - combine_script: scriptedMetricPainless.combine, + init_script: 'state.inputs = []', + map_script: ` + Map fields = new HashMap(); + String category = doc['${EVENT_KIND}'].value; + double score = doc['${ALERT_RISK_SCORE}'].value; + double weighted_score = 0.0; + + fields.put('time', doc['@timestamp'].value); + fields.put('rule_name', doc['${ALERT_RULE_NAME}'].value); + + fields.put('category', category); + fields.put('index', doc['_index'].value); + fields.put('id', doc['${ALERT_UUID}'].value); + fields.put('score', score); + + ${buildWeightingOfScoreByCategory({ userWeights: weights, identifierType })} + fields.put('weighted_score', weighted_score); + + state.inputs.add(fields); + `, + combine_script: 'return state;', params: { + max_risk_inputs_per_identity: RISK_SCORING_INPUTS_COUNT_MAX, p: RISK_SCORING_SUM_VALUE, risk_cap: RISK_SCORING_SUM_MAX, - global_identifier_type_weight: globalIdentifierTypeWeight || 1, }, - reduce_script: scriptedMetricPainless.reduce, + reduce_script: buildReduceScript({ globalIdentifierTypeWeight }), }, }, }, @@ -210,12 +286,6 @@ const processScores = async ({ }); }; -export const getGlobalWeightForIdentifierType = ( - identifierType: IdentifierType, - weights?: RiskScoreWeights -): number | undefined => - weights?.find((weight) => weight.type === RiskWeightTypes.global)?.[identifierType]; - export const calculateRiskScores = async ({ afterKeys: userAfterKeys, assetCriticalityService, @@ -237,7 +307,6 @@ export const calculateRiskScores = async ({ } & CalculateScoresParams): Promise => withSecuritySpan('calculateRiskScores', async () => { const now = new Date().toISOString(); - const scriptedMetricPainless = await getPainlessScripts(); const filter = [ filterFromRange(range), { bool: { must_not: { term: { [ALERT_WORKFLOW_STATUS]: 'closed' } } } }, @@ -276,7 +345,6 @@ export const calculateRiskScores = async ({ pageSize, weights, alertSampleSizePerShard, - scriptedMetricPainless, }); return aggs; }, {} as Record), diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts index 6a691eac42734..73f93d71b11f9 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts @@ -16,6 +16,11 @@ export const RISK_SCORING_SUM_VALUE = 1.5; */ export const RISK_SCORING_SUM_MAX = 261.2; +/** + * The risk scoring algorithm can only process a finite number of risk inputs per identity; this value represents the maximum number of inputs that will be processed. + */ +export const RISK_SCORING_INPUTS_COUNT_MAX = 999999; + /** * This value represents the maximum possible risk score after normalization. */ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts deleted file mode 100644 index 4c3b6f3c156b3..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts +++ /dev/null @@ -1,24 +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 { getPainlessScripts } from '.'; - -describe('getPainlessScripts', () => { - // to update snapshot run `yarn test:jest x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.test.ts -u` - test('Scripts should not have changed. If this change is intentional, ensure that Serverless scripted metric allowlists are updated', async () => { - const scripts = await getPainlessScripts(); - - expect(scripts).toMatchInlineSnapshot(` - Object { - "combine": "return state;", - "init": "state.inputs = []", - "map": "Map fields = new HashMap();fields.put('id', doc['kibana.alert.uuid'].value);fields.put('index', doc['_index'].value);fields.put('time', doc['@timestamp'].value);fields.put('rule_name', doc['kibana.alert.rule.name'].value);fields.put('category', doc['event.kind'].value);fields.put('score', doc['kibana.alert.risk_score'].value);state.inputs.add(fields); ", - "reduce": "Map results = new HashMap();results['notes'] = [];results['category_1_score'] = 0.0;results['category_1_count'] = 0;results['risk_inputs'] = [];results['score'] = 0.0;def inputs = states[0].inputs;for (int i = 0; i < inputs.length; i++) { double current_score = inputs[i].score / Math.pow(i + 1, params.p); if (i < 10) { inputs[i][\\"contribution\\"] = 100 * current_score / params.risk_cap; results['risk_inputs'].add(inputs[i]); } results['category_1_score'] += current_score; results['category_1_count'] += 1; results['score'] += current_score;}results['score'] *= params.global_identifier_type_weight;results['normalized_score'] = 100 * results['score'] / params.risk_cap;return results;", - } - `); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts deleted file mode 100644 index 896340262b21c..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/index.ts +++ /dev/null @@ -1,40 +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 fs from 'fs'; -import { flow } from 'lodash'; - -const PHASES = ['init', 'map', 'combine', 'reduce'] as const; - -type Phase = typeof PHASES[number]; -export type PainlessScripts = Record; - -const removeNewlines = (content: string) => content.replace(/\n/g, ''); -const condenseMultipleSpaces = (content: string) => content.replace(/\s+/g, ' '); -const removeComments = (content: string) => content.replace(/\/\/.*/g, ''); -const minifyContent = flow(removeComments, removeNewlines, condenseMultipleSpaces); - -const readScript = async (phase: Phase) => { - const content = await fs.promises.readFile(`${__dirname}/risk_scoring_${phase}.painless`, 'utf8'); - return minifyContent(content); -}; - -let cache: PainlessScripts | undefined; - -export const getPainlessScripts = async (): Promise => { - if (cache) { - return cache; - } - - const [init, map, combine, reduce] = await Promise.all(PHASES.map(readScript)); - - // The cache will only ever have one value, so we can safely update it - // un-atomicly without worrying about lost updates. - // eslint-disable-next-line require-atomic-updates - cache = { init, map, combine, reduce }; - return cache; -}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless deleted file mode 100644 index da2a75d569f18..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_combine.painless +++ /dev/null @@ -1 +0,0 @@ -return state; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless deleted file mode 100644 index 5ee9376d701ad..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_init.painless +++ /dev/null @@ -1 +0,0 @@ -state.inputs = [] diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless deleted file mode 100644 index 3d79df51be0e8..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_map.painless +++ /dev/null @@ -1,8 +0,0 @@ -Map fields = new HashMap(); -fields.put('id', doc['kibana.alert.uuid'].value); -fields.put('index', doc['_index'].value); -fields.put('time', doc['@timestamp'].value); -fields.put('rule_name', doc['kibana.alert.rule.name'].value); -fields.put('category', doc['event.kind'].value); -fields.put('score', doc['kibana.alert.risk_score'].value); -state.inputs.add(fields); \ No newline at end of file diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless deleted file mode 100644 index 88140b5ccd283..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/painless/risk_scoring_reduce.painless +++ /dev/null @@ -1,38 +0,0 @@ -Map results = new HashMap(); -results['notes'] = []; -results['category_1_score'] = 0.0; -results['category_1_count'] = 0; -results['risk_inputs'] = []; -results['score'] = 0.0; - -def inputs = states[0].inputs; - -// Currently the alerts index only has one shard so there will only be one state and we do not need to sort them -// If there are multiple shards we will need these lines -// List inputs = []; -// for (state in states) { -// inputs.addAll(state.inputs) -// } -// Collections.sort(inputs, (a, b) -> b.get('score').compareTo(a.get('score'))); - -for (int i = 0; i < inputs.length; i++) { - double current_score = inputs[i].score / Math.pow(i + 1, params.p); - - if (i < 10) { - inputs[i]["contribution"] = 100 * current_score / params.risk_cap; - results['risk_inputs'].add(inputs[i]); - } - -// every input is of type signal at the moment -// if (inputs[i].category == 'signal') { - results['category_1_score'] += current_score; - results['category_1_count'] += 1; -// } - - results['score'] += current_score; -} - -results['score'] *= params.global_identifier_type_weight; -results['normalized_score'] = 100 * results['score'] / params.risk_cap; - -return results; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts new file mode 100644 index 0000000000000..86bdc0d0e6be0 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts @@ -0,0 +1,123 @@ +/* + * 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 { RiskWeightTypes, RiskCategories } from '../../../../common/entity_analytics/risk_engine'; +import { + buildCategoryAssignment, + buildCategoryWeights, + buildWeightingOfScoreByCategory, +} from './risk_weights'; + +describe('buildCategoryWeights', () => { + it('returns the default weights if nothing else is provided', () => { + const result = buildCategoryWeights(); + + expect(result).toEqual([ + { host: 1, type: RiskWeightTypes.riskCategory, user: 1, value: RiskCategories.category_1 }, + ]); + }); + + it('allows user weights to override defaults', () => { + const result = buildCategoryWeights([ + { + type: RiskWeightTypes.riskCategory, + value: RiskCategories.category_1, + host: 0.1, + user: 0.2, + }, + ]); + + expect(result).toEqual([ + { + host: 0.1, + type: RiskWeightTypes.riskCategory, + user: 0.2, + value: RiskCategories.category_1, + }, + ]); + }); + + it('uses default category weights if unspecified in user-provided weight', () => { + const result = buildCategoryWeights([ + { type: RiskWeightTypes.riskCategory, value: RiskCategories.category_1, host: 0.1 }, + ]); + + expect(result).toEqual([ + { host: 0.1, type: RiskWeightTypes.riskCategory, user: 1, value: RiskCategories.category_1 }, + ]); + }); +}); + +describe('buildCategoryAssignment', () => { + it('builds the expected assignment statement', () => { + const result = buildCategoryAssignment(); + + expect(result).toMatchInlineSnapshot( + `"if (inputs[i].category == 'signal') { results['category_1_score'] += current_score; results['category_1_count'] += 1; }"` + ); + }); +}); + +describe('buildWeightingOfScoreByCategory', () => { + it('returns default weights if no user values provided', () => { + const result = buildWeightingOfScoreByCategory({ identifierType: 'user' }); + + expect(result).toMatchInlineSnapshot( + `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` + ); + }); + + it('returns default weights if no weights provided', () => { + const result = buildWeightingOfScoreByCategory({ userWeights: [], identifierType: 'host' }); + + expect(result).toMatchInlineSnapshot( + `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` + ); + }); + + it('returns default weights if only global weights provided', () => { + const result = buildWeightingOfScoreByCategory({ + userWeights: [{ type: RiskWeightTypes.global, host: 0.1 }], + identifierType: 'host', + }); + + expect(result).toMatchInlineSnapshot( + `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` + ); + }); + + it('returns specified weight when a category weight is provided', () => { + const result = buildWeightingOfScoreByCategory({ + userWeights: [ + { + type: RiskWeightTypes.riskCategory, + value: RiskCategories.category_1, + host: 0.1, + user: 0.2, + }, + ], + identifierType: 'host', + }); + + expect(result).toMatchInlineSnapshot( + `"if (category == 'signal') { weighted_score = score * 0.1; } else { weighted_score = score; }"` + ); + }); + + it('returns a default weight when a category weight is provided but not the one being used', () => { + const result = buildWeightingOfScoreByCategory({ + userWeights: [ + { type: RiskWeightTypes.riskCategory, value: RiskCategories.category_1, host: 0.1 }, + ], + identifierType: 'user', + }); + + expect(result).toMatchInlineSnapshot( + `"if (category == 'signal') { weighted_score = score * 1; } else { weighted_score = score; }"` + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts new file mode 100644 index 0000000000000..d0c7486324e30 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts @@ -0,0 +1,103 @@ +/* + * 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 { keyBy, merge } from 'lodash'; +import type { + RiskScoreWeight, + RiskScoreWeightCategory, + RiskScoreWeightGlobal, + RiskScoreWeights, +} from '../../../../common/api/entity_analytics/common'; +import type { IdentifierType } from '../../../../common/entity_analytics/risk_engine'; +import { RiskCategories, RiskWeightTypes } from '../../../../common/entity_analytics/risk_engine'; + +const RISK_CATEGORIES = Object.values(RiskCategories); + +const DEFAULT_CATEGORY_WEIGHTS: RiskScoreWeights = RISK_CATEGORIES.map((category) => ({ + type: RiskWeightTypes.riskCategory, + value: category, + host: 1, + user: 1, +})); + +/* + * This function and its use can be deleted once we've replaced our use of event.kind with a proper risk category field. + */ +const convertCategoryToEventKindValue = (category?: string): string | undefined => + category === 'category_1' ? 'signal' : category; + +const isGlobalIdentifierTypeWeight = (weight: RiskScoreWeight): weight is RiskScoreWeightGlobal => + weight.type === RiskWeightTypes.global; +const isRiskCategoryWeight = (weight: RiskScoreWeight): weight is RiskScoreWeightCategory => + weight.type === RiskWeightTypes.riskCategory; + +export const getGlobalWeightForIdentifierType = ({ + identifierType, + weights, +}: { + identifierType: IdentifierType; + weights?: RiskScoreWeights; +}): number | undefined => { + return weights?.find(isGlobalIdentifierTypeWeight)?.[identifierType]; +}; + +const getRiskCategoryWeights = (weights?: RiskScoreWeights): RiskScoreWeightCategory[] => + weights?.filter(isRiskCategoryWeight) ?? []; + +const getWeightForIdentifierType = ( + weight: RiskScoreWeight, + identifierType: IdentifierType +): number => { + const configuredWeight = weight[identifierType]; + return typeof configuredWeight === 'number' ? configuredWeight : 1; +}; + +export const buildCategoryScoreDeclarations = (): string => { + return RISK_CATEGORIES.map((riskCategory) => `results['${riskCategory}_score'] = 0.0;`).join(''); +}; + +export const buildCategoryCountDeclarations = (): string => { + return RISK_CATEGORIES.map((riskCategory) => `results['${riskCategory}_count'] = 0;`).join(''); +}; + +export const buildCategoryWeights = (userWeights?: RiskScoreWeights): RiskScoreWeightCategory[] => { + const categoryWeights = getRiskCategoryWeights(userWeights); + + return Object.values( + merge({}, keyBy(DEFAULT_CATEGORY_WEIGHTS, 'value'), keyBy(categoryWeights, 'value')) + ); +}; + +export const buildCategoryAssignment = (): string => { + return RISK_CATEGORIES.map( + (category) => + `if (inputs[i].category == '${convertCategoryToEventKindValue( + category + )}') { results['${category}_score'] += current_score; results['${category}_count'] += 1; }` + ).join(' else '); +}; + +export const buildWeightingOfScoreByCategory = ({ + userWeights, + identifierType, +}: { + userWeights?: RiskScoreWeights; + identifierType: IdentifierType; +}): string => { + const otherClause = `weighted_score = score;`; + const categoryWeights = buildCategoryWeights(userWeights); + + return categoryWeights + .map( + (weight) => + `if (category == '${convertCategoryToEventKindValue( + weight.value + )}') { weighted_score = score * ${getWeightForIdentifierType(weight, identifierType)}; }` + ) + .join(' else ') + .concat(` else { ${otherClause} }`); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts index b5ff9c3487a07..a35f4978ebf2c 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts @@ -8,7 +8,10 @@ import { loggerMock } from '@kbn/logging-mocks'; import { RISK_SCORE_PREVIEW_URL } from '../../../../../common/constants'; -import { RiskWeightTypes } from '../../../../../common/entity_analytics/risk_engine'; +import { + RiskCategories, + RiskWeightTypes, +} from '../../../../../common/entity_analytics/risk_engine'; import { serverMock, requestContextMock, @@ -168,7 +171,8 @@ describe('POST risk_engine/preview route', () => { const request = buildRequest({ weights: [ { - type: RiskWeightTypes.global, + type: RiskWeightTypes.riskCategory, + value: RiskCategories.category_1, host: 0.1, user: 0.2, }, @@ -182,7 +186,8 @@ describe('POST risk_engine/preview route', () => { expect.objectContaining({ weights: [ { - type: RiskWeightTypes.global, + type: RiskWeightTypes.riskCategory, + value: RiskCategories.category_1, host: 0.1, user: 0.2, }, @@ -195,7 +200,8 @@ describe('POST risk_engine/preview route', () => { const request = buildRequest({ weights: [ { - type: RiskWeightTypes.global, + type: RiskWeightTypes.riskCategory, + value: RiskCategories.category_1, host: 1.1, }, ], @@ -212,6 +218,7 @@ describe('POST risk_engine/preview route', () => { weights: [ { type: 'something new', + value: RiskCategories.category_1, host: 0.1, user: 0.2, }, diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts index 28ebe8dae5f56..98ea7b172583c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts @@ -514,6 +514,56 @@ export default ({ getService }: FtrProviderContext): void => { }); }); + context('with category weights', () => { + it('weights risk inputs from different categories according to the category weight', async () => { + const documentId = uuidv4(); + const userSignal = buildDocument( + { 'event.kind': 'signal', 'user.name': 'user-1' }, + documentId + ); + const hostSignal = buildDocument( + { 'event.kind': 'signal', 'host.name': 'host-1' }, + documentId + ); + await indexListOfDocuments(Array(50).fill(userSignal).concat(Array(50).fill(hostSignal))); + + await createAndSyncRuleAndAlerts({ + query: `id: ${documentId}`, + alerts: 100, + riskScore: 100, + }); + const { scores } = await previewRiskScores({ + body: { + weights: [{ type: 'risk_category', value: 'category_1', host: 0.4, user: 0.8 }], + }, + }); + + expect(sanitizeScores(scores.host!)).to.eql([ + { + calculated_level: 'Low', + calculated_score: 93.2375911647125, + calculated_score_norm: 35.695861854790394, + category_1_score: 35.69586185479039, + category_1_count: 50, + id_field: 'host.name', + id_value: 'host-1', + }, + ]); + + expect(sanitizeScores(scores.user!)).to.eql([ + { + calculated_level: 'High', + calculated_score: 186.475182329425, + calculated_score_norm: 71.39172370958079, + category_1_score: 71.39172370958077, + category_1_count: 50, + id_field: 'user.name', + id_value: 'user-1', + }, + ]); + }); + }); + describe('@skipInServerless with asset criticality data', () => { const assetCriticalityRoutes = assetCriticalityRouteHelpersFactory(supertest); From 4ca6b4a4048a1c0cdfd48ddb29676fd4b3bd3be6 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Fri, 17 May 2024 13:15:45 -0400 Subject: [PATCH 66/71] [Fleet] Add agent privileges telemetry (#183685) --- .../server/collectors/agent_collectors.ts | 27 +++++++++++++++++++ .../fleet_usage_telemetry.test.ts | 6 +++++ .../services/metrics/fetch_agent_metrics.ts | 1 + .../services/metrics/fleet_metrics_task.ts | 2 +- .../services/telemetry/fleet_usage_sender.ts | 8 ++++++ .../services/telemetry/fleet_usages_schema.ts | 20 ++++++++++++++ 6 files changed, 63 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts index 391114aa992b3..ad23bea85d365 100644 --- a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts +++ b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts @@ -69,6 +69,10 @@ export interface AgentData { error: number; degraded: number; }; + agents_per_privileges: { + root: number; + unprivileged: number; + }; agents_per_policy: number[]; agents_per_os: Array<{ name: string; @@ -89,6 +93,10 @@ const DEFAULT_AGENT_DATA = { agents_per_version: [], agents_per_os: [], upgrade_details: [], + agents_per_privileges: { + root: 0, + unprivileged: 0, + }, }; export const getAgentData = async ( @@ -159,6 +167,18 @@ export const getAgentData = async ( ], }, }, + privileges: { + filters: { + other_bucket_key: 'root', + filters: { + unprivileged: { + match: { + 'local_metadata.elastic.agent.unprivileged': true, + }, + }, + }, + }, + }, }, }, { signal: abortController.signal } @@ -176,6 +196,12 @@ export const getAgentData = async ( }) ); + const agentsPerPrivileges = { + root: (response?.aggregations?.privileges as any)?.buckets?.root?.doc_count ?? 0, + unprivileged: + (response?.aggregations?.privileges as any)?.buckets?.unprivileged?.doc_count ?? 0, + }; + const getAgentStatusesPerVersion = async (version: string) => { return await getAgentStatusForAgentPolicy( esClient, @@ -227,6 +253,7 @@ export const getAgentData = async ( agent_checkin_status: statuses, agents_per_policy: agentsPerPolicy, agents_per_version: agentsPerVersion, + agents_per_privileges: agentsPerPrivileges, agents_per_os: agentsPerOS, upgrade_details: upgradeDetails, }; diff --git a/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts b/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts index 430dc6f745ad9..dbce3c7f8b435 100644 --- a/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts @@ -135,6 +135,7 @@ describe('fleet usage telemetry', () => { name: 'Ubuntu', version: '22.04.2 LTS (Jammy Jellyfish)', }, + elastic: { agent: { unprivileged: false } }, // Root agent }, components: [ { @@ -172,6 +173,7 @@ describe('fleet usage telemetry', () => { name: 'Ubuntu', version: '20.04.5 LTS (Focal Fossa)', }, + elastic: { agent: { unprivileged: true } }, // Non root agent }, components: [ { @@ -482,6 +484,10 @@ describe('fleet usage telemetry', () => { num_host_urls: 0, }, packages: [], + agents_per_privileges: { + root: 3, + unprivileged: 1, + }, agents_per_version: [ { version: '8.6.0', diff --git a/x-pack/plugins/fleet/server/services/metrics/fetch_agent_metrics.ts b/x-pack/plugins/fleet/server/services/metrics/fetch_agent_metrics.ts index 5662a1a8fa1c8..ac5e692b9023e 100644 --- a/x-pack/plugins/fleet/server/services/metrics/fetch_agent_metrics.ts +++ b/x-pack/plugins/fleet/server/services/metrics/fetch_agent_metrics.ts @@ -64,6 +64,7 @@ export const fetchAgentMetrics = async ( upgrading_step: await getUpgradingSteps(esClient, abortController), unhealthy_reason: await getUnhealthyReason(esClient, abortController), }; + return usage; }; diff --git a/x-pack/plugins/fleet/server/services/metrics/fleet_metrics_task.ts b/x-pack/plugins/fleet/server/services/metrics/fleet_metrics_task.ts index 6b58decb4d109..0d1c0bd142169 100644 --- a/x-pack/plugins/fleet/server/services/metrics/fleet_metrics_task.ts +++ b/x-pack/plugins/fleet/server/services/metrics/fleet_metrics_task.ts @@ -186,7 +186,7 @@ export class FleetMetricsTask { this.wasStarted = true; try { - appContextService.getLogger().info(`Task ${this.taskId} scheduled with interval 1h`); + appContextService.getLogger().info(`Task ${this.taskId} scheduled with interval ${INTERVAL}`); await this.taskManager.ensureScheduled({ id: this.taskId, diff --git a/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts b/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts index f0a79a2633036..367d7f0ce0ff9 100644 --- a/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts +++ b/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts @@ -83,6 +83,7 @@ export class FleetUsageSender { const { agents_per_version: agentsPerVersion, agents_per_output_type: agentsPerOutputType, + agents_per_privileges: agentsPerPrivileges, upgrade_details: upgradeDetails, ...fleetUsageData } = usageData; @@ -92,6 +93,13 @@ export class FleetUsageSender { core.analytics.reportEvent(FLEET_USAGES_EVENT_TYPE, fleetUsageData); + appContextService + .getLogger() + .debug('Agents per privileges telemetry: ' + JSON.stringify(agentsPerPrivileges)); + core.analytics.reportEvent(FLEET_AGENTS_EVENT_TYPE, { + agents_per_privileges: agentsPerPrivileges, + }); + appContextService .getLogger() .debug('Agents per version telemetry: ' + JSON.stringify(agentsPerVersion)); diff --git a/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts b/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts index 5deaa97b02f3f..574c5dac1ad6a 100644 --- a/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts +++ b/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts @@ -135,6 +135,26 @@ export const fleetAgentsSchema: RootSchema = { }, }, }, + agents_per_privileges: { + _meta: { + description: 'Agents per privileges telemetry', + optional: true, + }, + properties: { + root: { + type: 'long', + _meta: { + description: 'Number of agents running with root privilege', + }, + }, + unprivileged: { + type: 'long', + _meta: { + description: 'Number of agents running without root privilege', + }, + }, + }, + }, upgrade_details: { _meta: { description: 'Agent upgrade details telemetry', From e24502d70ab98e738552ec46e69707d83152cf44 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Fri, 17 May 2024 11:00:46 -0700 Subject: [PATCH 67/71] [Security AI Assistant] Fixed assistant availability check in the overlay (#183585) This PR adding the check for the assistant license. The initial bug before fix: https://github.com/elastic/kibana/assets/55110838/1afcb112-714a-43e1-8f0d-58fb5b37fc4a after: no error dialog pop up --- .../use_assistant_overlay/index.test.tsx | 12 +++++++--- .../assistant/use_assistant_overlay/index.tsx | 8 +++++-- .../impl/new_chat/index.test.tsx | 9 ++++++++ .../impl/new_chat/index.tsx | 6 ++++- .../tabs/incompatible_tab/index.tsx | 23 +++++++++---------- .../summary_tab/callout_summary/index.tsx | 23 +++++++++---------- .../use_view_in_ai_assistant.ts | 12 +++------- .../rules_table/rules_table_toolbar.tsx | 3 ++- .../rule_status_failed_callout.tsx | 3 ++- .../right/hooks/use_assistant.ts | 5 ++-- .../public/overview/pages/data_quality.tsx | 4 ++-- .../side_panel/event_details/index.tsx | 5 ++-- 12 files changed, 66 insertions(+), 47 deletions(-) diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.test.tsx index c49db8d6246a3..9e21d9da57d74 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.test.tsx @@ -92,6 +92,7 @@ describe('useAssistantOverlay', () => { const id = 'test-id'; const suggestedUserPrompt = 'test user prompt'; const tooltip = 'test tooltip'; + const isAssistantAvailable = true; renderHook(() => useAssistantOverlay( @@ -101,7 +102,8 @@ describe('useAssistantOverlay', () => { getPromptContext, id, suggestedUserPrompt, - tooltip + tooltip, + isAssistantAvailable ) ); @@ -118,6 +120,7 @@ describe('useAssistantOverlay', () => { }); it('calls unRegisterPromptContext on unmount', () => { + const isAssistantAvailable = true; const { unmount } = renderHook(() => useAssistantOverlay( 'event', @@ -126,7 +129,8 @@ describe('useAssistantOverlay', () => { () => Promise.resolve('data'), 'id', null, - 'tooltip' + 'tooltip', + isAssistantAvailable ) ); @@ -136,6 +140,7 @@ describe('useAssistantOverlay', () => { }); it('calls `showAssistantOverlay` from the assistant context', () => { + const isAssistantAvailable = true; const { result } = renderHook(() => useAssistantOverlay( 'event', @@ -144,7 +149,8 @@ describe('useAssistantOverlay', () => { () => Promise.resolve('data'), 'id', null, - 'tooltip' + 'tooltip', + isAssistantAvailable ) ); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx index 8b8a399bc7671..3396223d192ca 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/use_assistant_overlay/index.tsx @@ -74,6 +74,9 @@ export const useAssistantOverlay = ( */ tooltip: PromptContext['tooltip'], + /** Required to identify the availability of the Assistant for the current license level */ + isAssistantEnabled: boolean, + /** * Optionally provide a map of replacements associated with the context, i.e. replacements for an attack discovery that's provided as context */ @@ -96,7 +99,7 @@ export const useAssistantOverlay = ( const { data: conversations, isLoading } = useFetchCurrentUserConversations({ http, onFetch: onFetchedConversations, - isAssistantEnabled: true, + isAssistantEnabled, }); // memoize the props so that we can use them in the effect below: @@ -135,7 +138,7 @@ export const useAssistantOverlay = ( : undefined; } - if (!conversation && defaultConnector && !isLoading) { + if (isAssistantEnabled && !conversation && defaultConnector && !isLoading) { try { conversation = await createConversation({ apiConfig: { @@ -166,6 +169,7 @@ export const useAssistantOverlay = ( conversations, createConversation, defaultConnector, + isAssistantEnabled, isLoading, promptContextId, ] diff --git a/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.test.tsx index 6850c84bf67b4..730c88413833d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.test.tsx @@ -23,6 +23,7 @@ const defaultProps: Props = { description: 'Test description', getPromptContext: () => Promise.resolve('Test prompt context'), tooltip: 'Test tooltip', + isAssistantEnabled: true, }; describe('NewChat', () => { @@ -38,6 +39,14 @@ describe('NewChat', () => { expect(newChatButton.querySelector('[data-euiicon-type="discuss"]')).toBeInTheDocument(); }); + it('renders the default New Chat button even if the Assistant is disabled', () => { + render(); + + const newChatButton = screen.getByTestId('newChat'); + + expect(newChatButton.querySelector('[data-euiicon-type="discuss"]')).toBeInTheDocument(); + }); + it('renders the default "New Chat" text when children are NOT provided', () => { render(); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.tsx index 3fd798f38c4b9..a4793dfd25a9d 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/new_chat/index.tsx @@ -23,6 +23,8 @@ export type Props = Omit & { promptContextId?: string; /** Optionally specify color of empty button */ color?: 'text' | 'accent' | 'primary' | 'success' | 'warning' | 'danger'; + /** Required to identify the availability of the Assistant for the current license level */ + isAssistantEnabled: boolean; }; const NewChatComponent: React.FC = ({ @@ -36,6 +38,7 @@ const NewChatComponent: React.FC = ({ promptContextId, suggestedUserPrompt, tooltip, + isAssistantEnabled, }) => { const { showAssistantOverlay } = useAssistantOverlay( category, @@ -44,7 +47,8 @@ const NewChatComponent: React.FC = ({ getPromptContext, promptContextId ?? null, suggestedUserPrompt, - tooltip + tooltip, + isAssistantEnabled ); const showOverlay = useCallback(() => { diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx index e1b1bdfc9a403..aa6866555a3b3 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/incompatible_tab/index.tsx @@ -145,18 +145,17 @@ const IncompatibleTabComponent: React.FC = ({ - {isAssistantEnabled && ( - - - - )} + + + diff --git a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx index d9403b7159e9f..aa32baafecf19 100644 --- a/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx +++ b/x-pack/packages/security-solution/ecs_data_quality_dashboard/impl/data_quality/data_quality_panel/tabs/summary_tab/callout_summary/index.tsx @@ -138,18 +138,17 @@ const CalloutSummaryComponent: React.FC = ({ - {isAssistantEnabled && ( - - - - )} + + + diff --git a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts index 228f882863777..58f66b1b2a0dd 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/view_in_ai_assistant/use_view_in_ai_assistant.ts @@ -12,8 +12,6 @@ import { useAssistantAvailability } from '../../../assistant/use_assistant_avail import { getAttackDiscoveryMarkdown } from '../../get_attack_discovery_markdown/get_attack_discovery_markdown'; import type { AttackDiscovery } from '../../types'; -const useAssistantNoop = () => ({ promptContextId: undefined, showAssistantOverlay: () => {} }); - /** * This category is provided in the prompt context for the assistant */ @@ -25,12 +23,7 @@ export const useViewInAiAssistant = ({ attackDiscovery: AttackDiscovery; replacements?: Replacements; }) => { - const { hasAssistantPrivilege } = useAssistantAvailability(); - - const useAssistantHook = useMemo( - () => (hasAssistantPrivilege ? useAssistantOverlay : useAssistantNoop), - [hasAssistantPrivilege] - ); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); // the prompt context for this insight: const getPromptContext = useCallback( @@ -41,7 +34,7 @@ export const useViewInAiAssistant = ({ }), [attackDiscovery] ); - const { promptContextId, showAssistantOverlay: showOverlay } = useAssistantHook( + const { promptContextId, showAssistantOverlay: showOverlay } = useAssistantOverlay( category, attackDiscovery.title, // conversation title attackDiscovery.title, // description used in context pill @@ -49,6 +42,7 @@ export const useViewInAiAssistant = ({ attackDiscovery.id, // accept the UUID default for this prompt context null, // suggestedUserPrompt null, // tooltip + isAssistantEnabled, replacements ?? null ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx index fb2c17ddb2fa1..e3618a1383598 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_toolbar.tsx @@ -78,7 +78,7 @@ export const RulesTableToolbar = React.memo(() => { ); // Assistant integration for using selected rules as prompt context - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); const { state: { rules, selectedRuleIds }, } = useRulesTableContext(); @@ -105,6 +105,7 @@ export const RulesTableToolbar = React.memo(() => { getPromptContext={getPromptContext} suggestedUserPrompt={i18nAssistant.EXPLAIN_THEN_SUMMARIZE_RULE_DETAILS} tooltip={i18nAssistant.RULE_MANAGEMENT_CONTEXT_TOOLTIP} + isAssistantEnabled={isAssistantEnabled} /> )} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_execution_status/rule_status_failed_callout.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_execution_status/rule_status_failed_callout.tsx index ba7f57aba5648..170cdaf265d4a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_execution_status/rule_status_failed_callout.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_execution_status/rule_status_failed_callout.tsx @@ -33,7 +33,7 @@ const RuleStatusFailedCallOutComponent: React.FC = message, status, }) => { - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); const { shouldBeDisplayed, color, title } = getPropsByStatus(status); const getPromptContext = useCallback( async () => @@ -84,6 +84,7 @@ const RuleStatusFailedCallOutComponent: React.FC = getPromptContext={getPromptContext} suggestedUserPrompt={i18n.ASK_ASSISTANT_USER_PROMPT} tooltip={i18n.ASK_ASSISTANT_TOOLTIP} + isAssistantEnabled={isAssistantEnabled} > {i18n.ASK_ASSISTANT_ERROR_BUTTON} diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts index 185591ff43a2d..2196674964cfe 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/hooks/use_assistant.ts @@ -56,7 +56,7 @@ export const useAssistant = ({ dataFormattedForFieldBrowser, isAlert, }: UseAssistantParams): UseAssistantResult => { - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); const useAssistantHook = hasAssistantPrivilege ? useAssistantOverlay : useAssistantNoop; const getPromptContext = useCallback( async () => getRawData(dataFormattedForFieldBrowser ?? []), @@ -73,7 +73,8 @@ export const useAssistant = ({ isAlert ? PROMPT_CONTEXTS[PROMPT_CONTEXT_ALERT_CATEGORY].suggestedUserPrompt : PROMPT_CONTEXTS[PROMPT_CONTEXT_EVENT_CATEGORY].suggestedUserPrompt, - isAlert ? ALERT_SUMMARY_VIEW_CONTEXT_TOOLTIP : EVENT_SUMMARY_VIEW_CONTEXT_TOOLTIP + isAlert ? ALERT_SUMMARY_VIEW_CONTEXT_TOOLTIP : EVENT_SUMMARY_VIEW_CONTEXT_TOOLTIP, + isAssistantEnabled ); return { diff --git a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx index 5c74c0e334180..587cf9377c5f0 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/data_quality.tsx @@ -131,7 +131,7 @@ const renderOption = ( ); const DataQualityComponent: React.FC = () => { - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { isAssistantEnabled } = useAssistantAvailability(); const httpFetch = KibanaServices.get().http.fetch; const { baseTheme, theme } = useThemes(); const toasts = useToasts(); @@ -284,7 +284,7 @@ const DataQualityComponent: React.FC = () => { reportDataQualityIndexChecked={reportDataQualityIndexChecked} httpFetch={httpFetch} ilmPhases={ilmPhases} - isAssistantEnabled={hasAssistantPrivilege} + isAssistantEnabled={isAssistantEnabled} isILMAvailable={isILMAvailable} lastChecked={lastChecked} openCreateCaseFlyout={openCreateCaseFlyout} diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 20b6341db6d91..33faddd5bea48 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -80,7 +80,7 @@ const EventDetailsPanelComponent: React.FC = ({ scopeId, isReadOnly, }) => { - const { hasAssistantPrivilege } = useAssistantAvailability(); + const { hasAssistantPrivilege, isAssistantEnabled } = useAssistantAvailability(); // TODO: changing feature flags requires a hard refresh to take effect, but this temporary workaround technically violates the rules of hooks: const useAssistant = hasAssistantPrivilege ? useAssistantOverlay : useAssistantNoop; const currentSpaceId = useSpaceId(); @@ -122,7 +122,8 @@ const EventDetailsPanelComponent: React.FC = ({ isAlert ? PROMPT_CONTEXTS[PROMPT_CONTEXT_ALERT_CATEGORY].suggestedUserPrompt : PROMPT_CONTEXTS[PROMPT_CONTEXT_EVENT_CATEGORY].suggestedUserPrompt, - isAlert ? ALERT_SUMMARY_VIEW_CONTEXT_TOOLTIP : EVENT_SUMMARY_VIEW_CONTEXT_TOOLTIP + isAlert ? ALERT_SUMMARY_VIEW_CONTEXT_TOOLTIP : EVENT_SUMMARY_VIEW_CONTEXT_TOOLTIP, + isAssistantEnabled ); const header = useMemo( From 7ae07f89137cf839869a2233de5f9f26900f9e99 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Fri, 17 May 2024 12:03:42 -0600 Subject: [PATCH 68/71] [EEM][POC] The POC for creating entity-centric indices using entity definitions (#183205) ## Summary This is a "proof of concept" for generating entity-centric indices for the OAM. This exposes an API (`/api/entities`) for creating "asset definitions" (`EntityDefinition`) that manages a transform and ingest pipeline to produce documents into an index which could be used to create a search experience or lookups for different services. ### Features - Data schema agnostic, works with known schemas OR custom logs - Supports defining multiple `identityFields` along with an `identityTemplate` for formatting the `asset.id` - Supports optional `identityFields` using `{ "field": "path-to-field", "optional": true }` definition instead of a `string`. - Supports defining key `metrics` with equations which are compatible with the SLO product - Supports adding `metadata` fields which will include multiple values. - Supports `metadata` fields can be re-mapped to a new destination path using `{ "source": "path-to-source-field", "limit": 1000, "destination": "path-to-destination-in-output" }` definition instead of a `string` - Supports adding `staticFields` which can also use template variables - Support fine grain control over the frequency and sync settings for the underlying transform - Installs the index template components and index template settings for the destination index - Allow the user to configure the index patterns and timestamp field along with the lookback - The documents for each definition will be stored in their own index (`.entities-observability.summary-v1.{defintion.id}`) ### Notes - We are currently considering adding a historical index which will track changes to the assets over time. If we choose to do this, the summary index would remain the same but we'd add a second transform with a group_by on the `definition.timestampField` and break the indices into monthly indexes (configurable in the settings). - We are looking into ways to add `firstSeenTimestamp`, this is a difficult due to scaling issue. Essentially, we would need to find the `minimum` timestamp for each entity which could be extremely costly on a large datasets. - There is nothing stopping you from creating an asset definition that uses the `.entities-observability.summary-v1.*` index pattern to create summaries of summaries... it can be very "meta". ### API - `POST /api/entities/definition` - Creates a new asset definition and starts the indexing. See examples below. - `DELETE /api/entities/definition/{id}` - Deletes the asset definition along with cleaning up the transform, ingest pipeline, and deletes the destination index. - `POST /api/entities/definition/{id}/_reset` - Resets the transform, ingest pipeline, and destination index. This is useful for upgrading asset definitions to new features. ## Example Definitions and Output Here is a definition for creating services for each of the custom log sources in the `fake_stack` dataset from `x-pack/packages/data-forge`. ```JSON POST kbn:/api/entities/definition { "id": "admin-console-logs-service", "name": "Services for Admin Console", "type": "service", "indexPatterns": ["kbn-data-forge-fake_stack.*"], "timestampField": "@timestamp", "lookback": "5m", "identityFields": ["log.logger"], "identityTemplate": "{{log.logger}}", "metadata": [ "tags", "host.name" ], "metrics": [ { "name": "logRate", "equation": "A / 5", "metrics": [ { "name": "A", "aggregation": "doc_count", "filter": "log.level: *" } ] }, { "name": "errorRate", "equation": "A / 5", "metrics": [ { "name": "A", "aggregation": "doc_count", "filter": "log.level: \"ERROR\"" } ] } ] } ``` Which produces: ```JSON { "host": { "name": [ "admin-console.prod.020", "admin-console.prod.010", "admin-console.prod.011", "admin-console.prod.001", "admin-console.prod.012", "admin-console.prod.002", "admin-console.prod.013", "admin-console.prod.003", "admin-console.prod.014", "admin-console.prod.004", "admin-console.prod.015", "admin-console.prod.016", "admin-console.prod.005", "admin-console.prod.017", "admin-console.prod.006", "admin-console.prod.018", "admin-console.prod.007", "admin-console.prod.019", "admin-console.prod.008", "admin-console.prod.009" ] }, "entity": { "latestTimestamp": "2024-05-10T22:04:51.481Z", "metric": { "logRate": 37.4, "errorRate": 1 }, "identity": { "log": { "logger": "admin-console" } }, "id": "admin-console", "indexPatterns": [ "kbn-data-forge-fake_stack.*" ], "definitionId": "admin-console-logs-service" }, "event": { "ingested": "2024-05-10T22:05:51.955691Z" }, "tags": [ "infra:admin-console" ] } ``` Here is an example of a definition for APM Services: ```JSON POST kbn:/api/entities/definition { "id": "apm-services", "name": "Services for APM", "type": "service", "indexPatterns": ["logs-*", "metrics-*"], "timestampField": "@timestamp", "lookback": "5m", "identityFields": ["service.name", "service.environment"], "identityTemplate": "{{service.name}}:{{service.environment}}", "metadata": [ "tags", "host.name" ], "metrics": [ { "name": "latency", "equation": "A", "metrics": [ { "name": "A", "aggregation": "avg", "field": "transaction.duration.histogram" } ] }, { "name": "throughput", "equation": "A / 5", "metrics": [ { "name": "A", "aggregation": "doc_count" } ] }, { "name": "failedTransRate", "equation": "A / B", "metrics": [ { "name": "A", "aggregation": "doc_count", "filter": "event.outcome: \"failure\"" }, { "name": "B", "aggregation": "doc_count", "filter": "event.outcome: *" } ] } ] } ``` Which produces: ```JSON { "host": { "name": [ "simianhacker's-macbook-pro" ] }, "entity": { "latestTimestamp": "2024-05-10T21:38:22.513Z", "metric": { "latency": 615276.8812785388, "throughput": 50.6, "failedTransRate": 0.0091324200913242 }, "identity": { "service": { "environment": "development", "name": "admin-console" } }, "id": "admin-console:development", "indexPatterns": [ "logs-*", "metrics-*" ], "definitionId": "apm-services" }, "event": { "ingested": "2024-05-10T21:39:33.636225Z" }, "tags": [ "_geoip_database_unavailable_GeoLite2-City.mmdb" ] } ``` ### Getting Started The easiest way to get started is to use the`kbn-data-forge` config below. Save this YAML to `~/Desktop/fake_stack.yaml` then run `node x-pack/scripts/data_forge.js --config ~/Desktop/fake_stack.yaml`. Then create a definition using the first example above. ```YAML --- elasticsearch: installKibanaUser: false kibana: installAssets: true host: "http://localhost:5601/kibana" indexing: dataset: "fake_stack" eventsPerCycle: 50 reduceWeekendTrafficBy: 0.5 schedule: # Start with good events - template: "good" start: "now-1d" end: "now-20m" eventsPerCycle: 50 randomness: 0.8 - template: "bad" start: "now-20m" end: "now-10m" eventsPerCycle: 50 randomness: 0.8 - template: "good" start: "now-10m" end: false eventsPerCycle: 50 randomness: 0.8 ``` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 1 + package.json | 1 + tsconfig.base.json | 2 + x-pack/packages/kbn-entities-schema/README.md | 3 + x-pack/packages/kbn-entities-schema/index.ts | 10 ++ .../kbn-entities-schema/jest.config.js | 12 ++ .../packages/kbn-entities-schema/kibana.jsonc | 5 + .../packages/kbn-entities-schema/package.json | 6 + .../kbn-entities-schema/src/schema/common.ts | 106 +++++++++++++++ .../kbn-entities-schema/src/schema/entity.ts | 21 +++ .../src/schema/entity_definition.ts | 42 ++++++ .../kbn-entities-schema/tsconfig.json | 18 +++ .../common/constants_entities.ts | 13 ++ .../create_and_install_ingest_pipeline.ts | 39 ++++++ .../entities/create_and_install_transform.ts | 30 +++++ .../lib/entities/delete_entity_definition.ts | 31 +++++ .../server/lib/entities/delete_index.ts | 24 ++++ .../lib/entities/delete_ingest_pipeline.ts | 27 ++++ .../errors/entity_id_conflict_error.ts | 18 +++ .../lib/entities/errors/entity_not_found.ts | 13 ++ .../errors/entity_security_exception.ts | 18 +++ .../errors/invalid_transform_error.ts | 13 ++ .../helpers/fixtures/entity_definition.ts | 46 +++++++ .../entities/helpers/generate_index_name.ts | 13 ++ .../get_elasticsearch_query_or_throw.ts | 17 +++ .../server/lib/entities/helpers/retry.ts | 53 ++++++++ .../generate_processors.test.ts.snap | 70 ++++++++++ .../generate_ingest_pipeline_id.ts | 13 ++ .../generate_processors.test.ts | 16 +++ .../ingest_pipeline/generate_processors.ts | 98 ++++++++++++++ .../lib/entities/read_entity_definition.ts | 36 +++++ .../lib/entities/save_entity_definition.ts | 37 +++++ .../server/lib/entities/start_transform.ts | 28 ++++ .../lib/entities/stop_and_delete_transform.ts | 37 +++++ .../generate_transform.test.ts.snap | 126 ++++++++++++++++++ .../generate_metadata_aggregations.ts | 26 ++++ .../transform/generate_metric_aggregations.ts | 118 ++++++++++++++++ .../transform/generate_transform.test.ts | 16 +++ .../entities/transform/generate_transform.ts | 86 ++++++++++++ .../transform/generate_transform_id.ts | 13 ++ .../server/lib/manage_index_templates.ts | 33 +++-- .../asset_manager/server/plugin.ts | 37 ++++- .../server/routes/entities/create.ts | 78 +++++++++++ .../server/routes/entities/delete.ts | 55 ++++++++ .../server/routes/entities/reset.ts | 65 +++++++++ .../asset_manager/server/routes/index.ts | 21 ++- .../asset_manager/server/routes/types.ts | 2 + .../server/saved_objects/entity_definition.ts | 40 ++++++ .../server/saved_objects/index.ts | 8 ++ .../server/templates/assets_template.ts | 2 + .../server/templates/components/base.ts | 32 +++++ .../server/templates/components/entity.ts | 40 ++++++ .../server/templates/components/event.ts | 29 ++++ .../server/templates/entities_template.ts | 60 +++++++++ .../asset_manager/tsconfig.json | 5 +- yarn.lock | 4 + 56 files changed, 1790 insertions(+), 23 deletions(-) create mode 100644 x-pack/packages/kbn-entities-schema/README.md create mode 100644 x-pack/packages/kbn-entities-schema/index.ts create mode 100644 x-pack/packages/kbn-entities-schema/jest.config.js create mode 100644 x-pack/packages/kbn-entities-schema/kibana.jsonc create mode 100644 x-pack/packages/kbn-entities-schema/package.json create mode 100644 x-pack/packages/kbn-entities-schema/src/schema/common.ts create mode 100644 x-pack/packages/kbn-entities-schema/src/schema/entity.ts create mode 100644 x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts create mode 100644 x-pack/packages/kbn-entities-schema/tsconfig.json create mode 100644 x-pack/plugins/observability_solution/asset_manager/common/constants_entities.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_ingest_pipeline.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_transform.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_entity_definition.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_index.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_ingest_pipeline.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_id_conflict_error.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_not_found.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_security_exception.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/invalid_transform_error.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/fixtures/entity_definition.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/generate_index_name.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/get_elasticsearch_query_or_throw.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/retry.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_processors.test.ts.snap create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_ingest_pipeline_id.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.test.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/read_entity_definition.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/save_entity_definition.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/start_transform.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/stop_and_delete_transform.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/__snapshots__/generate_transform.test.ts.snap create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metadata_aggregations.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metric_aggregations.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.test.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform_id.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/routes/entities/create.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/routes/entities/delete.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/routes/entities/reset.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/saved_objects/entity_definition.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/saved_objects/index.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/templates/components/base.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/templates/components/entity.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/templates/components/event.ts create mode 100644 x-pack/plugins/observability_solution/asset_manager/server/templates/entities_template.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cddb6388566ee..d160dda238f8d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -390,6 +390,7 @@ src/plugins/embeddable @elastic/kibana-presentation x-pack/examples/embedded_lens_example @elastic/kibana-visualizations x-pack/plugins/encrypted_saved_objects @elastic/kibana-security x-pack/plugins/enterprise_search @elastic/enterprise-search-frontend +x-pack/packages/kbn-entities-schema @elastic/obs-knowledge-team examples/error_boundary @elastic/appex-sharedux packages/kbn-es @elastic/kibana-operations packages/kbn-es-archiver @elastic/kibana-operations @elastic/appex-qa diff --git a/package.json b/package.json index 78dba79f5e6ba..04316640a2952 100644 --- a/package.json +++ b/package.json @@ -439,6 +439,7 @@ "@kbn/embedded-lens-example-plugin": "link:x-pack/examples/embedded_lens_example", "@kbn/encrypted-saved-objects-plugin": "link:x-pack/plugins/encrypted_saved_objects", "@kbn/enterprise-search-plugin": "link:x-pack/plugins/enterprise_search", + "@kbn/entities-schema": "link:x-pack/packages/kbn-entities-schema", "@kbn/error-boundary-example-plugin": "link:examples/error_boundary", "@kbn/es-errors": "link:packages/kbn-es-errors", "@kbn/es-query": "link:packages/kbn-es-query", diff --git a/tsconfig.base.json b/tsconfig.base.json index f87d111ac27af..dd82d55b1b0ad 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -774,6 +774,8 @@ "@kbn/encrypted-saved-objects-plugin/*": ["x-pack/plugins/encrypted_saved_objects/*"], "@kbn/enterprise-search-plugin": ["x-pack/plugins/enterprise_search"], "@kbn/enterprise-search-plugin/*": ["x-pack/plugins/enterprise_search/*"], + "@kbn/entities-schema": ["x-pack/packages/kbn-entities-schema"], + "@kbn/entities-schema/*": ["x-pack/packages/kbn-entities-schema/*"], "@kbn/error-boundary-example-plugin": ["examples/error_boundary"], "@kbn/error-boundary-example-plugin/*": ["examples/error_boundary/*"], "@kbn/es": ["packages/kbn-es"], diff --git a/x-pack/packages/kbn-entities-schema/README.md b/x-pack/packages/kbn-entities-schema/README.md new file mode 100644 index 0000000000000..2601be6543c58 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/README.md @@ -0,0 +1,3 @@ +# @kbn/entities-schema + +The entities schema for the asset model for Observability \ No newline at end of file diff --git a/x-pack/packages/kbn-entities-schema/index.ts b/x-pack/packages/kbn-entities-schema/index.ts new file mode 100644 index 0000000000000..92b93b7938125 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './src/schema/entity_definition'; +export * from './src/schema/entity'; +export * from './src/schema/common'; diff --git a/x-pack/packages/kbn-entities-schema/jest.config.js b/x-pack/packages/kbn-entities-schema/jest.config.js new file mode 100644 index 0000000000000..1d10119431ec3 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/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/kbn-entities-schema'], +}; diff --git a/x-pack/packages/kbn-entities-schema/kibana.jsonc b/x-pack/packages/kbn-entities-schema/kibana.jsonc new file mode 100644 index 0000000000000..9895c2074a584 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/entities-schema", + "owner": "@elastic/obs-knowledge-team" +} diff --git a/x-pack/packages/kbn-entities-schema/package.json b/x-pack/packages/kbn-entities-schema/package.json new file mode 100644 index 0000000000000..0be44d9d75055 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/entities-schema", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/packages/kbn-entities-schema/src/schema/common.ts b/x-pack/packages/kbn-entities-schema/src/schema/common.ts new file mode 100644 index 0000000000000..6b4f0cc794c2b --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/src/schema/common.ts @@ -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 { z } from 'zod'; +import moment from 'moment'; + +export enum EntityType { + service = 'service', + host = 'host', + pod = 'pod', + node = 'node', +} + +export const arrayOfStringsSchema = z.array(z.string()); + +export const entityTypeSchema = z.nativeEnum(EntityType); + +export enum BasicAggregations { + avg = 'avg', + max = 'max', + min = 'min', + sum = 'sum', + cardinality = 'cardinality', + last_value = 'last_value', + std_deviation = 'std_deviation', +} + +export const basicAggregationsSchema = z.nativeEnum(BasicAggregations); + +const metricNameSchema = z + .string() + .length(1) + .regex(/[a-zA-Z]/) + .toUpperCase(); + +export const filterSchema = z.optional(z.string()); + +export const basicMetricWithFieldSchema = z.object({ + name: metricNameSchema, + aggregation: basicAggregationsSchema, + field: z.string(), + filter: filterSchema, +}); + +export const docCountMetricSchema = z.object({ + name: metricNameSchema, + aggregation: z.literal('doc_count'), + filter: filterSchema, +}); + +export const durationSchema = z + .string() + .regex(/\d+[m|d|s|h]/) + .transform((val: string) => { + const parts = val.match(/(\d+)([m|s|h|d])/); + if (parts === null) { + throw new Error('Unable to parse duration'); + } + const value = parseInt(parts[1], 10); + const unit = parts[2] as 'm' | 's' | 'h' | 'd'; + const duration = moment.duration(value, unit); + return { ...duration, toJSON: () => val }; + }); + +export const percentileMetricSchema = z.object({ + name: metricNameSchema, + aggregation: z.literal('percentile'), + field: z.string(), + percentile: z.number(), + filter: filterSchema, +}); + +export const metricSchema = z.discriminatedUnion('aggregation', [ + basicMetricWithFieldSchema, + docCountMetricSchema, + percentileMetricSchema, +]); + +export type Metric = z.infer; + +export const keyMetricSchema = z.object({ + name: z.string(), + metrics: z.array(metricSchema), + equation: z.string(), +}); + +export type KeyMetric = z.infer; + +export const metadataSchema = z + .object({ + source: z.string(), + destination: z.optional(z.string()), + limit: z.optional(z.number().default(1000)), + }) + .or(z.string().transform((value) => ({ source: value, destination: value, limit: 1000 }))); + +export const identityFieldsSchema = z + .object({ + field: z.string(), + optional: z.boolean(), + }) + .or(z.string().transform((value) => ({ field: value, optional: false }))); diff --git a/x-pack/packages/kbn-entities-schema/src/schema/entity.ts b/x-pack/packages/kbn-entities-schema/src/schema/entity.ts new file mode 100644 index 0000000000000..514ed01494036 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/src/schema/entity.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 { z } from 'zod'; +import { arrayOfStringsSchema } from './common'; + +export const entitySchema = z.intersection( + z.object({ + entity: z.object({ + id: z.string(), + indexPatterns: arrayOfStringsSchema, + identityFields: arrayOfStringsSchema, + metric: z.record(z.string(), z.number()), + }), + }), + z.record(z.string(), z.string().or(z.number())) +); diff --git a/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts b/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts new file mode 100644 index 0000000000000..48b8c8060efbc --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/src/schema/entity_definition.ts @@ -0,0 +1,42 @@ +/* + * 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 { z } from 'zod'; +import { + arrayOfStringsSchema, + entityTypeSchema, + keyMetricSchema, + metadataSchema, + filterSchema, + durationSchema, + identityFieldsSchema, +} from './common'; + +export const entityDefinitionSchema = z.object({ + id: z.string().regex(/^[\w-]+$/), + name: z.string(), + description: z.optional(z.string()), + type: entityTypeSchema, + filter: filterSchema, + indexPatterns: arrayOfStringsSchema, + identityFields: z.array(identityFieldsSchema), + identityTemplate: z.string(), + metadata: z.optional(z.array(metadataSchema)), + metrics: z.optional(z.array(keyMetricSchema)), + staticFields: z.optional(z.record(z.string(), z.string())), + lookback: durationSchema, + timestampField: z.string(), + settings: z.optional( + z.object({ + syncField: z.optional(z.string()), + syncDelay: z.optional(z.string()), + frequency: z.optional(z.string()), + }) + ), +}); + +export type EntityDefinition = z.infer; diff --git a/x-pack/packages/kbn-entities-schema/tsconfig.json b/x-pack/packages/kbn-entities-schema/tsconfig.json new file mode 100644 index 0000000000000..f722f3587e7a2 --- /dev/null +++ b/x-pack/packages/kbn-entities-schema/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + ] +} diff --git a/x-pack/plugins/observability_solution/asset_manager/common/constants_entities.ts b/x-pack/plugins/observability_solution/asset_manager/common/constants_entities.ts new file mode 100644 index 0000000000000..aeeea398220a0 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/common/constants_entities.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const ENTITY_VERSION = 'v1'; +export const ENTITY_BASE_PREFIX = `.entities-observability.summary-${ENTITY_VERSION}`; +export const ENTITY_TRANSFORM_PREFIX = `entities-observability-summary-${ENTITY_VERSION}`; +export const ENTITY_DEFAULT_FREQUENCY = '1m'; +export const ENTITY_DEFAULT_SYNC_DELAY = '60s'; +export const ENTITY_API_PREFIX = '/api/entities'; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_ingest_pipeline.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_ingest_pipeline.ts new file mode 100644 index 0000000000000..e4d7116b95562 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_ingest_pipeline.ts @@ -0,0 +1,39 @@ +/* + * 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 { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateProcessors } from './ingest_pipeline/generate_processors'; +import { retryTransientEsErrors } from './helpers/retry'; +import { EntitySecurityException } from './errors/entity_security_exception'; +import { generateIngestPipelineId } from './ingest_pipeline/generate_ingest_pipeline_id'; + +export async function createAndInstallIngestPipeline( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const processors = generateProcessors(definition); + const id = generateIngestPipelineId(definition); + try { + await retryTransientEsErrors( + () => + esClient.ingest.putPipeline({ + id, + processors, + }), + { logger } + ); + } catch (e) { + logger.error(`Cannot create entity ingest pipeline for [${definition.id}] entity defintion`); + if (e.meta?.body?.error?.type === 'security_exception') { + throw new EntitySecurityException(e.meta.body.error.reason, definition); + } + throw e; + } + return id; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_transform.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_transform.ts new file mode 100644 index 0000000000000..f8cd02250d898 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/create_and_install_transform.ts @@ -0,0 +1,30 @@ +/* + * 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 { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateTransform } from './transform/generate_transform'; +import { retryTransientEsErrors } from './helpers/retry'; +import { EntitySecurityException } from './errors/entity_security_exception'; + +export async function createAndInstallTransform( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const transform = generateTransform(definition); + try { + await retryTransientEsErrors(() => esClient.transform.putTransform(transform), { logger }); + } catch (e) { + logger.error(`Cannot create entity transform for [${definition.id}] entity definition`); + if (e.meta?.body?.error?.type === 'security_exception') { + throw new EntitySecurityException(e.meta.body.error.reason, definition); + } + throw e; + } + return transform.transform_id; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_entity_definition.ts new file mode 100644 index 0000000000000..1067f33abaca3 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_entity_definition.ts @@ -0,0 +1,31 @@ +/* + * 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 { Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { SO_ENTITY_DEFINITION_TYPE } from '../../saved_objects'; +import { EntityDefinitionNotFound } from './errors/entity_not_found'; + +export async function deleteEntityDefinition( + soClient: SavedObjectsClientContract, + definition: EntityDefinition, + logger: Logger +) { + const response = await soClient.find({ + type: SO_ENTITY_DEFINITION_TYPE, + page: 1, + perPage: 1, + filter: `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${definition.id})`, + }); + + if (response.total === 0) { + logger.error(`Unable to delete entity definition [${definition.id}] because it doesn't exist.`); + throw new EntityDefinitionNotFound(`Entity defintion with [${definition.id}] not found.`); + } + + await soClient.delete(SO_ENTITY_DEFINITION_TYPE, response.saved_objects[0].id); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_index.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_index.ts new file mode 100644 index 0000000000000..7df6867115bbe --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_index.ts @@ -0,0 +1,24 @@ +/* + * 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 { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateIndexName } from './helpers/generate_index_name'; + +export async function deleteIndex( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const indexName = generateIndexName(definition); + try { + await esClient.indices.delete({ index: indexName, ignore_unavailable: true }); + } catch (e) { + logger.error(`Unable to remove entity defintion index [${definition.id}}]`); + throw e; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_ingest_pipeline.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_ingest_pipeline.ts new file mode 100644 index 0000000000000..1e42282369ef3 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/delete_ingest_pipeline.ts @@ -0,0 +1,27 @@ +/* + * 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 { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateIngestPipelineId } from './ingest_pipeline/generate_ingest_pipeline_id'; +import { retryTransientEsErrors } from './helpers/retry'; + +export async function deleteIngestPipeline( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const pipelineId = generateIngestPipelineId(definition); + try { + await retryTransientEsErrors(() => + esClient.ingest.deletePipeline({ id: pipelineId }, { ignore: [404] }) + ); + } catch (e) { + logger.error(`Unable to delete ingest pipeline [${pipelineId}]`); + throw e; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_id_conflict_error.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_id_conflict_error.ts new file mode 100644 index 0000000000000..5108ca31ed548 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_id_conflict_error.ts @@ -0,0 +1,18 @@ +/* + * 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 { EntityDefinition } from '@kbn/entities-schema'; + +export class EntityIdConflict extends Error { + public defintion: EntityDefinition; + + constructor(message: string, def: EntityDefinition) { + super(message); + this.name = 'EntityIdConflict'; + this.defintion = def; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_not_found.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_not_found.ts new file mode 100644 index 0000000000000..d81cd3322cae6 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_not_found.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export class EntityDefinitionNotFound extends Error { + constructor(message: string) { + super(message); + this.name = 'EntityDefinitionNotFound'; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_security_exception.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_security_exception.ts new file mode 100644 index 0000000000000..6e2f721d4de14 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/entity_security_exception.ts @@ -0,0 +1,18 @@ +/* + * 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 { EntityDefinition } from '@kbn/entities-schema'; + +export class EntitySecurityException extends Error { + public defintion: EntityDefinition; + + constructor(message: string, def: EntityDefinition) { + super(message); + this.name = 'EntitySecurityException'; + this.defintion = def; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/invalid_transform_error.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/invalid_transform_error.ts new file mode 100644 index 0000000000000..5d1c98d5dc3ae --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/errors/invalid_transform_error.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export class InvalidTransformError extends Error { + constructor(message: string) { + super(message); + this.name = 'InvalidTransformError'; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/fixtures/entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/fixtures/entity_definition.ts new file mode 100644 index 0000000000000..fdb808466ba83 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/fixtures/entity_definition.ts @@ -0,0 +1,46 @@ +/* + * 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 { entityDefinitionSchema } from '@kbn/entities-schema'; +export const entityDefinition = entityDefinitionSchema.parse({ + id: 'admin-console-logs-service', + name: 'Services for Admin Console', + type: 'service', + indexPatterns: ['kbn-data-forge-fake_stack.*'], + timestampField: '@timestamp', + identityFields: ['log.logger'], + identityTemplate: 'service:{{log.logger}}', + metadata: ['tags', 'host.name', 'kubernetes.pod.name'], + staticFields: { + projectId: '1234', + }, + lookback: '5m', + metrics: [ + { + name: 'logRate', + equation: 'A / 5', + metrics: [ + { + name: 'A', + aggregation: 'doc_count', + filter: 'log.level: *', + }, + ], + }, + { + name: 'errorRate', + equation: 'A / 5', + metrics: [ + { + name: 'A', + aggregation: 'doc_count', + filter: 'log.level: error', + }, + ], + }, + ], +}); diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/generate_index_name.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/generate_index_name.ts new file mode 100644 index 0000000000000..365104f3571eb --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/generate_index_name.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_BASE_PREFIX } from '../../../../common/constants_entities'; + +export function generateIndexName(definition: EntityDefinition) { + return `${ENTITY_BASE_PREFIX}.${definition.id}`; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/get_elasticsearch_query_or_throw.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/get_elasticsearch_query_or_throw.ts new file mode 100644 index 0000000000000..3b24344efd83b --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/get_elasticsearch_query_or_throw.ts @@ -0,0 +1,17 @@ +/* + * 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 { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { InvalidTransformError } from '../errors/invalid_transform_error'; + +export function getElasticsearchQueryOrThrow(kuery: string) { + try { + return toElasticsearchQuery(fromKueryExpression(kuery)); + } catch (err) { + throw new InvalidTransformError(`Invalid KQL: ${kuery}`); + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/retry.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/retry.ts new file mode 100644 index 0000000000000..421289d1c0479 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/helpers/retry.ts @@ -0,0 +1,53 @@ +/* + * 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 { setTimeout } from 'timers/promises'; +import { errors as EsErrors } from '@elastic/elasticsearch'; +import type { Logger } from '@kbn/logging'; + +const MAX_ATTEMPTS = 5; + +const retryResponseStatuses = [ + 503, // ServiceUnavailable + 408, // RequestTimeout + 410, // Gone +]; + +const isRetryableError = (e: any) => + e instanceof EsErrors.NoLivingConnectionsError || + e instanceof EsErrors.ConnectionError || + e instanceof EsErrors.TimeoutError || + (e instanceof EsErrors.ResponseError && retryResponseStatuses.includes(e?.statusCode!)); + +/** + * Retries any transient network or configuration issues encountered from Elasticsearch with an exponential backoff. + * Should only be used to wrap operations that are idempotent and can be safely executed more than once. + */ +export const retryTransientEsErrors = async ( + esCall: () => Promise, + { logger, attempt = 0 }: { logger?: Logger; attempt?: number } = {} +): Promise => { + try { + return await esCall(); + } catch (e) { + if (attempt < MAX_ATTEMPTS && isRetryableError(e)) { + const retryCount = attempt + 1; + const retryDelaySec = Math.min(Math.pow(2, retryCount), 64); // 2s, 4s, 8s, 16s, 32s, 64s, 64s, 64s ... + + logger?.warn( + `Retrying Elasticsearch operation after [${retryDelaySec}s] due to error: ${e.toString()} ${ + e.stack + }` + ); + + await setTimeout(retryDelaySec * 1000); + return retryTransientEsErrors(esCall, { logger, attempt: retryCount }); + } + + throw e; + } +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_processors.test.ts.snap b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_processors.test.ts.snap new file mode 100644 index 0000000000000..443063d70db60 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/__snapshots__/generate_processors.test.ts.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generateProcessors(definition) should genearte a valid pipeline 1`] = ` +Array [ + Object { + "set": Object { + "field": "event.ingested", + "value": "{{{_ingest.timestamp}}}", + }, + }, + Object { + "set": Object { + "field": "entity.definitionId", + "value": "admin-console-logs-service", + }, + }, + Object { + "set": Object { + "field": "entity.indexPatterns", + "value": "[\\"kbn-data-forge-fake_stack.*\\"]", + }, + }, + Object { + "json": Object { + "field": "entity.indexPatterns", + }, + }, + Object { + "set": Object { + "field": "entity.id", + "value": "service:{{entity.identity.log.logger}}", + }, + }, + Object { + "set": Object { + "field": "projectId", + "value": "1234", + }, + }, + Object { + "script": Object { + "source": "if (ctx.entity?.metadata?.tags != null) { + ctx[\\"tags\\"] = ctx.entity.metadata.tags.keySet(); +} +if (ctx.entity?.metadata?.host?.name != null) { + ctx[\\"host\\"] = new HashMap(); + ctx[\\"host\\"][\\"name\\"] = ctx.entity.metadata.host.name.keySet(); +} +if (ctx.entity?.metadata?.kubernetes?.pod?.name != null) { + ctx[\\"kubernetes\\"] = new HashMap(); + ctx[\\"kubernetes\\"][\\"pod\\"] = new HashMap(); + ctx[\\"kubernetes\\"][\\"pod\\"][\\"name\\"] = ctx.entity.metadata.kubernetes.pod.name.keySet(); +} +", + }, + }, + Object { + "remove": Object { + "field": "entity.metadata", + "ignore_missing": true, + }, + }, + Object { + "set": Object { + "field": "_index", + "value": ".entities-observability.summary-v1.admin-console-logs-service", + }, + }, +] +`; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_ingest_pipeline_id.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_ingest_pipeline_id.ts new file mode 100644 index 0000000000000..c772e198e64fd --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_ingest_pipeline_id.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_BASE_PREFIX } from '../../../../common/constants_entities'; + +export function generateIngestPipelineId(definition: EntityDefinition) { + return `${ENTITY_BASE_PREFIX}.${definition.id}`; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.test.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.test.ts new file mode 100644 index 0000000000000..33919ec678dcf --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.test.ts @@ -0,0 +1,16 @@ +/* + * 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 { generateProcessors } from './generate_processors'; +import { entityDefinition } from '../helpers/fixtures/entity_definition'; + +describe('generateProcessors(definition)', () => { + it('should genearte a valid pipeline', () => { + const processors = generateProcessors(entityDefinition); + expect(processors).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.ts new file mode 100644 index 0000000000000..33f27cc5daf71 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/ingest_pipeline/generate_processors.ts @@ -0,0 +1,98 @@ +/* + * 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 { EntityDefinition } from '@kbn/entities-schema'; +import { generateIndexName } from '../helpers/generate_index_name'; + +function createIdTemplate(definition: EntityDefinition) { + return definition.identityFields.reduce((template, id) => { + return template.replaceAll(id.field, `entity.identity.${id.field}`); + }, definition.identityTemplate); +} + +function mapDesitnationToPainless(destination: string, source: string) { + const fieldParts = destination.split('.'); + return fieldParts.reduce((acc, _part, currentIndex, parts) => { + if (currentIndex + 1 === parts.length) { + return `${acc}\n ctx${parts + .map((s) => `["${s}"]`) + .join('')} = ctx.entity.metadata.${source}.keySet();`; + } + return `${acc}\n ctx${parts + .slice(0, currentIndex + 1) + .map((s) => `["${s}"]`) + .join('')} = new HashMap();`; + }, ''); +} + +function createMetadataPainlessScript(definition: EntityDefinition) { + if (!definition.metadata) { + return ''; + } + return definition.metadata.reduce((script, def) => { + const source = def.source; + const destination = def.destination || def.source; + return `${script}if (ctx.entity?.metadata?.${source.replaceAll( + '.', + '?.' + )} != null) {${mapDesitnationToPainless(destination, source)}\n}\n`; + }, ''); +} + +export function generateProcessors(definition: EntityDefinition) { + return [ + { + set: { + field: 'event.ingested', + value: '{{{_ingest.timestamp}}}', + }, + }, + { + set: { + field: 'entity.definitionId', + value: definition.id, + }, + }, + { + set: { + field: 'entity.indexPatterns', + value: JSON.stringify(definition.indexPatterns), + }, + }, + { + json: { + field: 'entity.indexPatterns', + }, + }, + { + set: { + field: 'entity.id', + value: createIdTemplate(definition), + }, + }, + ...(definition.staticFields != null + ? Object.keys(definition.staticFields).map((field) => ({ + set: { field, value: definition.staticFields![field] }, + })) + : []), + ...(definition.metadata != null + ? [{ script: { source: createMetadataPainlessScript(definition) } }] + : []), + { + remove: { + field: 'entity.metadata', + ignore_missing: true, + }, + }, + { + set: { + field: '_index', + value: generateIndexName(definition), + }, + }, + ]; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/read_entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/read_entity_definition.ts new file mode 100644 index 0000000000000..e6817ab63f2af --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/read_entity_definition.ts @@ -0,0 +1,36 @@ +/* + * 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 { Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import { EntityDefinition, entityDefinitionSchema } from '@kbn/entities-schema'; +import { SO_ENTITY_DEFINITION_TYPE } from '../../saved_objects'; +import { EntityDefinitionNotFound } from './errors/entity_not_found'; + +export async function readEntityDefinition( + soClient: SavedObjectsClientContract, + id: string, + logger: Logger +) { + const response = await soClient.find({ + type: SO_ENTITY_DEFINITION_TYPE, + page: 1, + perPage: 1, + filter: `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${id})`, + }); + if (response.total === 0) { + const message = `Unable to find entity defintion with [${id}]`; + logger.error(message); + throw new EntityDefinitionNotFound(message); + } + + try { + return entityDefinitionSchema.parse(response.saved_objects[0].attributes); + } catch (e) { + logger.error(`Unable to parse entity defintion with [${id}]`); + throw e; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/save_entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/save_entity_definition.ts new file mode 100644 index 0000000000000..f79f88c50ac58 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/save_entity_definition.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsClientContract } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { SO_ENTITY_DEFINITION_TYPE } from '../../saved_objects'; +import { EntityIdConflict } from './errors/entity_id_conflict_error'; + +export async function saveEntityDefinition( + soClient: SavedObjectsClientContract, + definition: EntityDefinition +): Promise { + const response = await soClient.find({ + type: SO_ENTITY_DEFINITION_TYPE, + page: 1, + perPage: 1, + filter: `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${definition.id})`, + }); + + if (response.total === 1) { + throw new EntityIdConflict( + `Entity defintion with [${definition.id}] already exists.`, + definition + ); + } + + await soClient.create(SO_ENTITY_DEFINITION_TYPE, definition, { + id: definition.id, + overwrite: true, + }); + + return definition; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/start_transform.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/start_transform.ts new file mode 100644 index 0000000000000..766bbb10b1d67 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/start_transform.ts @@ -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 { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { retryTransientEsErrors } from './helpers/retry'; +import { generateTransformId } from './transform/generate_transform_id'; + +export async function startTransform( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const transformId = generateTransformId(definition); + try { + await retryTransientEsErrors( + () => esClient.transform.startTransform({ transform_id: transformId }, { ignore: [409] }), + { logger } + ); + } catch (err) { + logger.error(`Cannot start entity transform [${transformId}]`); + throw err; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/stop_and_delete_transform.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/stop_and_delete_transform.ts new file mode 100644 index 0000000000000..60a250a33f0d9 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/stop_and_delete_transform.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; +import { generateTransformId } from './transform/generate_transform_id'; +import { retryTransientEsErrors } from './helpers/retry'; + +export async function stopAndDeleteTransform( + esClient: ElasticsearchClient, + definition: EntityDefinition, + logger: Logger +) { + const transformId = generateTransformId(definition); + try { + await retryTransientEsErrors( + async () => { + await esClient.transform.stopTransform( + { transform_id: transformId, wait_for_completion: true, force: true }, + { ignore: [409] } + ); + await esClient.transform.deleteTransform( + { transform_id: transformId, force: true }, + { ignore: [404] } + ); + }, + { logger } + ); + } catch (e) { + logger.error(`Cannot stop or delete entity transform [${transformId}]`); + throw e; + } +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/__snapshots__/generate_transform.test.ts.snap b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/__snapshots__/generate_transform.test.ts.snap new file mode 100644 index 0000000000000..e692f2068eafd --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/__snapshots__/generate_transform.test.ts.snap @@ -0,0 +1,126 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`generateTransform(definition) should generate a valid summary transform 1`] = ` +Object { + "defer_validation": true, + "dest": Object { + "index": ".entities-observability.summary-v1.noop", + "pipeline": ".entities-observability.summary-v1.admin-console-logs-service", + }, + "frequency": "1m", + "pivot": Object { + "aggs": Object { + "_errorRate_A": Object { + "filter": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "match": Object { + "log.level": "error", + }, + }, + ], + }, + }, + }, + "_logRate_A": Object { + "filter": Object { + "bool": Object { + "minimum_should_match": 1, + "should": Array [ + Object { + "exists": Object { + "field": "log.level", + }, + }, + ], + }, + }, + }, + "entity.latestTimestamp": Object { + "max": Object { + "field": "@timestamp", + }, + }, + "entity.metadata.host.name": Object { + "terms": Object { + "field": "host.name", + "size": 1000, + }, + }, + "entity.metadata.kubernetes.pod.name": Object { + "terms": Object { + "field": "kubernetes.pod.name", + "size": 1000, + }, + }, + "entity.metadata.tags": Object { + "terms": Object { + "field": "tags", + "size": 1000, + }, + }, + "entity.metric.errorRate": Object { + "bucket_script": Object { + "buckets_path": Object { + "A": "_errorRate_A>_count", + }, + "script": Object { + "lang": "painless", + "source": "params.A / 5", + }, + }, + }, + "entity.metric.logRate": Object { + "bucket_script": Object { + "buckets_path": Object { + "A": "_logRate_A>_count", + }, + "script": Object { + "lang": "painless", + "source": "params.A / 5", + }, + }, + }, + }, + "group_by": Object { + "entity.identity.log.logger": Object { + "terms": Object { + "field": "log.logger", + "missing_bucket": false, + }, + }, + }, + }, + "settings": Object { + "deduce_mappings": false, + "unattended": true, + }, + "source": Object { + "index": Array [ + "kbn-data-forge-fake_stack.*", + ], + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "range": Object { + "@timestamp": Object { + "gte": "now-5m", + }, + }, + }, + ], + }, + }, + }, + "sync": Object { + "time": Object { + "delay": "60s", + "field": "@timestamp", + }, + }, + "transform_id": "entities-observability-summary-v1-admin-console-logs-service", +} +`; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metadata_aggregations.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metadata_aggregations.ts new file mode 100644 index 0000000000000..8c11aea138519 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metadata_aggregations.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; + +export function generateMetadataAggregations(definition: EntityDefinition) { + if (!definition.metadata) { + return {}; + } + return definition.metadata.reduce( + (aggs, metadata) => ({ + ...aggs, + [`entity.metadata.${metadata.destination ?? metadata.source}`]: { + terms: { + field: metadata.source, + size: metadata.limit ?? 1000, + }, + }, + }), + {} + ); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metric_aggregations.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metric_aggregations.ts new file mode 100644 index 0000000000000..9527671768e35 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_metric_aggregations.ts @@ -0,0 +1,118 @@ +/* + * 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 { KeyMetric, Metric, EntityDefinition } from '@kbn/entities-schema'; +import { getElasticsearchQueryOrThrow } from '../helpers/get_elasticsearch_query_or_throw'; +import { InvalidTransformError } from '../errors/invalid_transform_error'; + +function buildAggregation(metric: Metric, timestampField: string) { + const { aggregation } = metric; + switch (aggregation) { + case 'doc_count': + return {}; + case 'std_deviation': + return { + extended_stats: { field: metric.field }, + }; + case 'percentile': + if (metric.percentile == null) { + throw new InvalidTransformError( + 'You must provide a percentile value for percentile aggregations.' + ); + } + return { + percentiles: { + field: metric.field, + percents: [metric.percentile], + keyed: true, + }, + }; + case 'last_value': + return { + top_metrics: { + metrics: { field: metric.field }, + sort: { [timestampField]: 'desc' }, + }, + }; + default: + if (metric.field == null) { + throw new InvalidTransformError('You must provide a field for basic metric aggregations.'); + } + return { + [aggregation]: { field: metric.field }, + }; + } +} + +function buildMetricAggregations(keyMetric: KeyMetric, timestampField: string) { + return keyMetric.metrics.reduce((acc, metric) => { + const filter = metric.filter ? getElasticsearchQueryOrThrow(metric.filter) : { match_all: {} }; + const aggs = { metric: buildAggregation(metric, timestampField) }; + return { + ...acc, + [`_${keyMetric.name}_${metric.name}`]: { + filter, + ...(metric.aggregation !== 'doc_count' ? { aggs } : {}), + }, + }; + }, {}); +} + +function buildBucketPath(prefix: string, metric: Metric) { + const { aggregation } = metric; + switch (aggregation) { + case 'doc_count': + return `${prefix}>_count`; + case 'std_deviation': + return `${prefix}>metric[std_deviation]`; + case 'percentile': + return `${prefix}>metric[${metric.percentile}]`; + case 'last_value': + return `${prefix}>metric[${metric.field}]`; + default: + return `${prefix}>metric`; + } +} + +function convertEquationToPainless(bucketsPath: Record, equation: string) { + const workingEquation = equation || Object.keys(bucketsPath).join(' + '); + return Object.keys(bucketsPath).reduce((acc, key) => { + return acc.replaceAll(key, `params.${key}`); + }, workingEquation); +} + +function buildMetricEquation(keyMetric: KeyMetric) { + const bucketsPath = keyMetric.metrics.reduce( + (acc, metric) => ({ + ...acc, + [metric.name]: buildBucketPath(`_${keyMetric.name}_${metric.name}`, metric), + }), + {} + ); + return { + bucket_script: { + buckets_path: bucketsPath, + script: { + source: convertEquationToPainless(bucketsPath, keyMetric.equation), + lang: 'painless', + }, + }, + }; +} + +export function generateMetricAggregations(definition: EntityDefinition) { + if (!definition.metrics) { + return {}; + } + return definition.metrics.reduce((aggs, keyMetric) => { + return { + ...aggs, + ...buildMetricAggregations(keyMetric, definition.timestampField), + [`entity.metric.${keyMetric.name}`]: buildMetricEquation(keyMetric), + }; + }, {}); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.test.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.test.ts new file mode 100644 index 0000000000000..e97293b77dd4f --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.test.ts @@ -0,0 +1,16 @@ +/* + * 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 { entityDefinition } from '../helpers/fixtures/entity_definition'; +import { generateTransform } from './generate_transform'; + +describe('generateTransform(definition)', () => { + it('should generate a valid summary transform', () => { + const transform = generateTransform(entityDefinition); + expect(transform).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.ts new file mode 100644 index 0000000000000..6a8c0bd637715 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform.ts @@ -0,0 +1,86 @@ +/* + * 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 { EntityDefinition } from '@kbn/entities-schema'; +import { + QueryDslQueryContainer, + TransformPutTransformRequest, +} from '@elastic/elasticsearch/lib/api/types'; +import { getElasticsearchQueryOrThrow } from '../helpers/get_elasticsearch_query_or_throw'; +import { generateMetricAggregations } from './generate_metric_aggregations'; +import { + ENTITY_BASE_PREFIX, + ENTITY_DEFAULT_FREQUENCY, + ENTITY_DEFAULT_SYNC_DELAY, +} from '../../../../common/constants_entities'; +import { generateMetadataAggregations } from './generate_metadata_aggregations'; +import { generateTransformId } from './generate_transform_id'; +import { generateIngestPipelineId } from '../ingest_pipeline/generate_ingest_pipeline_id'; + +export function generateTransform(definition: EntityDefinition): TransformPutTransformRequest { + const filter: QueryDslQueryContainer[] = [ + { + range: { + [definition.timestampField]: { + gte: `now-${definition.lookback.toJSON()}`, + }, + }, + }, + ]; + + if (definition.filter) { + filter.push(getElasticsearchQueryOrThrow(definition.filter)); + } + + return { + transform_id: generateTransformId(definition), + defer_validation: true, + source: { + index: definition.indexPatterns, + query: { + bool: { + filter, + }, + }, + }, + dest: { + index: `${ENTITY_BASE_PREFIX}.noop`, + pipeline: generateIngestPipelineId(definition), + }, + frequency: definition.settings?.frequency || ENTITY_DEFAULT_FREQUENCY, + sync: { + time: { + field: definition.settings?.syncField ?? definition.timestampField, + delay: definition.settings?.syncDelay ?? ENTITY_DEFAULT_SYNC_DELAY, + }, + }, + settings: { + deduce_mappings: false, + unattended: true, + }, + pivot: { + group_by: definition.identityFields.reduce( + (acc, id) => ({ + ...acc, + [`entity.identity.${id.field}`]: { + terms: { field: id.field, missing_bucket: id.optional }, + }, + }), + {} + ), + aggs: { + ...generateMetricAggregations(definition), + ...generateMetadataAggregations(definition), + 'entity.latestTimestamp': { + max: { + field: definition.timestampField, + }, + }, + }, + }, + }; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform_id.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform_id.ts new file mode 100644 index 0000000000000..06faedb916774 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/entities/transform/generate_transform_id.ts @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EntityDefinition } from '@kbn/entities-schema'; +import { ENTITY_TRANSFORM_PREFIX } from '../../../../common/constants_entities'; + +export function generateTransformId(definition: EntityDefinition) { + return `${ENTITY_TRANSFORM_PREFIX}-${definition.id}`; +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/lib/manage_index_templates.ts b/x-pack/plugins/observability_solution/asset_manager/server/lib/manage_index_templates.ts index b853d7a360e1d..b364e63ff9a1f 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/lib/manage_index_templates.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/lib/manage_index_templates.ts @@ -6,6 +6,7 @@ */ import { + ClusterPutComponentTemplateRequest, IndicesGetIndexTemplateResponse, IndicesPutIndexTemplateRequest, } from '@elastic/elasticsearch/lib/api/types'; @@ -47,21 +48,18 @@ function templateExists( }); } -// interface IndexPatternJson { -// index_patterns: string[]; -// name: string; -// template: { -// mappings: Record; -// settings: Record; -// }; -// } - interface TemplateManagementOptions { esClient: ElasticsearchClient; template: IndicesPutIndexTemplateRequest; logger: Logger; } +interface ComponentManagementOptions { + esClient: ElasticsearchClient; + component: ClusterPutComponentTemplateRequest; + logger: Logger; +} + export async function maybeCreateTemplate({ esClient, template, @@ -93,9 +91,6 @@ export async function maybeCreateTemplate({ } export async function upsertTemplate({ esClient, template, logger }: TemplateManagementOptions) { - const pattern = ASSETS_INDEX_PREFIX + '*'; - template.index_patterns = [pattern]; - try { await esClient.indices.putIndexTemplate(template); } catch (error: any) { @@ -108,3 +103,17 @@ export async function upsertTemplate({ esClient, template, logger }: TemplateMan ); logger.debug(`Asset manager index template: ${JSON.stringify(template)}`); } + +export async function upsertComponent({ esClient, component, logger }: ComponentManagementOptions) { + try { + await esClient.cluster.putComponentTemplate(component); + } catch (error: any) { + logger.error(`Error updating asset manager component template: ${error.message}`); + return; + } + + logger.info( + `Asset manager component template is up to date (use debug logging to see what was installed)` + ); + logger.debug(`Asset manager component template: ${JSON.stringify(component)}`); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/plugin.ts b/x-pack/plugins/observability_solution/asset_manager/server/plugin.ts index 73b2cbe87c138..4c947926cf571 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/plugin.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/plugin.ts @@ -15,12 +15,17 @@ import { Logger, } from '@kbn/core/server'; -import { upsertTemplate } from './lib/manage_index_templates'; +import { upsertComponent, upsertTemplate } from './lib/manage_index_templates'; import { setupRoutes } from './routes'; import { assetsIndexTemplateConfig } from './templates/assets_template'; import { AssetClient } from './lib/asset_client'; import { AssetManagerPluginSetupDependencies, AssetManagerPluginStartDependencies } from './types'; import { AssetManagerConfig, configSchema, exposeToBrowserConfig } from '../common/config'; +import { entitiesBaseComponentTemplateConfig } from './templates/components/base'; +import { entitiesEventComponentTemplateConfig } from './templates/components/event'; +import { entitiesIndexTemplateConfig } from './templates/entities_template'; +import { entityDefinition } from './saved_objects'; +import { entitiesEntityComponentTemplateConfig } from './templates/components/entity'; export type AssetManagerServerPluginSetup = ReturnType; export type AssetManagerServerPluginStart = ReturnType; @@ -56,6 +61,8 @@ export class AssetManagerServerPlugin this.logger.info('Server is enabled'); + core.savedObjects.registerType(entityDefinition); + const assetClient = new AssetClient({ sourceIndices: this.config.sourceIndices, getApmIndices: plugins.apmDataAccess.getApmIndices, @@ -63,7 +70,7 @@ export class AssetManagerServerPlugin }); const router = core.http.createRouter(); - setupRoutes({ router, assetClient }); + setupRoutes({ router, assetClient, logger: this.logger }); return { assetClient, @@ -76,12 +83,36 @@ export class AssetManagerServerPlugin return; } + const esClient = core.elasticsearch.client.asInternalUser; upsertTemplate({ - esClient: core.elasticsearch.client.asInternalUser, + esClient, template: assetsIndexTemplateConfig, logger: this.logger, }).catch(() => {}); // it shouldn't reject, but just in case + // Install entities compoent templates and index template + Promise.all([ + upsertComponent({ + esClient, + logger: this.logger, + component: entitiesBaseComponentTemplateConfig, + }), + upsertComponent({ + esClient, + logger: this.logger, + component: entitiesEventComponentTemplateConfig, + }), + upsertComponent({ + esClient, + logger: this.logger, + component: entitiesEntityComponentTemplateConfig, + }), + ]) + .then(() => + upsertTemplate({ esClient, logger: this.logger, template: entitiesIndexTemplateConfig }) + ) + .catch(() => {}); + return {}; } diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/create.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/create.ts new file mode 100644 index 0000000000000..d403c39ae0ed1 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/create.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RequestHandlerContext } from '@kbn/core/server'; +import { EntityDefinition, entityDefinitionSchema } from '@kbn/entities-schema'; +import { stringifyZodError } from '@kbn/zod-helpers'; +import { SetupRouteOptions } from '../types'; +import { saveEntityDefinition } from '../../lib/entities/save_entity_definition'; +import { createAndInstallIngestPipeline } from '../../lib/entities/create_and_install_ingest_pipeline'; +import { EntityIdConflict } from '../../lib/entities/errors/entity_id_conflict_error'; +import { createAndInstallTransform } from '../../lib/entities/create_and_install_transform'; +import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; +import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error'; +import { startTransform } from '../../lib/entities/start_transform'; +import { deleteEntityDefinition } from '../../lib/entities/delete_entity_definition'; +import { deleteIngestPipeline } from '../../lib/entities/delete_ingest_pipeline'; +import { stopAndDeleteTransform } from '../../lib/entities/stop_and_delete_transform'; +import { ENTITY_API_PREFIX } from '../../../common/constants_entities'; + +export function createEntityDefinitionRoute({ + router, + logger, +}: SetupRouteOptions) { + router.post( + { + path: `${ENTITY_API_PREFIX}/definition`, + validate: { + body: (body, res) => { + try { + return res.ok(entityDefinitionSchema.parse(body)); + } catch (e) { + return res.badRequest(stringifyZodError(e)); + } + }, + }, + }, + async (context, req, res) => { + let definitionCreated = false; + let ingestPipelineCreated = false; + let transformCreated = false; + const soClient = (await context.core).savedObjects.client; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + try { + const definition = await saveEntityDefinition(soClient, req.body); + definitionCreated = true; + await createAndInstallIngestPipeline(esClient, definition, logger); + ingestPipelineCreated = true; + await createAndInstallTransform(esClient, definition, logger); + transformCreated = true; + await startTransform(esClient, definition, logger); + + return res.ok({ body: definition }); + } catch (e) { + // Clean up anything that was successful. + if (definitionCreated) { + await deleteEntityDefinition(soClient, req.body, logger); + } + if (ingestPipelineCreated) { + await deleteIngestPipeline(esClient, req.body, logger); + } + if (transformCreated) { + await stopAndDeleteTransform(esClient, req.body, logger); + } + if (e instanceof EntityIdConflict) { + return res.conflict({ body: e }); + } + if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) { + return res.customError({ body: e, statusCode: 400 }); + } + return res.customError({ body: e, statusCode: 500 }); + } + } + ); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/delete.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/delete.ts new file mode 100644 index 0000000000000..e1b273780a64f --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/delete.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 { RequestHandlerContext } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { SetupRouteOptions } from '../types'; +import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; +import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error'; +import { readEntityDefinition } from '../../lib/entities/read_entity_definition'; +import { stopAndDeleteTransform } from '../../lib/entities/stop_and_delete_transform'; +import { deleteIngestPipeline } from '../../lib/entities/delete_ingest_pipeline'; +import { deleteEntityDefinition } from '../../lib/entities/delete_entity_definition'; +import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_found'; +import { ENTITY_API_PREFIX } from '../../../common/constants_entities'; + +export function deleteEntityDefinitionRoute({ + router, + logger, +}: SetupRouteOptions) { + router.delete<{ id: string }, unknown, unknown>( + { + path: `${ENTITY_API_PREFIX}/definition/{id}`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + async (context, req, res) => { + try { + const soClient = (await context.core).savedObjects.client; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + + const definition = await readEntityDefinition(soClient, req.params.id, logger); + await stopAndDeleteTransform(esClient, definition, logger); + await deleteIngestPipeline(esClient, definition, logger); + await deleteEntityDefinition(soClient, definition, logger); + + return res.ok({ body: { acknowledged: true } }); + } catch (e) { + if (e instanceof EntityDefinitionNotFound) { + return res.notFound({ body: e }); + } + if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) { + return res.customError({ body: e, statusCode: 400 }); + } + return res.customError({ body: e, statusCode: 500 }); + } + } + ); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/reset.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/reset.ts new file mode 100644 index 0000000000000..3109ffc44520f --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/entities/reset.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RequestHandlerContext } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; +import { SetupRouteOptions } from '../types'; +import { EntitySecurityException } from '../../lib/entities/errors/entity_security_exception'; +import { InvalidTransformError } from '../../lib/entities/errors/invalid_transform_error'; +import { readEntityDefinition } from '../../lib/entities/read_entity_definition'; +import { stopAndDeleteTransform } from '../../lib/entities/stop_and_delete_transform'; +import { deleteIngestPipeline } from '../../lib/entities/delete_ingest_pipeline'; +import { deleteIndex } from '../../lib/entities/delete_index'; +import { createAndInstallIngestPipeline } from '../../lib/entities/create_and_install_ingest_pipeline'; +import { createAndInstallTransform } from '../../lib/entities/create_and_install_transform'; +import { startTransform } from '../../lib/entities/start_transform'; +import { EntityDefinitionNotFound } from '../../lib/entities/errors/entity_not_found'; +import { ENTITY_API_PREFIX } from '../../../common/constants_entities'; + +export function resetEntityDefinitionRoute({ + router, + logger, +}: SetupRouteOptions) { + router.post<{ id: string }, unknown, unknown>( + { + path: `${ENTITY_API_PREFIX}/definition/{id}/_reset`, + validate: { + params: schema.object({ + id: schema.string(), + }), + }, + }, + async (context, req, res) => { + try { + const soClient = (await context.core).savedObjects.client; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + + const definition = await readEntityDefinition(soClient, req.params.id, logger); + + // Delete the transform and ingest pipeline + await stopAndDeleteTransform(esClient, definition, logger); + await deleteIngestPipeline(esClient, definition, logger); + await deleteIndex(esClient, definition, logger); + + // Recreate everything + await createAndInstallIngestPipeline(esClient, definition, logger); + await createAndInstallTransform(esClient, definition, logger); + await startTransform(esClient, definition, logger); + + return res.ok({ body: { acknowledged: true } }); + } catch (e) { + if (e instanceof EntityDefinitionNotFound) { + return res.notFound({ body: e }); + } + if (e instanceof EntitySecurityException || e instanceof InvalidTransformError) { + return res.customError({ body: e, statusCode: 400 }); + } + return res.customError({ body: e, statusCode: 500 }); + } + } + ); +} diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/index.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/index.ts index 52d3198bb8a9f..d0b6c9f7ff0f1 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/routes/index.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/index.ts @@ -14,16 +14,23 @@ import { hostsRoutes } from './assets/hosts'; import { servicesRoutes } from './assets/services'; import { containersRoutes } from './assets/containers'; import { podsRoutes } from './assets/pods'; +import { createEntityDefinitionRoute } from './entities/create'; +import { deleteEntityDefinitionRoute } from './entities/delete'; +import { resetEntityDefinitionRoute } from './entities/reset'; export function setupRoutes({ router, assetClient, + logger, }: SetupRouteOptions) { - pingRoute({ router, assetClient }); - sampleAssetsRoutes({ router, assetClient }); - assetsRoutes({ router, assetClient }); - hostsRoutes({ router, assetClient }); - servicesRoutes({ router, assetClient }); - containersRoutes({ router, assetClient }); - podsRoutes({ router, assetClient }); + pingRoute({ router, assetClient, logger }); + sampleAssetsRoutes({ router, assetClient, logger }); + assetsRoutes({ router, assetClient, logger }); + hostsRoutes({ router, assetClient, logger }); + servicesRoutes({ router, assetClient, logger }); + containersRoutes({ router, assetClient, logger }); + podsRoutes({ router, assetClient, logger }); + createEntityDefinitionRoute({ router, assetClient, logger }); + deleteEntityDefinitionRoute({ router, assetClient, logger }); + resetEntityDefinitionRoute({ router, assetClient, logger }); } diff --git a/x-pack/plugins/observability_solution/asset_manager/server/routes/types.ts b/x-pack/plugins/observability_solution/asset_manager/server/routes/types.ts index ae1b967a5b596..561819d18cdae 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/routes/types.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/routes/types.ts @@ -6,9 +6,11 @@ */ import { IRouter, RequestHandlerContextBase } from '@kbn/core-http-server'; +import { Logger } from '@kbn/core/server'; import { AssetClient } from '../lib/asset_client'; export interface SetupRouteOptions { router: IRouter; assetClient: AssetClient; + logger: Logger; } diff --git a/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/entity_definition.ts b/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/entity_definition.ts new file mode 100644 index 0000000000000..5a63444974ad7 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/entity_definition.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObject, SavedObjectsType } from '@kbn/core/server'; +import { EntityDefinition } from '@kbn/entities-schema'; + +export const SO_ENTITY_DEFINITION_TYPE = 'entity-definition'; + +export const entityDefinition: SavedObjectsType = { + name: SO_ENTITY_DEFINITION_TYPE, + hidden: false, + namespaceType: 'multiple-isolated', + mappings: { + dynamic: false, + properties: { + id: { type: 'keyword' }, + name: { type: 'text' }, + description: { type: 'text' }, + type: { type: 'keyword' }, + filter: { type: 'keyword' }, + indexPatterns: { type: 'keyword' }, + identityFields: { type: 'object' }, + categories: { type: 'keyword' }, + metadata: { type: 'object' }, + metrics: { type: 'object' }, + staticFields: { type: 'object' }, + }, + }, + management: { + displayName: 'Entity Definition', + importableAndExportable: false, + getTitle(sloSavedObject: SavedObject) { + return `EntityDefinition: [${sloSavedObject.attributes.name}]`; + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/index.ts b/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/index.ts new file mode 100644 index 0000000000000..6145b05438bb2 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/saved_objects/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { entityDefinition, SO_ENTITY_DEFINITION_TYPE } from './entity_definition'; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/assets_template.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/assets_template.ts index 71d4058eba4c0..b99ecc4559187 100644 --- a/x-pack/plugins/observability_solution/asset_manager/server/templates/assets_template.ts +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/assets_template.ts @@ -6,11 +6,13 @@ */ import { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { ASSETS_INDEX_PREFIX } from '../constants'; export const assetsIndexTemplateConfig: IndicesPutIndexTemplateRequest = { name: 'assets', priority: 100, data_stream: {}, + index_patterns: [`${ASSETS_INDEX_PREFIX}*`], template: { settings: {}, mappings: { diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/components/base.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/base.ts new file mode 100644 index 0000000000000..adf527d653d9c --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/base.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 { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; + +export const entitiesBaseComponentTemplateConfig: ClusterPutComponentTemplateRequest = { + name: 'entities_v1_base', + _meta: { + documentation: 'https://www.elastic.co/guide/en/ecs/current/ecs-base.html', + ecs_version: '8.0.0', + }, + template: { + mappings: { + properties: { + '@timestamp': { + type: 'date', + }, + labels: { + type: 'object', + }, + tags: { + ignore_above: 1024, + type: 'keyword', + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/components/entity.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/entity.ts new file mode 100644 index 0000000000000..e696d32e0dfb2 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/entity.ts @@ -0,0 +1,40 @@ +/* + * 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 { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; + +export const entitiesEntityComponentTemplateConfig: ClusterPutComponentTemplateRequest = { + name: 'entities_v1_entity', + _meta: { + ecs_version: '8.0.0', + }, + template: { + mappings: { + properties: { + entity: { + properties: { + id: { + ignore_above: 1024, + type: 'keyword', + }, + indexPatterns: { + ignore_above: 1024, + type: 'keyword', + }, + defintionId: { + ignore_above: 1024, + type: 'keyword', + }, + latestTimestamp: { + type: 'date', + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/components/event.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/event.ts new file mode 100644 index 0000000000000..6ad4b628fdf36 --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/components/event.ts @@ -0,0 +1,29 @@ +/* + * 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 { ClusterPutComponentTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; + +export const entitiesEventComponentTemplateConfig: ClusterPutComponentTemplateRequest = { + name: 'entities_v1_event', + _meta: { + documentation: 'https://www.elastic.co/guide/en/ecs/current/ecs-event.html', + ecs_version: '8.0.0', + }, + template: { + mappings: { + properties: { + event: { + properties: { + ingested: { + type: 'date', + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/server/templates/entities_template.ts b/x-pack/plugins/observability_solution/asset_manager/server/templates/entities_template.ts new file mode 100644 index 0000000000000..d3934b24ea82c --- /dev/null +++ b/x-pack/plugins/observability_solution/asset_manager/server/templates/entities_template.ts @@ -0,0 +1,60 @@ +/* + * 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 { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types'; +import { ENTITY_BASE_PREFIX } from '../../common/constants_entities'; + +export const entitiesIndexTemplateConfig: IndicesPutIndexTemplateRequest = { + name: 'entities_v1_index_template', + _meta: { + description: 'The entities index template', + ecs_version: '8.0.0', + }, + composed_of: ['entities_v1_base', 'entities_v1_event', 'entities_v1_entity'], + index_patterns: [`${ENTITY_BASE_PREFIX}.*`], + priority: 1, + template: { + mappings: { + _meta: { + version: '1.6.0', + }, + date_detection: false, + dynamic_templates: [ + { + strings_as_keyword: { + mapping: { + ignore_above: 1024, + type: 'keyword', + }, + match_mapping_type: 'string', + }, + }, + { + entity_metrics: { + mapping: { + // @ts-expect-error this should work per: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html#match-mapping-type + type: '{dynamic_type}', + }, + // @ts-expect-error this should work per: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-templates.html#match-mapping-type + match_mapping_type: ['long', 'double'], + path_match: 'entity.metric.*', + }, + }, + ], + }, + settings: { + index: { + codec: 'best_compression', + mapping: { + total_fields: { + limit: 2000, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_solution/asset_manager/tsconfig.json b/x-pack/plugins/observability_solution/asset_manager/tsconfig.json index da1095d989afb..dbbc36252bfb1 100644 --- a/x-pack/plugins/observability_solution/asset_manager/tsconfig.json +++ b/x-pack/plugins/observability_solution/asset_manager/tsconfig.json @@ -26,6 +26,9 @@ "@kbn/metrics-data-access-plugin", "@kbn/core-elasticsearch-server", "@kbn/core-saved-objects-api-server", - "@kbn/core-saved-objects-api-server-mocks" + "@kbn/core-saved-objects-api-server-mocks", + "@kbn/entities-schema", + "@kbn/es-query", + "@kbn/zod-helpers" ] } diff --git a/yarn.lock b/yarn.lock index 98ce6357eebcd..63f50a8f16e93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4583,6 +4583,10 @@ version "0.0.0" uid "" +"@kbn/entities-schema@link:x-pack/packages/kbn-entities-schema": + version "0.0.0" + uid "" + "@kbn/error-boundary-example-plugin@link:examples/error_boundary": version "0.0.0" uid "" From 8e294fdbf5139021c2a2f48eb92ddd31f550c182 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 May 2024 19:30:54 +0100 Subject: [PATCH 69/71] skip flaky suite (#183718) --- .../cypress/e2e/artifacts/artifacts_mocked_data.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts index 9227eb80153fb..054ec73149e02 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts @@ -50,7 +50,8 @@ describe('Artifacts pages', { tags: ['@ess', '@serverless', '@skipInServerlessMK }); for (const testData of getArtifactsListTestsData()) { - describe(`When on the ${testData.title} entries list`, () => { + // FLAKY: https://github.com/elastic/kibana/issues/183718 + describe.skip(`When on the ${testData.title} entries list`, () => { it(`no access - should show no privileges callout`, () => { loginWithoutAccess(`/app/security/administration/${testData.urlPath}`); cy.getByTestSubj('noPrivilegesPage').should('exist'); From 2ef36a6431b73583a88c4105bc4b5e7cfb223d80 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 May 2024 19:31:37 +0100 Subject: [PATCH 70/71] skip flaky suite (#183719) --- .../management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts index 054ec73149e02..943ca171c527c 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts @@ -51,6 +51,7 @@ describe('Artifacts pages', { tags: ['@ess', '@serverless', '@skipInServerlessMK for (const testData of getArtifactsListTestsData()) { // FLAKY: https://github.com/elastic/kibana/issues/183718 + // FLAKY: https://github.com/elastic/kibana/issues/183719 describe.skip(`When on the ${testData.title} entries list`, () => { it(`no access - should show no privileges callout`, () => { loginWithoutAccess(`/app/security/administration/${testData.urlPath}`); From 6ba836b0233694ca35542a856c1f19c7f04295ca Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 May 2024 19:32:04 +0100 Subject: [PATCH 71/71] skip flaky suite (#183720) --- .../management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts index 943ca171c527c..22a796767a530 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts @@ -52,6 +52,7 @@ describe('Artifacts pages', { tags: ['@ess', '@serverless', '@skipInServerlessMK for (const testData of getArtifactsListTestsData()) { // FLAKY: https://github.com/elastic/kibana/issues/183718 // FLAKY: https://github.com/elastic/kibana/issues/183719 + // FLAKY: https://github.com/elastic/kibana/issues/183720 describe.skip(`When on the ${testData.title} entries list`, () => { it(`no access - should show no privileges callout`, () => { loginWithoutAccess(`/app/security/administration/${testData.urlPath}`);