From 122647b7c286f5c6a429e73166ff2dd542779f04 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:42:04 +0100 Subject: [PATCH 01/38] [ResponseOps][Alerts] Fix DSL filter issues in search bar (#193623) ## Summary Resolves https://github.com/elastic/kibana/issues/183908 Resolves https://github.com/elastic/kibana/issues/192557 This PR fixes an issue of DSL filter in
Search bar in Alerts page of Stack management image
Search bar in Alerts page of Observability image
Search bar in "If alerts matches query" action filter image
Search bar in Maintenance window page image
### 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 ### How to verify - Create a DSL filter in above mentioned pages - Verify that filter is applied properly --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../src/alerts_search_bar/index.test.tsx | 171 ++++++++++++++++ .../src/alerts_search_bar/index.tsx | 9 +- .../src/alerts_search_bar/types.ts | 4 +- .../rule_actions_alerts_filter.test.tsx | 160 +++++++++++---- .../rule_actions_alerts_filter.tsx | 20 +- .../create_maintenance_windows_form.tsx | 17 +- .../maintenance_window_scoped_query.tsx | 2 +- .../application/lib/value_validators.test.ts | 70 ++++++- .../action_type_form.tsx | 4 +- .../alerts_search_bar.test.tsx | 190 ++++++++++++++++++ .../alerts_search_bar/alerts_search_bar.tsx | 7 +- .../alert_create_flyout.ts | 92 ++++++++- 12 files changed, 686 insertions(+), 60 deletions(-) create mode 100644 packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.test.tsx diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.test.tsx b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.test.tsx new file mode 100644 index 00000000000000..a7ae24e88dd71f --- /dev/null +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.test.tsx @@ -0,0 +1,171 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import React from 'react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { Filter, FilterStateStore } from '@kbn/es-query'; +import { ToastsStart } from '@kbn/core-notifications-browser'; +import { useLoadRuleTypesQuery, useRuleAADFields, useAlertsDataView } from '../common/hooks'; +import { AlertsSearchBar } from '.'; +import { HttpStart } from '@kbn/core-http-browser'; + +const mockDataPlugin = dataPluginMock.createStartContract(); +jest.mock('@kbn/kibana-utils-plugin/public'); +jest.mock('../common/hooks'); + +jest.mocked(useAlertsDataView).mockReturnValue({ + isLoading: false, + dataView: { + title: '.alerts-*', + fields: [ + { + name: 'event.action', + type: 'string', + aggregatable: true, + searchable: true, + }, + ], + }, +}); + +jest.mocked(useLoadRuleTypesQuery).mockReturnValue({ + ruleTypesState: { + isInitialLoad: false, + data: new Map(), + isLoading: false, + error: null, + }, + authorizedToReadAnyRules: false, + hasAnyAuthorizedRuleType: false, + authorizedRuleTypes: [], + authorizedToCreateAnyRules: false, + isSuccess: false, +}); + +jest.mocked(useRuleAADFields).mockReturnValue({ + aadFields: [], + loading: false, +}); + +const unifiedSearchBarMock = jest.fn().mockImplementation((props) => ( + +)); + +const toastsMock = { toasts: { addWarning: jest.fn() } } as unknown as ToastsStart; +const httpMock = { + post: jest.fn(), +} as unknown as HttpStart; + +describe('AlertsSearchBar', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly', async () => { + render( + + ); + expect(await screen.findByTestId('querySubmitButton')).toBeInTheDocument(); + }); + + it('calls onQuerySubmit correctly', async () => { + const onQuerySubmitMock = jest.fn(); + + render( + + ); + + fireEvent.click(await screen.findByTestId('querySubmitButton')); + + await waitFor(() => { + expect(onQuerySubmitMock).toHaveBeenCalled(); + }); + }); + + it('calls onFiltersUpdated correctly', async () => { + const onFiltersUpdatedMock = jest.fn(); + const filters: Filter[] = [ + { + meta: { + negate: false, + alias: null, + disabled: false, + type: 'custom', + key: 'query', + }, + query: { bool: { filter: [{ term: { 'kibana.alert.rule.consumer': 'stackAlerts' } }] } }, + $state: { store: FilterStateStore.APP_STATE }, + }, + ]; + + const newUnifiedSearchBarMock = jest.fn().mockImplementation((props) => ( + + )); + + render( + + ); + + fireEvent.click(await screen.findByTestId('filtersSubmitButton')); + + await waitFor(() => { + expect(onFiltersUpdatedMock).toHaveBeenCalledWith(filters); + expect(mockDataPlugin.query.filterManager.setFilters).toHaveBeenCalledWith(filters); + }); + }); +}); diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx index efda77df2c4063..00822d013e0d29 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/index.tsx @@ -41,14 +41,14 @@ export const AlertsSearchBar = ({ http, toasts, unifiedSearchBar, - dataViewsService, + dataService, }: AlertsSearchBarProps) => { const [queryLanguage, setQueryLanguage] = useState('kuery'); const { dataView } = useAlertsDataView({ featureIds, http, toasts, - dataViewsService, + dataViewsService: dataService.dataViews, }); const { aadFields, loading: fieldsLoading } = useRuleAADFields({ ruleTypeId, @@ -119,7 +119,10 @@ export const AlertsSearchBar = ({ displayStyle: 'inPage', showFilterBar, onQuerySubmit: onSearchQuerySubmit, - onFiltersUpdated, + onFiltersUpdated: (newFilters) => { + dataService.query.filterManager.setFilters(newFilters); + onFiltersUpdated?.(newFilters); + }, onRefresh, showDatePicker, showQueryInput: true, diff --git a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts index 8875ef62947a84..f8566b2ec692cc 100644 --- a/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts +++ b/packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts @@ -11,7 +11,7 @@ import type { Filter } from '@kbn/es-query'; import type { ValidFeatureId } from '@kbn/rule-data-utils'; import type { ToastsStart, HttpStart } from '@kbn/core/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; -import type { DataViewsContract } from '@kbn/data-views-plugin/common'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; export type QueryLanguageType = 'lucene' | 'kuery'; @@ -41,5 +41,5 @@ export interface AlertsSearchBarProps { http: HttpStart; toasts: ToastsStart; unifiedSearchBar: UnifiedSearchPublicPluginStart['ui']['SearchBar']; - dataViewsService: DataViewsContract; + dataService: DataPublicPluginStart; } diff --git a/packages/kbn-alerts-ui-shared/src/rule_form/rule_actions/rule_actions_alerts_filter.test.tsx b/packages/kbn-alerts-ui-shared/src/rule_form/rule_actions/rule_actions_alerts_filter.test.tsx index d3ef68206cfa46..9b7502add4045e 100644 --- a/packages/kbn-alerts-ui-shared/src/rule_form/rule_actions/rule_actions_alerts_filter.test.tsx +++ b/packages/kbn-alerts-ui-shared/src/rule_form/rule_actions/rule_actions_alerts_filter.test.tsx @@ -9,12 +9,12 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { httpServiceMock } from '@kbn/core/public/mocks'; -import { RuleActionsAlertsFilter } from './rule_actions_alerts_filter'; -import type { AlertsSearchBarProps } from '../../alerts_search_bar'; import { FilterStateStore } from '@kbn/es-query'; +import { AlertsSearchBarProps, AlertsSearchBar } from '../../alerts_search_bar'; import { getAction } from '../../common/test_utils/actions_test_utils'; -import userEvent from '@testing-library/user-event'; +import { RuleActionsAlertsFilter } from './rule_actions_alerts_filter'; const http = httpServiceMock.createStartContract(); @@ -23,43 +23,7 @@ jest.mock('../hooks', () => ({ })); jest.mock('../../alerts_search_bar', () => ({ - AlertsSearchBar: ({ onFiltersUpdated, onQueryChange, onQuerySubmit }: AlertsSearchBarProps) => ( -
- AlertsSearchBar - - - -
- ), + AlertsSearchBar: jest.fn(), })); const { useRuleFormState } = jest.requireMock('../hooks'); @@ -79,7 +43,7 @@ describe('ruleActionsAlertsFilter', () => { SearchBar: {}, }, }, - dataViews: {}, + data: {}, }, formData: { actions: [], @@ -93,6 +57,46 @@ describe('ruleActionsAlertsFilter', () => { }); }); + (AlertsSearchBar as jest.Mock).mockImplementation( + ({ onFiltersUpdated, onQueryChange, onQuerySubmit }: AlertsSearchBarProps) => ( +
+ AlertsSearchBar + + + +
+ ) + ); + afterEach(() => { jest.clearAllMocks(); }); @@ -166,18 +170,86 @@ describe('ruleActionsAlertsFilter', () => { await userEvent.click(screen.getByText('Update Filter')); expect(mockOnChange).toHaveBeenLastCalledWith({ - filters: [{ $state: { store: 'appState' }, meta: {} }], + filters: [{ $state: { store: 'appState' }, meta: {}, query: {} }], kql: 'test', }); await userEvent.click(screen.getByText('Update Query')); expect(mockOnChange).toHaveBeenLastCalledWith({ - filters: [{ $state: { store: 'appState' }, meta: {} }], + filters: [{ $state: { store: 'appState' }, meta: {}, query: {} }], kql: 'onQueryChange', }); await userEvent.click(screen.getByText('Submit Query')); expect(mockOnChange).toHaveBeenLastCalledWith({ - filters: [{ $state: { store: 'appState' }, meta: {} }], + filters: [{ $state: { store: 'appState' }, meta: {}, query: {} }], kql: 'onQuerySubmit', }); }); + + test('renders filters correctly', async () => { + const filters = [ + { + meta: { + negate: false, + alias: null, + disabled: false, + type: 'custom', + key: 'query', + }, + query: { bool: { filter: [{ term: { 'kibana.alert.rule.consumer': 'stackAlerts' } }] } }, + $state: { store: FilterStateStore.APP_STATE }, + }, + ]; + + (AlertsSearchBar as jest.Mock).mockImplementation( + ({ onFiltersUpdated, onQueryChange, onQuerySubmit }: AlertsSearchBarProps) => ( +
+ AlertsSearchBar + + + +
+ ) + ); + + render( + + ); + + await userEvent.click(screen.getByTestId('alertsFilterQueryToggle')); + + expect(mockOnChange).toHaveBeenLastCalledWith(undefined); + + await userEvent.click(screen.getByText('Update Filter')); + expect(mockOnChange).toHaveBeenLastCalledWith({ filters, kql: '' }); + }); }); diff --git a/packages/kbn-alerts-ui-shared/src/rule_form/rule_actions/rule_actions_alerts_filter.tsx b/packages/kbn-alerts-ui-shared/src/rule_form/rule_actions/rule_actions_alerts_filter.tsx index 646d1b359969bc..791c1ce0491f28 100644 --- a/packages/kbn-alerts-ui-shared/src/rule_form/rule_actions/rule_actions_alerts_filter.tsx +++ b/packages/kbn-alerts-ui-shared/src/rule_form/rule_actions/rule_actions_alerts_filter.tsx @@ -31,7 +31,7 @@ export interface RuleActionsAlertsFilterProps { http: RuleFormPlugins['http']; notifications: RuleFormPlugins['notifications']; unifiedSearch: RuleFormPlugins['unifiedSearch']; - dataViews: RuleFormPlugins['dataViews']; + data: RuleFormPlugins['data']; }; } @@ -48,7 +48,7 @@ export const RuleActionsAlertsFilter = ({ http, notifications: { toasts }, unifiedSearch, - dataViews, + data, } = propsPlugins || plugins; const [query, setQuery] = useState(action.alertsFilter?.query ?? DEFAULT_QUERY); @@ -84,7 +84,19 @@ export const RuleActionsAlertsFilter = ({ ); const onFiltersUpdated = useCallback( - (filters: Filter[]) => updateQuery({ filters }), + (filters: Filter[]) => { + const updatedFilters = filters.map((filter) => { + const { $state, meta, ...rest } = filter; + return { + $state, + meta, + query: filter?.query ? { ...filter.query } : { ...rest }, + }; + }); + + // Wrapping filters in query object here to avoid schema validation failure + updateQuery({ filters: updatedFilters }); + }, [updateQuery] ); @@ -108,7 +120,7 @@ export const RuleActionsAlertsFilter = ({ http={http} toasts={toasts} unifiedSearchBar={unifiedSearch.ui.SearchBar} - dataViewsService={dataViews} + dataService={data} appName={appName} featureIds={featureIds} ruleTypeId={ruleTypeId} diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx index 6783289963f6c7..7f3c25eeaf55d0 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.tsx @@ -117,6 +117,17 @@ export const CreateMaintenanceWindowForm = React.memo { + return filtersToTransform.map((filter) => { + const { $state, meta, ...rest } = filter; + return { + $state, + meta, + query: filter?.query ? { ...filter.query } : { ...rest }, + }; + }); + }; + const scopedQueryPayload = useMemo(() => { if (!isScopedQueryEnabled) { return null; @@ -124,9 +135,13 @@ export const CreateMaintenanceWindowForm = React.memo diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts index 97c9ba686435a9..df1dce08f684ab 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/value_validators.test.ts @@ -11,9 +11,10 @@ import { isValidUrl, getConnectorWithInvalidatedFields, getRuleWithInvalidatedFields, + validateActionFilterQuery, } from './value_validators'; import { v4 as uuidv4 } from 'uuid'; -import { Rule, IErrorObject, UserConfiguredActionConnector } from '../../types'; +import { Rule, IErrorObject, UserConfiguredActionConnector, RuleUiAction } from '../../types'; describe('throwIfAbsent', () => { test('throws if value is absent', () => { @@ -441,3 +442,70 @@ describe('getRuleWithInvalidatedFields', () => { expect((rule.actions[1].params as any).incident.field.name).toEqual('myIncident'); }); }); + +describe('validateActionFilterQuery', () => { + test('does not return an error when kql query exists', () => { + const actionItem: RuleUiAction = { + actionTypeId: 'test', + group: 'qwer', + id: '123', + params: { + incident: { + field: { + name: 'myIncident', + }, + }, + }, + alertsFilter: { query: { kql: 'id: *', filters: [] } }, + }; + + expect(validateActionFilterQuery(actionItem)).toBe(null); + }); + + test('does not return an error when filter query exists', () => { + const actionItem = { + actionTypeId: 'test', + group: 'qwer', + id: '123', + params: { + incident: { + field: { + name: 'myIncident', + }, + }, + }, + alertsFilter: { + query: { + kql: undefined, + filters: [ + { + $state: { store: 'state' }, + meta: { key: 'test' }, + query: { exists: { field: '_id' } }, + }, + ], + }, + }, + }; + + expect(validateActionFilterQuery(actionItem)).toBe(null); + }); + + test('returns an error no kql and no filters', () => { + const actionItem = { + actionTypeId: 'test', + group: 'qwer', + id: '123', + params: { + incident: { + field: { + name: 'myIncident', + }, + }, + }, + alertsFilter: { query: { kql: '', filters: [] } }, + }; + + expect(validateActionFilterQuery(actionItem)).toBe('A custom query is required.'); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx index 9176d9d54ef3af..9af97b713dff3e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_form.tsx @@ -157,7 +157,7 @@ export const ActionTypeForm = ({ http, notifications, unifiedSearch, - dataViews, + data, } = useKibana().services; const { euiTheme } = useEuiTheme(); const [isOpen, setIsOpen] = useState(true); @@ -513,7 +513,7 @@ export const ActionTypeForm = ({ plugins={{ http, unifiedSearch, - dataViews, + data, notifications, }} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.test.tsx new file mode 100644 index 00000000000000..61fceacead3413 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.test.tsx @@ -0,0 +1,190 @@ +/* + * 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 { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { useAlertsDataView } from '@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'; +import { Filter, FilterStateStore } from '@kbn/es-query'; +import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; +import { NotificationsStart } from '@kbn/core-notifications-browser'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useLoadRuleTypesQuery } from '../../hooks/use_load_rule_types_query'; +import { useRuleAADFields } from '../../hooks/use_rule_aad_fields'; +import { AlertsSearchBar } from './alerts_search_bar'; + +const mockDataPlugin = dataPluginMock.createStartContract(); +jest.mock('@kbn/kibana-utils-plugin/public'); +jest.mock('../../hooks/use_load_rule_types_query'); +jest.mock('../../hooks/use_rule_aad_fields'); +jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'); +jest.mock('@kbn/kibana-react-plugin/public', () => ({ + ...jest.requireActual('@kbn/kibana-react-plugin/public'), + useKibana: jest.fn(), +})); + +jest.mocked(useAlertsDataView).mockReturnValue({ + isLoading: false, + dataView: { + title: '.alerts-*', + fields: [ + { + name: 'event.action', + type: 'string', + aggregatable: true, + searchable: true, + }, + ], + }, +}); + +jest.mocked(useLoadRuleTypesQuery).mockReturnValue({ + ruleTypesState: { + initialLoad: false, + data: new Map(), + isLoading: false, + error: undefined, + }, + authorizedToReadAnyRules: false, + hasAnyAuthorizedRuleType: false, + authorizedRuleTypes: [], + authorizedToCreateAnyRules: false, + isSuccess: false, +}); + +jest.mocked(useRuleAADFields).mockReturnValue({ + aadFields: [], + loading: false, +}); + +const mockUseKibana = useKibana as jest.Mock; + +describe('AlertsSearchBar', () => { + beforeEach(() => { + mockUseKibana.mockReturnValue({ + services: { + data: mockDataPlugin, + unifiedSearch: { + ui: { + SearchBar: jest.fn().mockImplementation((props) => ( + + )), + }, + }, + notifications: { toasts: { addWarning: jest.fn() } } as unknown as NotificationsStart, + }, + }); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders correctly', async () => { + render( + + ); + expect(await screen.findByTestId('querySubmitButton')).toBeInTheDocument(); + }); + + it('calls onQuerySubmit correctly', async () => { + const onQuerySubmitMock = jest.fn(); + + render( + + ); + + fireEvent.click(await screen.findByTestId('querySubmitButton')); + + await waitFor(() => { + expect(onQuerySubmitMock).toHaveBeenCalled(); + }); + }); + + it('calls onFiltersUpdated correctly', async () => { + const onFiltersUpdatedMock = jest.fn(); + const filters: Filter[] = [ + { + meta: { + negate: false, + alias: null, + disabled: false, + type: 'custom', + key: 'query', + }, + query: { bool: { filter: [{ term: { 'kibana.alert.rule.consumer': 'stackAlerts' } }] } }, + $state: { store: FilterStateStore.APP_STATE }, + }, + ]; + + mockUseKibana.mockReturnValue({ + services: { + data: mockDataPlugin, + unifiedSearch: { + ui: { + SearchBar: jest.fn().mockImplementation((props) => ( + + )), + }, + }, + notifications: { toasts: { addWarning: jest.fn() } } as unknown as NotificationsStart, + }, + }); + + render( + + ); + + fireEvent.click(await screen.findByTestId('filtersSubmitButton')); + + await waitFor(() => { + expect(onFiltersUpdatedMock).toHaveBeenCalledWith(filters); + expect(mockDataPlugin.query.filterManager.setFilters).toHaveBeenCalledWith(filters); + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx index 3896e5d0e938a7..5e2880e65231c7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_search_bar/alerts_search_bar.tsx @@ -53,6 +53,7 @@ export function AlertsSearchBar({ unifiedSearch: { ui: { SearchBar }, }, + data: dataService, } = useKibana().services; const [queryLanguage, setQueryLanguage] = useState('kuery'); @@ -183,7 +184,11 @@ export function AlertsSearchBar({ displayStyle="inPage" showFilterBar={showFilterBar} onQuerySubmit={onSearchQuerySubmit} - onFiltersUpdated={onFiltersUpdated} + onFiltersUpdated={(newFilters) => { + const mappedFilters = structuredClone(newFilters); + dataService.query.filterManager.setFilters(mappedFilters); + onFiltersUpdated?.(mappedFilters); + }} onRefresh={onRefresh} showDatePicker={showDatePicker} showQueryInput={true} diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts index bf4a81ea4772d0..3c39bd235bf97b 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts @@ -25,6 +25,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const toasts = getService('toasts'); const esClient = getService('es'); const apmSynthtraceKibanaClient = getService('apmSynthtraceKibanaClient'); + const filterBar = getService('filterBar'); + const esArchiver = getService('esArchiver'); async function getAlertsByName(name: string) { const { @@ -95,6 +97,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('create alert', function () { let apmSynthtraceEsClient: ApmSynthtraceEsClient; before(async () => { + await esArchiver.load( + 'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword' + ); const version = (await apmSynthtraceKibanaClient.installApmPackage()).version; apmSynthtraceEsClient = await getApmSynthtraceEsClient({ client: esClient, @@ -130,7 +135,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { return Promise.all([apmSynthtraceEsClient.index(events)]); }); - after(() => apmSynthtraceEsClient.clean()); + after(async () => { + await apmSynthtraceEsClient.clean(); + await esArchiver.unload( + 'test/api_integration/fixtures/es_archiver/index_patterns/constant_keyword' + ); + }); beforeEach(async () => { await pageObjects.common.navigateToApp('triggersActions'); @@ -344,6 +354,57 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await deleteAlerts(alertsToDelete.map((alertItem: { id: string }) => alertItem.id)); }); + it('should create an alert with DSL filter for conditional action', async () => { + const alertName = generateUniqueKey(); + await rules.common.defineIndexThresholdAlert(alertName); + + // filterKuery validation + await testSubjects.setValue('filterKuery', 'group:'); + const filterKueryInput = await testSubjects.find('filterKuery'); + expect(await filterKueryInput.elementHasClass('euiFieldSearch-isInvalid')).to.eql(true); + await testSubjects.setValue('filterKuery', 'group: group-0'); + expect(await filterKueryInput.elementHasClass('euiFieldSearch-isInvalid')).to.eql(false); + + await testSubjects.click('.slack-alerting-ActionTypeSelectOption'); + await testSubjects.click('addNewActionConnectorButton-.slack'); + const slackConnectorName = generateUniqueKey(); + await testSubjects.setValue('nameInput', slackConnectorName); + await testSubjects.setValue('slackWebhookUrlInput', 'https://test.com'); + await find.clickByCssSelector('[data-test-subj="saveActionButtonModal"]:not(disabled)'); + const createdConnectorToastTitle = await toasts.getTitleAndDismiss(); + expect(createdConnectorToastTitle).to.eql(`Created '${slackConnectorName}'`); + await testSubjects.click('notifyWhenSelect'); + await testSubjects.click('onThrottleInterval'); + await testSubjects.setValue('throttleInput', '10'); + + await testSubjects.click('alertsFilterQueryToggle'); + + await pageObjects.header.waitUntilLoadingHasFinished(); + + const filter = `{ + "bool": { + "filter": [{ "term": { "kibana.alert.rule.consumer": "*" } }] + } + }`; + await filterBar.addDslFilter(filter, true); + + await testSubjects.click('saveRuleButton'); + + const toastTitle = await toasts.getTitleAndDismiss(); + expect(toastTitle).to.eql(`Created rule "${alertName}"`); + + await testSubjects.click('editActionHoverButton'); + await pageObjects.header.waitUntilLoadingHasFinished(); + + await testSubjects.scrollIntoView('globalQueryBar'); + + await filterBar.hasFilter('query', filter, true); + + // clean up created alert + const alertsToDelete = await getAlertsByName(alertName); + await deleteAlerts(alertsToDelete.map((alertItem: { id: string }) => alertItem.id)); + }); + it('should create an alert with actions in multiple groups', async () => { const alertName = generateUniqueKey(); await defineAlwaysFiringAlert(alertName); @@ -542,5 +603,34 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await deleteConnectorByName('webhook-test'); }); + + it('should add filter', async () => { + const ruleName = generateUniqueKey(); + await defineAlwaysFiringAlert(ruleName); + + await testSubjects.click('saveRuleButton'); + await testSubjects.existOrFail('confirmRuleSaveModal'); + await testSubjects.click('confirmRuleSaveModal > confirmModalConfirmButton'); + await testSubjects.missingOrFail('confirmRuleSaveModal'); + + const toastTitle = await toasts.getTitleAndDismiss(); + expect(toastTitle).to.eql(`Created rule "${ruleName}"`); + + await testSubjects.click('triggersActionsAlerts'); + + const filter = `{ + "bool": { + "filter": [{ "term": { "kibana.alert.rule.name": "${ruleName}" } }] + } + }`; + + await filterBar.addDslFilter(filter, true); + + await filterBar.hasFilter('query', filter, true); + + // clean up created alert + const alertsToDelete = await getAlertsByName(ruleName); + await deleteAlerts(alertsToDelete.map((alertItem: { id: string }) => alertItem.id)); + }); }); }; From 288d41d61ec2389b2e8856da75fd0f3107f9c484 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Sun, 13 Oct 2024 13:39:09 -0700 Subject: [PATCH 02/38] [Connectors][GenAI] Inference Service Kibana connector (#189027) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Resolves https://github.com/elastic/kibana/issues/188043 This PR adds new connector which is define integration with Elastic Inference Endpoint via [Inference APIs](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-apis.html) The lifecycle of the Inference Endpoint are managed by the connector registered handlers: - `preSaveHook` - [create](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html) new Inference Endpoint in the connector create mode (`isEdit === false`) and [delete](https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-inference-api.html)+[create](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html) in the connector edit mode (`isEdit === true`) - `postSaveHook` - check if the connector SO was created/updated and if not removes Inference Endpoint from preSaveHook - `postDeleteHook` - [delete](https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-inference-api.html) Inference Endpoint if connector was deleted. In the Kibana Stack Management Connectors, its represented with the new card (Technical preview badge): Screenshot 2024-09-27 at 2 11 12 PM To simplify the future integration with AI Assistants, the Connector consists from the two main UI parts: provider selector and required provider settings, which will be always displayed Screenshot 2024-10-07 at 7 59 09 AM and Additional options, which contains optional provider settings and Task Type configuration: Screenshot 2024-10-07 at 8 00 15 AM subActions corresponds to the different taskTypes Inference API supports. Each of the task type has its own Inference Perform params. Currently added: - completion & completionStream - rerank - text_embedding - sparse_embedding Follow up work: 1. Collapse/expand Additional options, when the connector flyout/modal has AI Assistant as a context (path through the extending context implementation on the connector framework level) 2. Add support for additional params for Completion subAction to be able to path functions 3. Add support for tokens usage Dashboard, when inference API will include the used tokens count in the response 4. Add functionality and UX for migration from existing specific AI connectors to the Inference connector with proper provider and completion task 5. Integrate Connector with the AI Assistants --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: István Zoltán Szabó Co-authored-by: Liam Thompson <32779855+leemthompo@users.noreply.github.com> Co-authored-by: Steph Milovic --- .github/CODEOWNERS | 5 + docs/management/action-types.asciidoc | 4 + .../action-types/inference.asciidoc | 126 ++ .../images/inference-completion-params.png | Bin 0 -> 115726 bytes .../connectors/images/inference-connector.png | Bin 0 -> 224091 bytes docs/management/connectors/index.asciidoc | 1 + docs/settings/alert-action-settings.asciidoc | 1 + ...connector_types_generativeai_response.yaml | 11 + .../use_load_action_types/index.tsx | 12 +- .../common/connector_feature_config.ts | 2 +- .../components/schemas/connector_types.yaml | 1 + .../components/schemas/inference_config.yaml | 23 + .../components/schemas/inference_secrets.yaml | 9 + .../connector_types.test.ts.snap | 347 ++++++ .../mocks/connector_types.ts | 1 + .../common/experimental_features.ts | 1 + .../common/inference/constants.ts | 43 + .../common/inference/schema.ts | 68 ++ .../common/inference/types.ts | 38 + .../public/connector_types/index.ts | 4 + .../inference/additional_options_fields.tsx | 360 ++++++ .../inference/connector.test.tsx | 353 ++++++ .../connector_types/inference/connector.tsx | 445 +++++++ .../connector_types/inference/constants.tsx | 37 + .../inference/get_task_types.test.ts | 50 + .../inference/get_task_types.ts | 606 ++++++++++ .../connector_types/inference/helpers.ts | 85 ++ .../inference/hidden_fields.tsx | 88 ++ .../public/connector_types/inference/index.ts | 8 + .../inference/inference.test.tsx | 142 +++ .../connector_types/inference/inference.tsx | 92 ++ .../connector_types/inference/params.test.tsx | 167 +++ .../connector_types/inference/params.tsx | 238 ++++ .../providers/assets/images/alibaba_cloud.svg | 3 + .../assets/images/amazon_bedrock.svg | 11 + .../providers/assets/images/anthropic.svg | 3 + .../assets/images/azure_ai_studio.svg | 44 + .../providers/assets/images/azure_open_ai.svg | 9 + .../providers/assets/images/cohere.svg | 9 + .../providers/assets/images/elastic.svg | 16 + .../assets/images/google_ai_studio.svg | 6 + .../providers/assets/images/hugging_face.svg | 10 + .../providers/assets/images/ibm_watsonx.svg | 3 + .../providers/assets/images/mistral.svg | 34 + .../providers/assets/images/open_ai.svg | 3 + .../providers/get_providers.test.tsx | 53 + .../inference/providers/get_providers.ts | 1054 +++++++++++++++++ .../service_provider.test.tsx | 42 + .../service_provider.tsx | 126 ++ .../providers/selectable/index.test.tsx | 60 + .../inference/providers/selectable/index.tsx | 136 +++ .../connector_types/inference/translations.ts | 107 ++ .../public/connector_types/inference/types.ts | 41 + .../connector_configuration_field.tsx | 360 ++++++ .../connector_configuration_form_items.tsx | 136 +++ .../connector_configuration_utils.ts | 55 + .../lib/dynamic_config/types.ts | 71 ++ .../server/connector_types/index.ts | 4 + .../connector_types/inference/index.test.ts | 85 ++ .../server/connector_types/inference/index.ts | 182 +++ .../inference/inference.test.ts | 310 +++++ .../connector_types/inference/inference.ts | 232 ++++ .../lib/gen_ai/create_gen_ai_dashboard.ts | 2 +- .../lib/gen_ai/gen_ai_dashboard.ts | 12 +- .../lib/unflatten_object.test.ts | 45 + .../connector_types/lib/unflatten_object.ts | 16 + .../stack_connectors/server/plugin.test.ts | 2 +- x-pack/plugins/stack_connectors/tsconfig.json | 2 + .../task_cost_check.test.ts.snap | 4 + .../create_connector_flyout/header.tsx | 18 +- .../encrypted_fields_callout.test.tsx | 16 +- .../encrypted_fields_callout.tsx | 4 +- .../server/inference_simulation.ts | 53 + .../actions/connector_types/inference.ts | 518 ++++++++ .../check_registered_connector_types.ts | 1 + .../check_registered_task_types.ts | 1 + 76 files changed, 7251 insertions(+), 16 deletions(-) create mode 100644 docs/management/connectors/action-types/inference.asciidoc create mode 100644 docs/management/connectors/images/inference-completion-params.png create mode 100644 docs/management/connectors/images/inference-connector.png create mode 100644 x-pack/plugins/actions/docs/openapi/components/schemas/inference_config.yaml create mode 100644 x-pack/plugins/actions/docs/openapi/components/schemas/inference_secrets.yaml create mode 100644 x-pack/plugins/stack_connectors/common/inference/constants.ts create mode 100644 x-pack/plugins/stack_connectors/common/inference/schema.ts create mode 100644 x-pack/plugins/stack_connectors/common/inference/types.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/constants.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.test.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/index.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/inference.test.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/inference.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/params.test.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/params.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/alibaba_cloud.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/amazon_bedrock.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/anthropic.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/azure_ai_studio.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/azure_open_ai.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/cohere.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/elastic.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/google_ai_studio.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/hugging_face.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/ibm_watsonx.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/mistral.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/open_ai.svg create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.test.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.test.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.test.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/translations.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts create mode 100644 x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/types.ts create mode 100644 x-pack/plugins/stack_connectors/server/connector_types/inference/index.test.ts create mode 100644 x-pack/plugins/stack_connectors/server/connector_types/inference/index.ts create mode 100644 x-pack/plugins/stack_connectors/server/connector_types/inference/inference.test.ts create mode 100644 x-pack/plugins/stack_connectors/server/connector_types/inference/inference.ts create mode 100644 x-pack/plugins/stack_connectors/server/connector_types/lib/unflatten_object.test.ts create mode 100644 x-pack/plugins/stack_connectors/server/connector_types/lib/unflatten_object.ts create mode 100644 x-pack/test/alerting_api_integration/common/plugins/actions_simulators/server/inference_simulation.ts create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/inference.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fdd0d98d8d53b4..a8cc39b7b072b9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1681,6 +1681,11 @@ x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/ /x-pack/plugins/stack_connectors/server/connector_types/gemini @elastic/security-generative-ai @elastic/obs-ai-assistant @elastic/appex-ai-infra /x-pack/plugins/stack_connectors/common/gemini @elastic/security-generative-ai @elastic/obs-ai-assistant @elastic/appex-ai-infra +# Inference API +/x-pack/plugins/stack_connectors/public/connector_types/inference @elastic/appex-ai-infra @elastic/security-generative-ai @elastic/obs-ai-assistant +/x-pack/plugins/stack_connectors/server/connector_types/inference @elastic/appex-ai-infra @elastic/security-generative-ai @elastic/obs-ai-assistant +/x-pack/plugins/stack_connectors/common/inference @elastic/appex-ai-infra @elastic/security-generative-ai @elastic/obs-ai-assistant + ## Defend Workflows owner connectors /x-pack/plugins/stack_connectors/public/connector_types/sentinelone @elastic/security-defend-workflows /x-pack/plugins/stack_connectors/server/connector_types/sentinelone @elastic/security-defend-workflows diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index 30bf4f791e5d81..361892e430afde 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -28,6 +28,10 @@ a| <> | Send a request to {gemini}. +a| <> + +| Send a request to {inference}. + a| <> | Send email from your server. diff --git a/docs/management/connectors/action-types/inference.asciidoc b/docs/management/connectors/action-types/inference.asciidoc new file mode 100644 index 00000000000000..8c7f2840f9c5c4 --- /dev/null +++ b/docs/management/connectors/action-types/inference.asciidoc @@ -0,0 +1,126 @@ +[[inference-action-type]] +== {infer-cap} connector and action +++++ +{inference} +++++ +:frontmatter-description: Add a connector that can send requests to {inference}. +:frontmatter-tags-products: [kibana] +:frontmatter-tags-content-type: [how-to] +:frontmatter-tags-user-goals: [configure] + + +The {infer} connector uses the {es} client to send requests to an {infer} service. The connector uses the <> to send the request. + +[float] +[[define-inference-ui]] +=== Create connectors in {kib} + +You can create connectors in *{stack-manage-app} > {connectors-ui}*. For example: + +[role="screenshot"] +image::management/connectors/images/inference-connector.png[{inference} connector] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. + +[float] +[[inference-connector-configuration]] +==== Connector configuration + +{infer-cap} connectors have the following configuration properties: + +Name:: The name of the connector. +Service:: The supported {infer} service provider. +Task type:: The {infer} task type, it depends on the selected service. +Inference ID:: The unique identifier of the {infer} endpoint. +Provider configuration:: Settings for service configuration. +Provider secrets:: Configuration for authentication. +Task type configuration:: Settings for task type configuration. + +[float] +[[inference-action-configuration]] +=== Test connectors + +You can test connectors using the <> or +while creating or editing the connector in {kib}. For example: + +[role="screenshot"] +image::management/connectors/images/inference-completion-params.png[{infer} params test] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. +[float] +[[inference-connector-actions]] +=== {infer-cap} connector actions + +The {infer} actions have the following configuration properties. Properties depend on the selected task type. + +[float] +[[inference-connector-perform-completion]] +==== Completion + +The following example performs a completion task on the example question. +Input:: +The text on which you want to perform the {infer} task. For example: ++ +[source,text] +-- +{ + input: 'What is Elastic?' +} +-- + +[float] +[[inference-connector-perform-text-embedding]] +==== Text embedding + +The following example performs a text embedding task. +Input:: +The text on which you want to perform the {infer} task. For example: ++ +[source,text] +-- +{ + input: 'The sky above the port was the color of television tuned to a dead channel.', + task_settings: { + input_type: 'ingest' + } +} +-- +Input type:: +An optional string that overwrites the connector's default model. + +[float] +[[inference-connector-perform-rerank]] +==== Reranking + +The following example performs a reranking task on the example input. +Input:: +The text on which you want to perform the {infer} task. Should be a string array. For example: ++ +[source,text] +-- +{ + input: ['luke', 'like', 'leia', 'chewy', 'r2d2', 'star', 'wars'], + query: 'star wars main character' +} +-- +Query:: +The search query text. + +[float] +[[inference-connector-perform-sparse-embedding]] +==== Sparse embedding + +The following example performs a sparse embedding task on the example sentence. +Input:: +The text on which you want to perform the {infer} task. For example: ++ +[source,text] +-- +{ + input: 'The sky above the port was the color of television tuned to a dead channel.' +} +-- + +[float] +[[inference-connector-networking-configuration]] +=== Connector networking configuration + +Use the <> to customize connector networking configurations, such as proxies, certificates, or TLS settings. You can apply these settings to all your connectors or use `xpack.actions.customHostSettings` to set per-host configurations. diff --git a/docs/management/connectors/images/inference-completion-params.png b/docs/management/connectors/images/inference-completion-params.png new file mode 100644 index 0000000000000000000000000000000000000000..686ee9771c2f56a80fa2175aeecc7eb4ef82f60f GIT binary patch literal 115726 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⁣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%0Drrt@bqgLQ3hbRfTH32xq3%`FXvGY~vgqP_4@^wo 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Ν)$?N4mYJx!0D?T4z2Lpf5{&y=|CZ+x^7RvdLshX+p`J& zV|t)`7Kw$pZ937Cw@rAD%A17E_oy{K1P&#|^b6B`@l>3{5Qid@S>O_PtN3n~Q0DN9 z6~vuyKmlNT*#FjI$c#9g4FuuOdI@>@1%3^S%8(lu*!2eZ7tq4VaOV6E7IJw;{A1BK zAl_d3&v;uF+$0Agj`W9nHNIepvk=;Mp65gRrg|-Do**k3FTNa^d$UcMBrqEs{Rv$L zw)v-S$S*a(+z89`i+ibh2~)@Jur*A1f;Ydfzw6>0(>qe?5-aqR&V-kaJJCG(%c|ED!+w*Y!r0!asqF z!`J$*69^9M>+J}$cPk4-?JcF?A8Ezs)UIG|^|r!bw3d_5^~8iMCDf({lX`w}e!&QD z$pSPx$8Ia;B|Hnm&EMebuJ?iG@NgCAS5Osq&uGCVMXA=F#~YR>mkia3so-|bGgUU? z67v!kAg`4~xW|QRu^D5~@H|+Ucc=m~f*EEpcr{msNomM4ANaYZ<&n5OiOG44*%ea#?qQqH9 zwF*5`!`AhWx)0dURaw0am4eoJ{5p5^&iz%`s&>mgo+eY@RF6+AE`ciK35BrKU=!}HazX;Zkse`<_{dJC5C?0tT9_{@E-$+r2k!%A3H&#vLqG98u4 zDe$hl)SfN!ZmZt#v%!tEgmIdjwJ2T9DODOK@ulJ3KuDb;mq)|Q3e2PGVU>kXh-5!_ zw!aI)D8Xvp$ZG`{8bAXgQ;T=9!=!zSA*#D0$PtZ1HKdyQ+_)b1-8+}NNzZG)AIxk( zsc~Y0lkdk7;3!rQ#0pmA*RJvQg&tTzsW14a=m0JFdUf9mOHD1@6~YUs@gB6|YGYIu z$?}GDq5!1%dYqRi*M<~5bR~QREu(;}JMPgWs@#W(%zson9uSu%Ei*hs0vceO^X80b z$A(s<<|Qn-J8zsKCCxXn;cH_pIPKd!*i*-5gz>`dC`^5Ogp1r5dDyO-&kl1Ybjn(y z-lhsP&GvUnbQ?c(nO5s|;RpbMmsnivL91QY#M$Bf3O=sjRvc;9uN^;w%H$MsL) z8Gu|-GSsydQr)tuann4x(Ba0bdiGKUV`aAy{;klRin8}mpPE8A71NAmw?8Ma=r)K@ z(bsgO;Y~s3=$Op+*XeKAt>0}N+!yyxx+q59&g$_ts%pF#6JgPL7pgR-#gn9q+V@5) z+rHMVw`L6{`K~OujSKJE7kyWBp~wT$Yp}Dw@_>f!#07QLclY=&zNCq*HPUlFzY+Q2 z*_o+P-n*+l4?#w)uG;YQ)#WVqbm_OHgRn=6itCDRr(a`_kmW0QNG+~fjNeGcfu?D*)kWN1 zi#$RVRyQANiUm>qnpK$ZTh*ZCy1VbBfE0&^gCSo}w#R5yP*5UQPDsT;YKK*=nRjLx zDHxXTrSpuGq=9w2_Ot=HKoVR8O%6M5{YuOmqvb&3i-;fTaQvrrGTvRMxbDYz=PKiQ z?X)X7{zj;}0~}l*v@9;X%sBa6@W!nhXA?E9_s6-Zb{`u}I-s*WKj*XId{<#w$C*DI z!HRQ!QEX%@B|aBva^fIJWw3nFVpsNkp-Oo9nt#=dp-VeZ!E}ibyjrHiX}T&V5vTRQ z*_rGT9Ah9>mnAIU{xen+j|6}%%d~N~fz(ALGS1t1_`}*F4wL`%e1ykl!GWQY?*|XC z0cg^+(P-)~S47R`Jz$x!FETuJn1#>>^!cURm*ALplON$!o&ss4$Q zv*}8evD3xgIj^m3J(muI4FBQ_;4xngimiY>)Qj#nS=S6X=ho!8Tj_?pa2|53WB4uV z8TEPKQ{59AN&bTkf!sUR@kyL-qWyn~mGcE8)>z=X3B<~s{j-BTd>{NHU!?xTRoAfD zMfPI*&N8+C1=IW|$QZ;3SV@(|SMy~*d&a*IpCJu002dLNc7whrF>q;D1^_b1+h@Bc z88Hw9KnHEqpKpr(^$kCN8odk5>vFb4YRjJN({&WE><#c=7uA0@Cx1}^%3=U21@5lF z_3((F5K({YeEir21qL#I8$|rP&VS9~LkYgK{nat40Y`;?9`;`ZyABdC8|(Db+3df4 z!%twh&-#F@tgD3T+L=87dWX)w!veE0OOe0B-gBK&OaP9Ue$cd%+g>kl7P*URB{{xh z?YT}W1;Es%Y_|H0?)3uycccA%cK#o3w6Apa$8-0r>K^6nGAqk-@N7JqMehdMlo7%( z6F+I2hAk?=Ht6NQ-Lx7a$1MPOZ3&2xz$a~XC2I2;bmClpoGS7+SO1m49<9hTvsi!f zWY19m1RjG@XI|+^A`E?zMVgY%xsFXw>JfkVHctcBdEyKecIKWFpo#_@^&%dG&eT9^ zpb8}xH^Rmwct>+-#z*tV8Ter z$zvgM6s)_|MKzTRa zI$%J&CSdIm)bqQFSF6H*R-0nLM8Giyz+Z(U|D4rPe$^7oR81-t*re*VYLAP+HXJg8Oez6|R_?yY&E1qZS3lXg zCuw{-6)NyH-m^r}pMdQ>T3m)!aQ1y%x)cqNJ|9JJqK1D|6~q(+Trxuv0HCS3#iJre z|7e&led$uq`fRrV1|RJiU7h0gK%bUaTw>Q9U7cpySUeJb)wo1}*bX*6@-W9z_5s-Y z#DdOV+5lTafOmddNy+DsXA~>fNl5KpVf+DKy@BA{8261_25bQ|AuK z$p!Ta9RTg7`!WUoHp31Mo^meIPm)cD_9o#P=9zO2!M#2CHjmOt10E^M0IoVJh~dJ< zmS}c!X7;UVBn%!^W8VZbcHX?d>O5#CZJb*L@}a~*9A~ruYTJGxwcr;D_48`d70}~C zJFI<*c25kfP`(8zh~~DMpdJ^}U|m)U99LjA^C>2n;fbas{Ra`EX%-@+2yajwU%A0p zs_b*`khnF`-_9%!tjvY1cSxNCw&C(zX#wd=O4ym9d1?+DKmG)0f`TF6F8?br=IY0IJ9h~+hr#(Ukq_uO&g ztQ7I;Y2(0E|o^t$eHm9fEE&pB@2oIBWW_>C^;RF}W{ zy>i8+?0$wK^VOga~FF}-87qji!-C`C}*5z zw6rM#7gd(&rONfFW6i7t^&mPWGg-=jP|xcgM5V$3&@9ij&z!um(fS38H^V6Ggr;8HGhV$4JqIY;@E1((e|V^fXxm)lY1z9qW)ktAV1zFwVX2IYXk zqUpN7`2v?UbjcK3sdH&ipxYeM7G>#vLD@x=z;$P|C&$FiWRyn`WohL(C~+G+e;^@6 zrJ9CML)=jH=Vdgb)#(daJAT>!$!Ya$9Fv{r{CDxy=aJ*5Xv;LJH33_^4LY*?PDd!M zzMWQFSAX&hmsn+!plUCjf@p_~*$Wg|OzkVO%&E}Zb`IW1>bgrZeiap0?QU^ ztwcs_)2*x;ei+mJaRn5c|rtsy<|Nj z95`@rm1c5Zgw}dmb%QRpeTRV;QdlEdmr3g3Z6IwJ=kEAR8{Ok~M8NYM#C9zJff;@m zo=9C=%uCZGBfPpz$M57iCF+*UC+sq`;A4L(=-=@X3*15XD^{1-zu~{6GX$+>+$QJk zK%)G}Q|bh7(lX0uD?^!10|;5k7`T&c!}%3*5mJl6bOJA{T#v4KG}~1*kkx*>n@)jZ zJOyiv8z*6@I#!KAen~>r{M+>!hNz$i!hKQd`md-{2aGbH$qf!B$mS!8jO9KRcIFiU zi+eurH>x--Ybo77nrvIbeCO|CLy3iK$24e$y8iOLd6RAc#Z)5q`0F>P#8QqpFF2;A zR1Bm(r93RhmqO@|r{-l+kxjWbl7t54)i?evPwXM-a06=u>RfvTCELRhAvf+EGslyw zI#?0~R`(vZ0Hc{=s)HEBdLkTNCuBOCKuWB_!5qJbFA`NZc8Z)mR!W`5Ks2@3T*Mj^ z@#nfLdU+RdMERlydUp}$(fow~6Ryt7od151AQXKS{)NXdMO-(YuI$+)f6X!%Mp@dp zxf!U7?(7*gFoR9}eh}1{8X#BVli;ED$qPF{3woaGPnc1c?!aWt^Ri%vd^yg6j&a>? z*dD<rNmBx@~Sb;f;@ z<=&rNeBjpa{m~QDEMfNU;a=0giXtvX7J{wW-UM3d1S*e|_e`07ApFELRb{idR_d+B zWBa&5>fau)$bbjvnf2uezCGZUhFG)DPnt6VdUbAd-ySRiyg`jZo*>XBgFJAR>xc99 zVTsh}s{yOw188~TM^Zw#26#0pt=lk*C2-EV(m^k&H0@Z2$>rr;=4ASZXC5d2T~ZKI z^1s;o^LQxVz6~62Ulb}K32g`=duS}FWS2F|kbNiHOpK)xDuoEy_nonCGloi%eHn}~ zm}D7@G1eK3<$HOa`+h#3`?<^W`~CU-zV3f|&1+0E*L9xfb)Luj{XUN4ExZV@t!Pgo z+xuh3yArFO-7Z!lqy+e%kntCvtnga1+r5LS5rb?&1AO>j2sK|lf0_nxV9-@SjxZ$n zv%wG{t5rU`FgHy<7mh*;qF1Ij8UcSB$gk^M>pC?56d&mVpj}(W(3Vsk+rX(W%tsLc zI~#E~YRCJ|JRk?gi`hjh#61v@Xw1E@$9u&5tJ9sHwRU08zJWw3a^SUo(NkWib)Ci& zT3>Ef75~(_Aw-{H9*b5mgy0xy^d)azXW19;$ zEH*p|0MA`5bdK_N&k3oRJ!6M2m8B>%=B>@;_L42%fPV|){t3{XK`+kS(@Enu+Gw}A zq+2>uC3SpbAbm#*ZI@lh`O@H`es+{D4??{gYM~)(`qAgRiXYeWl7LY;l6CiU2KpNKYO)~PjEATBhMdC@{Tq8`Anldp zTZj0K(zs(3jP1qhd5&cOhOALwzgIdlP4rS@bI=cSOV>|536}eA9zB4jzn&D=Yt;~5 zATV@3Rv_DY>yE>u*@>6Iy>`Qkvvw%}3rIWH<=pNz1$PJ^Ai4|ao%PLXT*1SO-4C)_ zufDsk+bBNwXPIT`+K~X0(jUiz#%NFM`;s-cc9a`;65W}$H_v!ol-vr$eygTTbc|Dcr;{Z3wSlQpC6eUBi4yanSX-qpxjk2 zzdwDghdFNaCAr2nO)V38_qkF@f~+dHNKf`^h%vrs!@u_fWM)WuSQ4;KoIgQAZJ9gD z`loE7#T&tQo@XHVTMu2iaw6-e&W$V2Ssr-tp}<3N(`>q2A5(7K#El&S&tY)n(+_o5 zAA9Nq|LmOrNj`|t1*JTTv|lJ`JeHziCRUL9!DcROdQQh&Pn>M@6s^ItbjN>nDbgO* zXHf8NV>8hnJCBt|ase$iDj*}wh(}PPPX$GIfG`IU^lzx+l z`|k|L=Ru~P6)Qki5^3MoKRXMlpP@F(1*g5dFJJ&)Cf@DI$4zME7Sh_B$eWUPZ}A^N zY&MGA0;9f3BgE%a0aJ;?5BKs`H4@e{gjovPFa!N}WiNWaz}NT(p3vZPFQ3V(0n8+Z zk!s5g_wQ~7Reb?<<;qf5TjbV%UIT(iGi1dQY5Jwo^nE$_>Kbl~G| zGgN1%hz;uiswZV0jUJidaKewD#pkLC*A4ACObDGhyXL+iEVd_UGuZ7D*jjcfscS=c zE!Yd1REe;DE}L*1EQO8*jG78*6+TC0j_m)r1FZ zSgTRk@CG5-s#|U)>ph^S6>40*XzsF8Qjs}VBY{8zjHp2XQX|P@wk*}BnA<+`xd8ZF zT*?&vmPOFADU`mrVc{iu==~k~${meOY zEq}g88Lu>wH=}n)ramx0NL7Y7Kv{)q^>OT7J_xJYB{q!3r3N}qwa3+%uuF=@dGrVu z0`?t=Qh^QRICwUoS9SyrHT5}pm(A0%+wUs2A!0ci)qp^L*vQWOiBNFPUJO)VSYmsv zakC9Dd$DI0;IzNicwvFKyx+Ne)1=|+aCKnEUgnZPwla#tc;hlF*C)5=fFwtD$t@=; z9u^uSpO!@K0ip?`#vSrHwMw(VL0J#f&!ZJ@YaX!np2PRY7swv1`u0nHaX0LkX^3zk zY{$p|Wz39g0qH_kT(Q*XoX4r09RBRX3j>(rkF$dqD~)+M2oo2$7&$DE3y4C{NKW0J z%qZBu+G(_-?IRZBmdgc@+J2>^l3j}zkZ9A$F5lIFFEyUu9e8PWD`bYvA6&X?j{3#D z#TXiCp`j$1s^`Mh$_zc~O^4h|Rp4V+vrrbwj7n_+? zFZ6l?Ie~!Q;VLJzxDM~TyL}@n-5|Ww#oo-<1*gfsvX~1Zu&-4KI zFweUgojFC4yj-a61;tqBdvSxo8Inq=AMa{8ko%>oSRZ(~Xwod?ed#qV7>f6RSb;Y9>njocb;&z_%6e zBrppDDx-@78;cX;0s6mQ3khzmN1t_oj?yCC2)kG$;|%}XnurO;>(?vK+zq>Nl||vp z+b1QDwa@4snFo&xL$ayiUZrV|OT*|DOJBTxe1=((Plx&Dll1J~bBBBgY)Ld%A*qpI z=M%5UNmdJ^qgd={_X4;Z%OzImvIcYh*vd5+oZx|5r4053gJBG9YvJ&kmcTQPN;o#! zvI*tcg9sn){#aMSQm>T~S}~CC$2{+F14}+9_>nH^26K5u4S8yt8zHoCEmi+1KRhROF=TbQ+r;=ActDLu`!b0gSXrZ>Y_Mt}b^pt-0EA{F) zu%)GC-ojXb5z;mJdW@DR=#Ukq`Xn;tq=trQqb9#W(MNLia@Dn>cmF*6|M#Frsy$Is zE%0GTx-b_%1)d>+W0FjbIu=86at)5kzD=r0p~8H9L;Syk>z}!k=fMrRSve~4ec9YX z@NYuxTU|Y87Y2*K!pu?UTuN>=5|8Sa+=6+X^*ZX!!q^A-8Uzpe_~OsG@f%+cjit5A zzI$?3ZW1IT{t^!Qm9e~YU7@E+KAE2KM@x{j%STQiASJ3jRWPpDg_c7A?LAh%&`?cB zC#oN=?7j;NoPObsWcQbd*mK;LRQQLh^(=~pXXWZHUHnr3xi@^; zACI0uyj{jl4gE382K?d0)tf-)r&n4+q5soG?thQ@-idhL=HN?sh95*)@=t(w?-tTd zoC4Z>jYqeq|G9?(@NM^vfIeO~Qo-SWzHi{$fZ;v@KI-S;>EV&}xXkisJ#)gobr(9r zv|FNQU&0UFCYXp-SgYCQSligBKqw5Y*s{z6)pw)+o>cy;j`5H4e&?EY(KGiq?*)b2 z-Q9Kg<;+ySw=l1F0c+!7{pb`)%S|NwKS`#tG}D=W&ZiVp6-t`2mE1( z@o2|oXt_TP(hBUo^DbDO9A^hU!|He6J{>4Zqf<0gD92*4QL$wz)d?x4CvBLrtXtQg z8|Sd?H^x6dpBq9*=!#0(eli0uMSar#zPkEWF!J!_8M@aJ+#n@Mx3Q}#K=yK|mCoMK zVP9kB8&40Cu78Y5;0GA4QCS`*kCua8!Vf%NOEx9y_Ni;Z%U37qK1cKHvwYI*Pt4C_ zs~++5aI^f7MV>$$s@2r#6YY2wZKQLBkMH@Q{h|E^=!VQ8b{(DTazdI>=Yq%s_j_Wx ziQnoCH3o}Jgx$G+Wvj>cZ8*Ol*tb5D!CmTrwrw4b? z78xJU=imVRu3YBPn7OlFQ?&k`KkZ-t^(;LCo~7rSH-Y(&6#w_C>))2?`uWQ<%O{zO zFDe>}Uy0qfvh;=SjMvdgl^*6Z*E@gr0RPW>a=@y3b@Oz2ByErsx20+bO{JD)yb;?}I;e zpQp9+>Qy=(0r$Ro9R9nX{pWi}1QdFhBYFQbocqkte}?lvtMfk(`~N=m|FMJo|Gjq_ zoKeJtbI4AX1Y%U`ULD~x5|tYkD!sJNA#>(--jQn8&X23_PUjm`ss3yMpTS1plW)da zy~VdF_6y_%ZQGG#-b4uS*|dFRu#|H6V{o}wH@YEuC{fZSwxg)nq#M8O1$u*fHLA9}sfj#bGrCn1_vXM`V=y zl8#(i(YJ5k&i^BBi1Ch8^d3u0r|^&t1=w?}KwCfxmtee&deQkfE^!?}Abosf$ zBbHmBKufDIT)0AZ=MU)7B~AEKxtH*#XLI($H3yF{oB&|L;a}oq`vTVkhsJ?Zdi<*T zzUbh@r8B^%j(>Bo-|sr@`KLY-iv4$UZcNh{u@W?ulojGy#k_v;ILc! z@`ElvRXB0Ri{ni0o8)`{J`aDJ^%JZB!ezC%_`?52WJ0Xao2heqQ9*ifj-B&IbHgnm z5s~KX1^22BJ9T?DbBJ(cR05C66cg0^P4@951z=roA(o-odY`aMCbSEDCONAIp{I}k zVo*lNbG&Z(^1Ig7C(gS76L9Euqgw1Xe@UaXqNeohGlzgF8>I92He*iPFYd;Nxeymp z`B|+y!a)A)b){oKvygZEBd?~OMUVm9rB^S!L>GBks>;yCX;JpcVsw-R94yE<3eD}mg9zE?>`ztU(u}@8O`Tb2 zRjh(tu3?(p@w&{#Nc$a#()I!-obKm{-1e4J`63}(GVg-L%eOzG_2w~Uh%Yvn*_x+mt?lJCTx9K!0a~(Q~PK>$2yok6$!%e2pz?<@Wfs z)Y5jPHO6!8xz_-|lrBB_H5}V?`Q}#2{P3Mm^4CUFDOJi@u%U*rXz;T z>+`5zc(tu|Rd4!TvwUE~@WF_F z%`BgQ;W6CWS6uks>v2WHESYf^Vr8l?uEwo~DojeQt!W0gL@<)7a29vKJ!=X$TsEXL z^Qx;*i9bUlud*ptOsoWQM}Hn;zm_4$=+>=4GwRmja1j=8O8nLw^HhVjvx;s=l|kat zWU2R;xMb!RYK?vu(U;l*8v$&Zxr&0pcf`7r)uU)T(86~ja z!uik*AF-a(t@z!d5BxcHT0y#6AKQgdpXJ0;mIi7>-TThZuFrsU)*|_ez)!Cc=wMOL({9QfCj@qZDSf4ZN2l7yWECofOLx!zhkH=R~8PX z-o<;%d%tczh?1T3J+nz!XPiuQBRovyj8Lt6Rh~iaX4~Cqsb)1X_6@mYH1X8IzS@vH zIicjT#=iPWPYmVkD%R9iCkk8e^YI@!GTS$927Bk)K)ZBnlFw)xBM;FDB^PR6;X8j| z1DoU2Ld!>Gk8$p%7a1YDLO$>gf((o$-L5Vzxo0S4t<8w3CzyNJfmn)7GVT>|bQNdM z9*lVsKEOP>1DpQD`Y8qG+cCT{mmnY<7p-}_OTR32xP7Lg+pg^`e&=+RcT<(2YU}!X z55)0 zCNZ_Y-)nXt@KvQK&2x|~cflK%ehg%uWMpcZLAl1U!4ZKGz%UE3)O2$BrVWB^t%CRL^E7~Otx+L(73)L%6fQ0O|b z$zJIGZM@ph){J_~Fiz{Y5H9I}VqElz8C|ZfVv|P25c+wZlK3+O99F(9tsyxJa1Tuv zd?l|(&xly{7tzh}w+ITJ2ruSS>y&LYNJyJJB-WoKSK5F49fE_St+2P5fQ2Z_ZM?j> zyC%ghdS~O-u8q#aWPP+}-_QQ& zEcIEPY7Y8*@t=3#6cGqoEv_W8MY-_?kp7+OKx@SST_d?yG$PY1!a{U~y!tGTtbxGz z0C`M`Fvr~TvRYTocs$TmVoo+A{pV*n4wurr_edv&!$;`PeGN-NK7_a?E?gj{dSsv) z`X5tGsODjElM7-`KF>@Bn4Ay|1eaqjSpZo(F74rseo)<2VTF(MImKckE8$1p z)|#wf6PwzBxkg(#ad03~`VHccL^bM02O4T7RiPf4#Yk8i-+9CHQT-OrLjySNC{-xgczCH_p6aO#vcvM50ZfQGS8k(a4Ji%x|3 zMMRP-MX^M`YB0@=%}N8o9L&7W+QY)m|6`{Ukd zNC-`~njE>1%=Pgtb6dONG#5jM_--9cVd>Ka>D?qWs&k0!OGe|%;U($n`b=k1Yyuj6 z;hCdaRR>E-ICC3hwSpE*Tx-8G2+R?NsyO5c%V8yr6&BHEL|^(jd7d7Z(Pt!m$$JBk zuVD+RW1*EZw{ii9H$~$LD5svGPtg>yh-HCx_x?mR%&HWT!?v~~1|-iDoySfZ6+IS# z6~T*Trgw!UBBXVMQ1+P1eaUK49r@G*Ob_jZ*vJADziS_4gz>}{XA(T1RBcQwt#~+D zDsb3)!EKi-r)hoKn-SnZ{1&8sK6&1@CtJy!gf4%IojWu-hFIg1cHx?fT^U?&g~&E; zZ6+p5?)u7OAgO_~ok_=WYjmAXcyEO+T`RLPG#{S>B$+oQfUnZD@tw~&z3eVt!inT3 zcE_?SZCndSo>p}4@{d^k;ZtkHWzup)2(n|<^nhTGgU?8g%E-8_Obbdg@oKfYyC8fQ z(5p{MNeQLjJKKzE?ej-Nd=9t}IyVF0$tjc#Ou7uhti|;(X{R=R%*tz}Bgg>>pB)&t zB`t4IU|hp8V@CsOX2HshM7g;Q5yobvu!dAI;g&mIRcM~|^7a7_;$Vk%a*1t#8zGdd zVz%sDL3Dh)L$U7~7h3}1ba}PQsZKG_jiRtwm$->)yD`P{#$&M0c}Y7#si!rFtfS_m zZY5=GeeGQ)NQgfiG4^Kf3G&_k6iQ8>8}(%@Bh|49H!N3!INfNj^6C+13|->1K;6E{ z@ulrTxLSt_7egw(Qw(*IR6iaeZ%3R7=vZIIU8-7QacMsioo>X&1`vjP>4^aji982R z2tqN@!;bFgX!!(|g3Lw@u-3Mz2syBPMV(PfR$z;oFNSd&+Q98>wHsA;ja9oCGSD(x z@@ZLw@61e~AwAGe^#`)^iow82P;($BeA<1rsG?69?&>AFh3~UberH6z@5pq*x zK@0fZ$IJy*hfHPNRy|8Xrtc%dl}>@oM^LS=WYg;c$hlpZ(^dB5W`wzKJ$zjnQcNV6 z&Y{3K46Rzg(d90`R9$=SLMl9y>SVZ@l1aU=Er`rOgJ|^KOJknuS}Mqem#J|>j&MP; zj~5XpKU(KS<%M>_0zG%Pkl(9|T^!ws_mH88>JhgA7sH6=?M7|F28inFFZa7Zk3sXr z%dP-OuJPncA+b9N{xf#IO0U9AxcugVl-=AJ^+)d%PjD`DZn(d2X##mcy+;oJbZfw| zwbfImkC%p0Ce6fOWqZ>pjbZm=DgRe z-x}HmK2yoY)2Z=b>)mSPkPDi9b*4>dzIRbK?3g(ML|&SOf*R5@Q>whLZuf3`9E>%A zr?-5*qE)9t%Rd+^_08jygm&NOyGkJX4spLF7HH?pVRx|WSP-l6d~97P!F8BDYo^J6 z8~jY^D7vYmON{)i+$DHd&fC^Z5tCBQF>KTwx9C|IW?H&Te2aJKd^y6)>IHHNz>Q_3 z#~H_`ctWaE_f`y-#>NOv?;F_{d=C*TenN-I@G7{)HJ&P_(=$a4nDa~Z*^z*C_>!K5 zrCMP&1G=(znvQ!9z#4XP>c=yniDTpixp~x>SKX;=o!TMkuyD4k;u!MaHiO&_Imw>y zP^Ioay#P!$We2t0pv}o@&_EATUPaA}2qWjxyX2|CHxNc|mpXibQ$$+3%Ho^fg+#oE zg4w*7(H!33c1p9IBGjDNzb(=W&7CRLgm2Z&xyeq(L0|K5gs9?!#XKHrnRLn)r8cNN z#Pf<}1+wzFgnaYlhS4A*j1&^BW;K~lrXMh$s&@`O-fz*>5#%mw`m2d!b0{6@b(^0_ z6-$+wm3>`VB-V7kVPnA>3D+bd->-sY2q^=f8Q61HzJ@VyNzLV#K^_;QI~FY&6D>|f zl<2lM)I1={NqSW}57!%QCR`b;Z+>+QvR(pPXlQMiXoJ0jr?5Bhm!vaQrJS_3{vKB` znSa)Q)Ulz4&G^g8eY{)F_ss?l!A+qo+0Gtw8S6xLh+1e+zr__)wiEeE2KgFs{Rap_ zllf?Dc0aVBAIkV%+*Jx3SjDj^tOKqANr`_NoE2Mi2R7$-94vjvE*h75On$0Aw?9Ij zf7bSbBGZ{4Re}k_S1zE|>hagG_lI-fR-B*OkK2AyLoKMrU%A>D1UQ1#HSor&l{T?X zCv4#FWvvuJ5SpQ(yR62|uU=mC3LO)+J#^Biy*~8E5yR?mc7CnU6!x9RfCJ&?{hIW( zRj}q5^fwvUdhE!BivLhYvY+&@c@c z&J({jLX^lM%c8H)>Ziaf@Gsqs>)*$?)=;J&;8t#cW9_IXr#%empBvjng~{uiOC_bk zF$EjU9y@ir^7k@;bv*8{our1M&XA2lZ+YFv+dV^j8{*PgM(>bwagDs8)UqZ)cfH=_!{5ulY=Pv@ zv<1FNJy<3 z5XR~*fiRHSo*c+A)?JV3K-8H*;Hbs!haiiqF1i>r=WSnCFe4wxe0W8$Z)x_-OCM%( z)%=`&HwQT~UdFrxgvnHcWzJMrjny9w^XQ;`nJ?H(d`kFUmEAPs0N&QB$!@YnoEuZS z?q^J_;{^NefZz0Is&&`Y#AQ&jdR;;o0glCoU%4(d8f3fcV$dX4d9ZNO%OPIiYeNfH zSx_xABFOZ|+on_>u=yBd6bf=Bm*%-PFreo^WMGjPVrXpn%5ivFzN{pQ1wrMojliwt zpj~RGkhD%GkUT+$=+KKV**MOaZsE{Uzqr|T^z06Tw=T2qjC62D-sk% zxVJfqpaoZFqpoDeEPArdQnOt0;%NW98o(xFoQ}ROL?F0o)W%U@3spkDqhtL1$$SG~ zwImc!W`=UJFEdi6F!(A)HURaH-{(3q7i2<>l-W|uq>kquQ`&s$GlC-%9KX{G5;#~M z;U_W~Z&o$H@NXGfbI~$*S_(TollXNJP~}}xLW*Lw>-ymZ?cm*pS{I^hV|`0{n8Y(Q zDW3C2`oQXKRMYzGA^NA4DGXJ4_d`296cba^+UaBRh?}E1gKI4QXW-;ocYW;JwMm!D$2qeEh^{irh0y=vUtkR~Q@& z<}FE6i_4D-QS@b~Cm6)t=U=!|h7szM^d4PIYe!vcqX*@Ci|dJk9(foPZ7K0yV*!>3 z|GiU)*hD8C5ODW!`wRI83#Ar&ps-ml0%wqi>FufQA;mY+}(0*h1(9!tqd(i@-z+PDTQpWlxkV?#U3Z+42 zn{7EY+a@D&u;Y#4h5j8vO3J~E(6#v?cT3K}VGC5{PWhC;WTBfa&SGG=xne)F+b;;@)n~mPq8@TiQV?z*UoCdp8doz9a2WzWa zM5J^^6Q5+`vS{7R%L*5;)Fq?ftb5YMJFVqE3E7suyhAofc0>m;_j zC3b}4+3(mg+E?F21_Y(vQaa%(-oQJY&ZeuDPSVz0n4c8goWKh9!o`65D_y+6H6y;}iSZFbx{qR^(1u*Mse%x-+s z&t96`6D(t$b5hS{@wLb~E>>PbvL`~X*tnVD%En$6K}f8Bf{%CLeJys;$w=TnJ3_le zg$c;X&?kc3-+cT2?MLOTInH9aUWv6q()A@r2AW>O&UXkqJ`KnkA2{lj@GNX0#*Z}R zdYf;Z2G4|iMq2>xF*I?{l)lMaA9>~WS)AJ z#OWxg%;{cROn|N?uFs)GC`)v?5pOSnlPD8D#LD zBS?`6rU;@z=va|$h~|Kczw`zboeT>8^yMZAfBqq@J02i4knKp;=AdiN9xac3{(PFmhgea$W|*3@m2UP-z;Qbixy{F^;XVvVC-$F= zzo`X&R=lG9Nr{yuKCK<}5yJSTsU)et(rb3u>9zf1PeL(fVL9y)SNNO~*grLfWkq;x zY6{a5Hhc03*Vb%PC(|6K0Cc3UufCB_vcLXR)v862)DtmK;Jj>x>H3tx;|*Hs@*F$U zw^fhQib}A5oGOqd3;8N$D7A%N6_1i|!eF8Nvq9Ewy_Y|)o;yRYSUH1iylD0`R@~Lg zA}>KM>5>@5oBjE0M`n1DYfvF|Z1*07)yv^Th6}Oj$tb=hlu}Ncvr9~W4{NBmc$rJq zP_Xwsjjh{MSPz7b-T;zG!JPARu(#8lyI4)9>`DHZsRdj~1!E6bH=ifVcV|kasWJbM10BXcX2?Pd@I_{d`iHq%VP%+S6`7scmf5dk!-nA0L(a4Nk09|obO zZp4|UW!GtFOr-YrhHG7**W})fgRm|DGjdZkGnKh&ab`g>T1wcK)But5_lWf;qjQH! zH5#36_tax;D9J`$g7Pj-iDTzlop-)ATZSyQuL+gC90r0LhbPNef*1*Q%zKwXcJ&Mm z*Q4DWV~;_dQcj9C5iZd}eHZ^^WcRy&+0Ap%FhPrZ~GxgLiHfMm)HSmgY zqv7sZihtA?)N7+IbZ!@K7C-{Sga>!+X57`q$wKI!;iKRW3l=%NabBWV;*HnH19Efz zJur2@-?0sEBmm_T+LF(}&_?@;NL)L&bhCZKwK>JO89(Q-{<$`nW4aaOYKO4i?eZo>*e2GZ1?K4qam_pNe z!;v5`2B!jx{V0w?wY_pd4`8%LdHoM?cIps6-egWC1Ub7U`uIaZ23_)0FNcAxxAg@i zQF&n`n15PC3D#p1AKE^p4m9Ap>6=^(t{FRGx+JM5@WW-p!9?ikcD$*_;7^ciE<@tO@u^flS+8J(ctQ*4!2%^^eq+GNgp+BDmpD3+?{P~2-mw} zcza>gjylS&ISbMY3ZS2bQg@Gv&1aB4Mur#66$>-Mv~qIQaB=ZD`Fwdd;&0MULThq%*B z>GRF8V*VI`1y-e2X7PGXGJpo=k#E{~V+Ce9H(cedCU`aosS-Ndi%s*eLcfxS_W{Dj zxkH<9gof?y?+MD7p^^x@Kvd`g#(w_NZ}wG%2B0Lntnu*hK6EaC$P#=7U?N`ad=PA2k3Iu;HY`|Ge>+&BxzfrQHH%g-$8i<#&_v*V39ad7dVKev77l`aq|RvhKm9|3|V7*ylQ}srGa9*A>8=zfp18EAylhctn-=vz;IK8crJNi;3y=H*ozMR-q!-EWo4PR zQ>N}ADaqDLzqtD-DUy9zty6uuzDyS?)UOD?R)17-YMZ9Kkz3 z?4)Aw$6fdK&=E+{I+-iX~4~?R= zKsMK0E-=}gGJT1jZRwq!Y*B$Jey$B=1e%jM0)F&nOW)qJ+aGAu!gasMwAz_1Ue4w1 zCCE_v**N8djlT90Rg5cWY88F)2W$fWR2_D>nwuCH{-+|EeqjW-fzA&HwTw|7yt9N zcDzafh_7dEEbnd3Ib;>+0aiF zp3Pl2jN^G3J|E&Cd`)8F}JCa7a~;sUUgmf0Tn_O4Kvlc z;+)`)mmEEz=F5%(ejM^^%LPh3%<%Y#*_Y~Traj)&QRu@G&e6LtEK1ppjrObEFR<~yr-E1-1Db{tTe{LT)KrjeZl%F4?6^3|a6w=6Q~e|QC}#_$g> z&hNGICI(Wlgk=hH!sh!s;VjGGw;qE=b`NvINiHChEAv{drqoerBc5Hcyr~!1s+HCMj02uNrL!+7)*L4Ft#-jyqX4PZRCS_>3c!1ca(C1Em^v*OrD!{I*DxTV@uA%Iyqc_a2IYqA z(z;c)&5A&l7eF<5i?dBcx<(d8N)!=MKPRrpnWAQgnu?og; z$U=t39EKC;w>-N~3|yI^do;PLW^n{Oil9cZn)}{&94O*T)k?jZkY@&M>J)Vvtf4&j z0+LScBq(dt+5*~q>;l#H#;v}{A`+;0Dksr35@@~LUR;B>Ard2B51XimRcBg6hggnt z-o1LWz=V*EZHL%#t&Gm$kwcsDRrB571}08oj0Xw}awe;m6M>te-%3DR-nO^ggTU zk~+xgN64o$NTm2B4^TzJj%{`Q@+MLT(Bz>vYKyyy|8*rzM{z z&?fG($uC`#p3b|dQ!;ka!!LQ*J4rDiN=O}5udsbsK@wzGd``H2OL8zxSz`XFFa!3sw5EK(r1L#55fl6_9 z0E$T?H^n-BjdaPVCLrjJ-!9l=q)dWD0i=4;%`kZ@^CMtG^8xh*F;InM0u!yX$Vk+F zv1^=;ykYJ%J6IKc({#P!&Qd=*Dk|z~cYGwDh)C}-i;J%v9!ii~u#^MW=)rkcuxFJQ z>Te++vp0d~M!K3##13=C7+<_0PfNBl8xE7Jv)o@`Ll0%AoHS4hn7?H=-<9U6ax zEGDD`KH_8-|JFsb!FJ0A>ZPO+YbU~)Lxd{t>@x$GGoAzDPBW7TM&dPYweQMBD^HLq z07K7uwjb_t!>b@*J-x3O9hAPQ1#na8^Ny{~-iYBhDCKZj3x}F7b;uvv@zPfUT_RV*d%~7d%5IT0_;t&5#tf5COwnSG; z`pcj@YCy>K8N3oPO(zSK7)6h)*a5@%fuaYKL@jN$>tudEo8Ihu@TOV)J%b`six9q~ z>wwU?3QTEnYv<&1}Lk&6yGQ-l^->m#OqEWDZ z5oZoFwCzd|%v4^hDG__CN9@(~&%UdZ;YC_kxZ#CBU~N2nGEXTD817JVPhVQL&I{Ey zBhM@!1H)^EVl;$fc;dk49f5XBG;KpMJxeeyh1d%)H)Z#DPfBazl?6I=QC_ZrZuTh* zMtN60?ND2|3a2vp&{F_YOcgg+GX$KZR|)_(23Ey5N}J5mE2c>|COU`6>?y!~E4GP2 zwm+bIsP>UBb%A$c%nzp-x{C$(NH{DX_t3%}|7v(r1BO4$lHl-P1G3r83>kwUyU8D zM-m2wePd9Wne(0%dDs>!C@AwSeQ+TCT&~9Ma6_sZaRyfJ^-$@h5HHIjq2UB#1Xk^3 z|5dC@rn^CjI99zIEtD)fl<8wNV$#>3WaoM1N(km(q{=&G+2Jib?oWhe8%3)@ zuyLu=!co0eX0b@uMl;RAK#v<vz@g%>(_H+Q$bNs(>jzUFbB% z_#gsqTK`3TzCBHi`WWvn0U$u+3chnwwLo81oiiXSsD8T(=`mU*XfxEK2r=3{7-YSQ zwwecc$SBG%dtaHSRP;yAs}+EC696)zf$o=Wy%NMBUl}ipFqTddHO|~eW4demk)~|% zi;-td-jEb(cf)C5zD$JZg&nP>c1cCTox$Kvq`+_UTmKys{Y+!&Nzn#G;<4UDsUJ;L zyiL&6-uZX|kalq=>}a~RGyj72#5gI2?3jOoBo!$i9ax{7phg))(&vk#(c+3Fw!uHt|M=k90Vw3UdwR5SGK}Kjad*_pW;`Q1;|lP}h%$?KNNjHg znbIbz#3kwEkCDINp6ko~pCQpJso@&381^Su(4E3SEWlSQ^G z_yFM7lx@D-GMNl*!9~N#tBBo(>b?p?b7WU)4n8}?4T2`O9x zZuLvCfQiKj+>?T}8%{Ablyq{;ZQ2!YoHk|;StDaiT#;+GY$D6U3mc((cKfiG8SkOd7*dj<;b>&7qc zwCpi%!Fq&RV-GE2BDb7SmwuHCXl10Zx=UY!U1g&E;=(yEhbgU`u1J94e#XB$F7{dh z7f*2~3@LsA{i}MGRGhtnT)TJ^&_vUrGKr%nxmt5m{qw_|rB_yUZ2*Xv)m0`-)v%Kr zNw2x&ZS7sRn}UNH(5<^E+RFDrsy@x8#4#1?M7Ez=dD0Z2gggT`Z`6#6j&5R|N*LtS zAxb$%(y6GNV`7SSGV=9B#Irh~2TV^o}LdZ+AyH%VQ-Vf0lo}*nY5QfdIHC zRF;U);rUo)9LNRMV)uSFIt|7v6ukWQ{d142tYFun> z?KumzEiIsNbQnY3<12i<6uOm0+glQ+#AwoaLGUMKY)xIQvM+D57mNi-%r{f%6FJ8cO1a$AP0MOGVYBvF|!Oa zuq=n1M9^&nN5#Z&W3jT=?OMVQ4bf@r`@d-s3T3~Vxh1P>fpA$RfBhLB8<*ivS>+z) z%S#X@`)s$7>xq)Cu|wy|fKsclh=@pzvf*>~biVz@_lf-ZTR|hO6cUbGOhC81#zrl^ zJUiPID|*iAQ0Ys4ATn+R)|8s@cOI;xuLt-4&#JvA#6k7ep-&dR-py*3ciGTrfocT? zAd`D}@Z;*~J+apH7-91#yaH+9?0r2E(mo%pjMn}iEg|>$9{`R&^ke|)QLLpR2C8f8 zixxPbmGIY`{M)M+8COax>@W}VKYe_BnLK}WhVG|~ic0&f2@P97Oqq4SIP>Iw1C%y% z!ug@fhxOXqDypX&3|SQoLYF^lbq{@ z1sB#|07e4Q;=H`d2D_-e3Uf0! zDg7oQf-y~EzX3RSyH1I?43qu6b%9RQnUkm|53nDQ#JM-qrKMu;VyLXkV%cL z>Oaq;!~C!3TgaL$Vpkr8|Ci;#>!5+dkN-L!kC!D@zWA5L@xM0J^K36P5+&6JtJPKD zdLoNf|MTiz!s*$U7}&=mqNQosl6_O(&C^z4u<;C%4O%yiEQ zD(ADzN#J6hxj5s?l=`GxqX6EZn)92-d$tFFCBnlp%Y&}3a`pqYFSfNW^D0}@D5R+P z7(OhnJX;;xrvrCpd&6f||Cth-R(QaEEg5X0-VHA*y4l(_c14=!z@MG*_Ga+DQQD`I zm4N?&8X*1rC<>~h`;}}u9`JvZLG~(e|KIT}*f-kv`5Yi$$XwQBlKpe8a__k~BLvtS z3F(|b^W{54>B+fAR44uAg8-{Edp3o_LY}d!bJZT2Wn-VhFgF4=W$yVfQ^6HB&(D`S z0C1Uo@6F>kVPWkUx#MdlZ5Yi`(LX&ZpUqC5@ggzf-~#?qG~SJ$6Au3+sNBny6))nG z069&JIFk;@3lG*QyXynw?EmIhfMQxP?MD720F8!$j=XJb^qLw zoMW$Yz$TZ6{P_C)D4BvL_3SAxn$-e6Xp)A3B%?_-Kv|TW=jcHO6_&T8DsIP8o;4Ny zt&sb#Tg#c(0O8{DyQ{kLG}Tqb`eVnMjzPpw>ZGJ{5$69hLb&uFP5*dI3D`0%8#`?=wZc#vU!`=mwm}XYdgkLXCFfYTn zC~pehM;K6j4;THhr-^nN5QZOILTD=`A{%VPqzOgGkd;M@)2xC7O;slYCQn?wVh#Vs=PD z@|gB8=WFKmg0{*9WlbzQZw2*+z(+mL{wvK>(iis}f zjj$m2qRlw^!|*$xBnnCn_bPr+Ku9L!x{#!6nzmBb>V4GAR<KZ6-IF;`ip$M_RP0wfABSFm#UTz~!WLKPg z9#yCp1vpIOyD!R1-K-NQhXCsc#bz6)uh@pv}|Kv7L!Ouy%v2nD2s8 zW>%qXCQiP74vhJ-t`6Lr{$I1S=Iuzv$|@5N=%+qqhL@6R*G8&SVY3<=*wefh^6iB$ z0)e~H@)(V){SctB2Q^U+A*S2fr?QwJ3nuFbc3^vUMTNfQtYBG_o~YEFlwmpsRF}I< zN@DT>=LLDgU6(kb2-tIAsv4T)p6Y5yqXYdX2jIn6uj;A z0Ao7(cv|@JO~9(eku}wxvNBC9iH%xXf*|l|yjcmeD$qnWnZ6h-Q2r!N@H~uTpsF&X z(XGkRA*@L@zIcs_rs)Bh>4y&fE%c(Km6EAF-R+9k3}AUE97U%x<)XWgVDCww|L#nX z?lIGqnY5mGz>4U^YJr4E*qR@w)#^r%TvgVSS9i7d&c0+Uffa%h<`4#*^Kw}^Icj!d zugt5O3Z@-fH0<3_-EygX0s%DLwvR&GEtq`4&XRg{)kJdO_IAl>&K-5Qr%p?KYT+6O z205UV+EwNd9X54*V!0(XuvH(ZxTsxB5g2BlOMb%@?{DS`dpzsoFuZOlO>K;X0>oJB zHS^t}>eYI~yvXx(1C&*nz6W1}gvfQCG#~wn2cm?Qqve)UTokBhRlGraoEgk8VArZ@ zTxc(@~Jl#DAJg}%(lSBR+tf`#t3`* zcmZbE#`d?8rM(SGTkpRj>I$L$+MXk~QWK%B-hPvTdsM=^#acV-VjI?Tr`_AZJyTyf zrTkLmIxyNwu#TA^>N#%Jq34P)|5YMZGpddEZHW0O?Q{$a&$qh8dNHLJAK;$Tvun;X zfO??LHt+Lo+8NWP66@pnBNbwZ`k&PWq-iD&;X*ubNmUTyZsh4Nv zP6z(-F>?6I^#8E;=Fw31{r~vYLP_XKrDO@IBr}yQ%TQzsk&-M!WnV|eV2mLNm9jJT zC3`ZmuVX4j_T69@6b55V8D?xVzHis(zR&l*uKT{P^ZoC4&hLCqr*k6aJ@41NU(eU$ z`FuVek0)9nne#$@WY_~Hi*{cL(9?SWbLBx>{&UQ?t)i}Xu+E-3Co}*EKrYE_Uf#)(%GB@5as75j=RSsJAk5$r`b0441%GnhHFel-uLY5S# zzJ*igy!(Eu2vy1iOb#krYZM7F9_m0y%}}YybjiPgWiwXC#>)}KxMBHqNQ;&FX2VFO zbH%O~1H;0LvH;UVJ(@$5EHauSGASm^J2eG2GC8wbg;B55{M)%=3EP0Yg;Qq=@RKv&ZZp#$(;CHMJ%er;2$XUaT2QBY8bi^RaCWyYNCW|}a(a3mb{Goti z{9hnp@LRy#_IU58ceZv9i0>AR-(tSmjd~|fEBtrYN&BZ~B_-Rn@)g^PVT}TdeX|+~ zKeH(31}M6kd@{04oA*gbT66C{S6VX?)|-b@!`5z#H*^ze=rc9}Yw0}0a(2CMaj(=; zuiOP>8ZBWe)5Pio_EyfLFVQi!ZWn$q3`4j9IT;87xObYHuQ-v}CPj}q`fPiOZwm5u zyYB!k)b@!QsH540lSXjlIicR``pqx?M}MG;^}Hp`3rJq6*|OVZ%L?BPPGV*IFS~f+ zyM4+s!gW-b6~U>gWFCF1(;j%h^cT=hc7&N)lma~QYD^lZ9l{WHL1s*!4Crw*aIWL> z+1XKH>e$zpA5DX$12JN(h7pnbVBBV8=ap%Po^HNdk^|g25g>y9uJaAGj?B;lyhjv# z%vO^sR~Oj2h2*h+I_&^tcrkJ&e6xG@_prE1Zq=Z)9EQo#WChykWtd`S*Ln+Qd_fYu z;Oo%PQ+X(5z4a zPT-dtFuyNGYOK%f#L5HW|4B2q+l9IC>W>4~RKVr1!t_@P>~Dx&XO3g_J<8k3i2z1&LY-LXbtPRT9kwrQeqT2|(AY7~)SV6{HYr}}-(($7 zm$Ec8N^1gqDoE~|4{gGg-?Wc&Lu)yuSL`6-nzq?0#C@|#fk4+okrOWb zex8tWq}?(@h+a+=%MZ@1aA6v|Dy_U|#tnI)3BTQnDk|bqZ4Q5rDn!GG1Fak%V!HLP zEuGKdgTI-E4E}r@To{Jh%w*gfAnA_r-w8(mn^1yWLT50PSNOfA;)UO9jysquN zhf;wezvlU5EYny~j7r?J+-rC%u)8sg`c*h|v98dh?rkONL)Apn7Qb_otL%2laQ3-1 z#p%zm39NNgPN(Nv|Ma@I%}%$I zGx})F+Yh&k=>ebV#Ox?UMe`AD?)no+VvKQ>GH>QT46;VEZtM&H8BPc7|HXM9G{3^o zSUK&zpt(x67IF+PYM|zy5X){2i>z+$4Rv^qJ~dbqcT@0|QL)w`Q*mm|uxY8>K%9OE zR!=n0WzsyCE|Ea&YN+zot^Ik_VmAAG!aUWs`kWeftbx438M(^nF5rCsVxcFznE{jN8P5%BW&QUoxt7aYmDehP@ zG#CE8q;!(*HRyBhcE-hluFL6yBF=r;!fO+jeLzbF7YX1|(#Q7DKvG6z{p2FmKV3^% zO&t_i+M!FpSZJlt#=Ie{%8bM%DslFc4^9KO^;y_wcx;2+$W%{q0Tj*jRl(jz%4_5Q zJ%C;?PP&0n^PViQpDrKbsKx<3$4=)h;~JrXh%X*FmNYS?bkyN?^9CPWI*gQVOHQ@E z?lZRfw0vL=q5=G4?8870e$^ooS?G>$P@jWnR(J2}63r=!xns`!S4S3pDtOQF!O2AB zU5}BHoFD@uam?++(tmmZsL+5cY^hO~!fJ4EkL23x1xRocE8L0Iz4e>GF&EXhUtY<1 z;Z(!68~o%=ldB|&rV&vqQk08dfMtSu-~`A&4?)`?IudSHD^>C zle*JFq!Ah8-dosxUSaC&wA9K>hgwC;*7O`*^32c^+N_I?nGlc`s=$C^Lbb53;k*(v zQBpssYOQmte#vbj+?u7Mt*S=Pkh44wmWDNKLL}w8s@?sW&%vAOjOG2e3l@W>)(IfX z>iF`TXD!?AKUW|cp~S{2OfQO;)AX~Ch)Fp|y@(bU*S~50_Lx|F-qL8zMkcKYzT=B^ zoI^UyoswOITbIt&_29@EsO7D3lnzscoOY7`(wkFCbHXrmp~5c(cEu4)PIb67ce8fW zsT{%xFkE0s#Ge`&_yba!#3dT!LPtt>YCJB@d8ksDx^N2Unws{69N-(ZZP?P5U6g;f zmT?WKowlX*{pbQ>p)?0FrLL|~;@iV1dmv4{n0fe$X42T&jqnG_T^I?S$d;s!@14V$ z)VfWgL=uMfUt|Lx>p;ogXWs^HG>{ zDx!5=48U(g)V5L$^zo=`&c@M;KeLg~fL?(Y<1tCDt6owjXlIX|6${1^NZTJ1 zsewr(uj=i6=Zp)SXIyC`G!%2$*W3Ud*_uRBx^i04y^Qj`e8uOdOyxo^k+a+CqoHEm z8S;B2Ixa0{zxQ`9Ib4)my6@8VfV2^AuwV4SlVDJgiX=n%P<4pMHLo`@}Rdw^|EvLU$xKiQgZX7rN^_kQ>vqFT5jo-+`_VVP?0IO(buUQ~SlX-z8JubXnbd zlJ|smJKlb>B(}Z^i-|QmhIy>TjiTS)2)7z!u>eIelh6|QkgO{G1bL^(q^@y^gfq)W zE3|f^5{ciOa=ogGW&NJ5RcMhBC4dpKgI0x_giK>IAgA=3^5BmEf6b8;Lp)3$cp%o#1cgFytz&+jjJ;-Nfdb@{g5{Qt+O6D<9`h$VqR{ z*+u_!vOd{S+EXv^3PjPK{Wfpdifj*Wwy-v>j!)JsDI>o5&!%Tbu1}?%(Cr>Ex|S4V znnj5K3ycV`lyxwi^>Vna;N({41ux|L1+e>}o0L;6yp?uE z$1J1)Rz_#kZlqeB@;`%~E&(^mf6Nb-HdHSqUTD_FNf|-dAJ8vF#*J*6Ts_a+TO<`# z71>yi*3g~;bJoJk7YHQ^mSL+4d1+iM3V}&MsS1OaQZgqrS-OjVFjs&e2(bI4& zcB{gCk+$GXw@pt?Ju-K%^OO@%mza%G_jSJW77m~ALmw|f+(m3bM(+FAtE#V#IIL@w zLb#7NK1fmXE}iL0B@#PUtt!@*#@Tu>sHYf6=gl3X4CT89Ff|y)^p-E}? zo9YY;Sb7d+4q%oy&kq>T&*T_g^wnm96 zHWcW2<30;p?&DM08L{`zo_$%f?C?VU3O!OUa%uGQ@l$<1?m3sv{Y-g4^E{yByf-Rn zc}$|tD+7jl{WZa6-z)zvC+N0R86gJ$ddA}Rw<h|D~yarV1FbeXB$Um}K{FgoS@CDSrosXCDXio&I}Sn@KS+ zdwOu}>LB;O{_elM@)>JjVE3<;55NEYPyO#(`_qT^zpM5qy59djYk%Ib|G(LxJHPx{ zD;=DG87FsLlQu%?ZvJYJRmi~K5SxOH_wKLPC_3voG1q08i%4~pAi2&+H_v>JpzEZm z$G*n1>&-KqiQNt|Pc~++PK5@>EG1r~r(JuZaq&L(VuKI0dTq;@%8Z3rR^Kx>yBPZC zKF*&SJW}VcXGtGosIyip#;qtpzhf#r*%*`DZ+X9yqt5VB-)CwXtHRU+?XV zqqX(rNLK;qWqochR4`!ROq%{a&ZY~^VNsPfC~um$Oo}2Rb*5%A5QC&Q#a=YUCUk(L z7fT%X{9R!71Qt8(<}vjs5y(Z%aqw(H?JQyKTyH}6*76CqvY+u>kB!qaWxO5${qp?E zI&!V*iqGmNUIIlh>lU|$fO4nN3OTdkER3XTQa*FM?3wlxj{4P;6%JjD_P*^=U6hBa6k2-)S;zGOssa&?doVss0$i+5f8Dw}%z; z^?;M~Et(puQS4x*Gpq8LjK$5xL98VA2~a$6gGNB>!ihfV?+Ph`7U$o)eB+ZoSmx3| zK~J=8YZTGlcRVmP*C?~w5+h}+-M2mV!}Zs9*iD>#lArzPRrcbjJ)~f$*sP-*hL$F1fHc+ z>k?evGFIzVa_;OTAyvN%_jwKpT(k>YKmB;0V*PY27ejR{H?(uhCGdT~=4=?n^;;4_ zd+g(b!s{4LWQv@t7~J5a-d%J4gDILH-_*~`Y~Sh8WY}hc8yl250YBn z!PO44DqDs}O9p;;Q^)Tp`z+@-_!S|F3ufvV?l_vDTLT>%R_?*_he6rf0(uk5RkK}B zU_^v_hO;C!1Kk4R^MCT zuPsfOwY)y%TH%shp_T~}u=dL)4sEF-7URc}zM5dqJS4Z3*Gt8jEQbJe=Y`%ikYj^UlD1*5O~^qt{uCCH9Pq17$vQ;#nM*zG;~th4%Cjyj zaarK7cU`##I#AiYoO--~Jh;vdx9fZRHM<;1vC5)!)X`@?g{SyYs>lFOeP_9v@+OHWSx2U%h4LzFs-ZJ6X-e>vsezLI_X{~!7k)_fzgwHc z<$JT=n}zWtsv=i_=dSe@p1sfQlu>nnm9npYH|V@iB-S$+OTiY4r>m%)Z}6qXBpOw% z>~(=E`$i`ksh*<*ZD@JamR0)#W-vH$gkjcgGRuFx&w=DGe7g+YpwORfD}ZF;_#O}0 zpuRx1b?=UDw2H>6_<~N!Kdx0GjFXx!{e_|PuU}qo-^H$+JD$NH-)~7tCr^I?2>)g( zjn;rJc;Z~&$JgDOYr1iBD_;fROaAGTi%l)eF%f-W_(|f@XgP9&1r!PRq`Ri}+F8$i ze8xF?3xgZuKH+>s4`sS=Zh>CMc}pVo6X^k4x$ZiK5isB- zS2Wqo2f0AFhqVhsU6R>YnXRB~{9TkN(tmp0>}sXX?v6;b4*Fqu>Z*5=fYqV<%^dZ> zuuwY`jH8CaBziPxWdM>1U>w=;VjYZnDW`jr^}6XuhHkUXDM|3*lQWY;APO7UD#v*a z<{4$)b{WQ=cO|gBwVS41b)^myx7k_!1p@^yc)8~Lp9X{5KTJzd2_h2>x)F1R6Zs!B z0Xl_G6_4%$uV}!v8)H>8CnkD5`#>)K$}hf+k;*oS{suGOQa6YZ#KsWRTjfslDVoeG zb0d|1?I53M$KWkEn|i_BB2WHbcR!({**YdTgcGGREMjbikv<=R36Ll@D=niyv-I(9 zBOmUx;MO96u-N1_UfV`A5&-xa5}7?of{t1G^l_6?Y@@;^?P+Xal(e(4`}XrFfZ3o$ z8oviBTKO!z?l^UnGgS=9_vU% zd}#Fz#+vCwl{JGFZ>3urkWa#k)?|Cyzv#tL%>?HA!3TJSq7_>r<3=m+ujSQ~iLkFL zu(OjK0=1<9e?y|beux`*cEdOi3e3N=r1^le)_&#w@_C_J5~hA3Hy8^HHyt}e$kq&> z{nEzoQ`m%OZ+0B+P@L$NntjKfC@-~d?N(o6*WX4x;t}edJY8=@;6WijjKi7f^?ur8 z_#nL7#Ad3Ha==LUmML(^`sjYeghxgDl8`-fV5`FD0Gw0CI_eREHf%29`imK0rhNEE zEG)4dNlrhn)U|Dmv9=glCaV`1oihS#wmVR*OiUi!e_`Aj6q=eNZI$IZ{ke3O)WSFN zeb9CxHq?hy29zxILT?fLAqzn4!`LXi7<-2ZJ!D3l7Q|dKzu0A=x;!f^@0BzjNG!p= z`hQ)R2X*gw&Msuzpb*dRHRZ<|B&!akO57s2W>H#B!=gJ%FXY&`FAg+>557mnU4m|w zIF+6?6#TOBXzh?Wy*bJnF#HEhVfV6{na)jhaEG?90_2*ljUXpJ8EVmN{}v4=i8%qf z1Z!HWo*y?&*#!%>lP6{1WV9ro>Utp*qX-i@o%YVE=&ivO_~W`p)0CQdp(lae;7hZXg4=VT zgqWIS;Z*u8#(L@&q2W4Vf;lcdc?s%Jp-^8F?8H}%LowS}d}>RxnH!^WDzIefsQ*@` z+RcP?%>e+|HYEz26jtHgyDX3vlpSCxhbP=E6wU}-O)}$SRJxWXXWOUt@z|Q7BFCLJh-TLsBP=2aw;a1Dp%- z7tC>sDsAa17~#Erq7MfQV3Fu9&W6~r9Q}?ISx2nu>bISqtvrg{LYidQ5 zN3Kk18ZOlvtEV{^y1&=_Q||VcBk%X!BX4Xnd zZp|Fp8r&%2OwEd?B+M!}85f#GVHK{;3@?yfZAsBaYRM;DLDF5s64BGw@15AMAY znsz|g#=+CKf9(fH@xCd9yE(JuoqKQhNCUNME>XjH1^3K~tp=s~B>}o7${EerFaR6| z60Gi57N(cFb_|#Fm!QC@d0nz4xf2yVBlbK#3**in8FJ7f-0e}n3C zd4W$=YJ^w0G=UQes#`xQd;VU%pX*yB;sOqWVvIa}ka)@bdspVjo3RF0<}%c4{pase z4?a8`@n1;0AH?1$;5s;4)qJ7FcO0+)&}A$sopl|Sr8J?KB6@M>-cT2g+nkE=Pyyi3 zk{mtJokdmD!ze)C*L@Ok?zd`Me;g@u@!pgX`PWz-ykG`^-7ae{D@ikd_vw8D0GpPF zRySOCieRJIff(8mak*1m0{IHiPMuLcb=L6uKp;kVwMhz4#JT<^vFN@6+&TF{$eoFA z9GeW(4QHy*P=+48B9akKS@L`%`V@&rheW^X_Lbu~%s4J=c6AbfN#Ny1U}uNO3m;aP7X& z?CvXnUzLl!1mqcQ&yW4x`W-wIeH6&+Of=GM|K`*J?&qvIu+XtwQh#2tzrN2ilKT&O z=RH?${jXLrfBc4SIj|aM`Az0c^ne^RK8oS{-7L&Ti zb#_V~UILrnd;7eAvq=*xMD`$B$=<6rhrp!|(f7%ECMK=Y?Vi zwwW-a^F|r5i6tufxO&C5^qQfUiwjMR7Sx;t>yGweDrVlR#*GD4OI0EecUm)ti6sBy zSP-S8q1;Wc$jgx9Wy`Ndjk_D4!$l1fz+{}lWPs})Mqtufxe)4-v+NBtk#}JT_Cj_0I=T*fD=+uh*QUP&i-Wm>KpeQ^ za!A1GG{$nrH-LfhgC|Awdb8*&-7s1aW8~1&wIPy+N z;AauiR^Iu+PIWXYd<>j)$L4XSny_uxHc@=kjII+4j#Xvbw$?0lH{33v%x0)iLOTW9 zere3=MShIqJUE>4kWk#5sh)%V^dt1$UC#oiS+2y>ZOmLDB1AxgqrShu2t1*p&aZde zEhBfZIVf7U{TG=qtIJdh-2QUv21S=?*m*@lO2qy&1IOe zImo!JT$9k^?h92rAE$xYV+sTCyi$SJ{rKN@9(Md>qzzy`MX=)X!{*D-i>|OK@Ia7_$#&#^@3Pq zS>Wha`de3)rp*GkAh^oA=zlg3I&vtG!!ryCW}@yFG+C z;U0d*dv-&;N{v1TRtU}<0D6!XwsH42nTt;0OJ2bs;U;4=P#JsF)Yv~WtzBJ!k=E&% z&50@?X54UzQ8e;!_UkDz=}xn>lJd(?j-R&I;wEl*o=DCeg5TF40E=Uydg5xbN z0W)Q4z-mz#^UMfOwtL;lt-|0_`DUQJV4_gGr=ySLI`q4Vu zaJ+bS<-3+U?CQjcwx(Z0h8?}fyp4-d%k`4Oy*IhsGiRj<-%kHfdx0QDsgCqo|L|88 zCb&o$rSyZMTuDYx1hiG~tt`b=4a0`vv2s5D=-nF{a&U(N+onrv=ZplVE>OL7JLTWF zmt!Rr?va*74mjJh2lX^4@Ok}xe_xnmjf*+|IYWM7ALGmt{|tH9yK~1oY!<+uE!mW* z>}#c0m@4I316y6PFf0pQNQScNU6<$kNWa74dNE2%J%$xo`mNHxZRI} zU`Cl&EWcCwW|t4Ja^B=kVn3KRHjPzz6)c#uTWa~J_&uwW68=G?O?(PNX{K8ff;g!m zto9(t$Q0{c)dq*ZS5H(r@(A~NqmBY<%6Q)Aml&>m`vWVa$C*4GWIgYhx9At~>zWh0 zi=QBC$>i}}PSFXmWZT#C78f>4FY4b)S8@CFHjnW& ze7F_Ydx%rSo45=?<=w3jSc-Vevs7D-Nac+vghvm_1*f0n&z*N*$J&G(R~G7Kvktf! z>rm7SgD}y}3X2^#O2V5~-vvVLRdP)c{00NrLvp=&d(NZ#iuN4iDJ-$3$4w~sUFX~S zZV)B!`LnqE$$&#$OXH=1tWf{?4K@0`yHchrUL|v=^`>OuZg= zPR3>*!lJ1=Ie)GHsZ;LOJ54LZ0O@I9I0YCLH|$?B-?30&sdmv`-{#T2bS`(%^bXsN zSMuN0X;;r(f2aAR>8eW_dJneQF!7@KxSxA;Cc!;gS>CXxw1K_6F}vcF4(jv`uJcJ8 zbH$m}IJ*DVH_7YI+uV61(I2L|X{uprw(K%|i;eH)1HOpYPmM?m4~*8=H5gCC7?Oe$>wifH!W>bz06t z1iCxYMy-8qWUL;CUl=4QY>HPsRz`AWIukY_49QMb!6$nh?*Tqk7iXiiX=6KMc|gn# zVZ-w+d=XOIGk3qor-tnNW6hK*D&b^g#iQ&z1*JiluX}$upV?Y}`NKi2g|?&y>GBNl zAQkCih0Syzc$Yczy3xbmj?r68-b;J)TE_SoB6JDp;(1G!so$EEW81Dib@Wa)I-;%@HRt1sXY5aYLCQDFZc zi24EBsm&O$;Nj5-+#D6!qJ>L?@SW^W-E%T{;?|AuoJdOGtC{htZ9pvq=wY^7*lJP; zttb4w+oNohUd=Jsb*Q z7MHqRk>L_RQR;D$#Y$*2{dUAlbHaE?L$$o3-37!t8$jV56mRqe+Ga z@OF?C!!sSr#faC^$)vpsJ!6@HFB7!TgU$Ob8`m5n1sXhl5F+qZlU@3L;GRC) zpiE4)-tK9>{9rD$zNX_tjIH~qM>_JQE8Zw=y9nb5bi;fmYJJsKh8HyOdzzR0<%eh5 z6+bC&L4(eE+nDi9C_YdmKlJaOcSz+GQ9U+*y_Qr*(E%f-^}wF60cc7`3~f)1!QUn|#J~>B~IM!~dqA>zeZLk5rO? zPkM3A%xNl4e_4Uh2v{Q#1t#j0IKl8#!5;F&71L zn^XA)Y>=d3mAe?bxZFA5ut3iF0 z`G%CanQ2*IzrYL*(HUhiw&`0YnOy8#X4D)UFCboGb~f;n&o5DejLr{p5!Fc7pdGr@ zI*gy$=(jLSurggL2&DB^;Wvoc0}NE*mQ*a95EM~Vj8b!cWRb77gTQSx^INK^|7A-Y zSyw!G)^X{4nvr0ar-DRS+o>T+ZysLQ>w^wP;gOLZiZO4aIr%88afMGOUqx-hK!A=} z$cpRCSl#f32ZhTp{YX@!&rfN zsFGzaYf%m_z6OHy(;)TA)u{f%_NL!D2xUY%16+rgR}SjWaMkLHM)W|oo(EOc2u%t> z$9}Z%WF;P|Yh zhVlK7yjcSK_?DAxelva{`Z_2XV_9vTKBjJuvUl1T%PF#LNCZU)gFyZ0=Aka7!*UR^ zS4L*DN?4Fs8I^yrku~cQr7rx;C47NH(S8hon}a_;q1{fVR)dSXl-oWr!7kg7AY?jG92@4q|W84NPhkCnbYjfsT zq-JEz=uR|lYI+XNW_8@Vuvr!9RX0H`AT^Iwn4qYerPDVwtrMUPjFD+;i8>$e3%6wS zQAjy?%_}Vla#57NJfudIslNm-e)WCB z5tNyXS@xQlzoW8k<5g{QmYGA}MV0WuY|^Ul{0EGGaz2pqAp4F2vpo8$9d zU}y-KLsM+qEsTO{+n*#N3s1OruoLkXEGNe%6zHJ9y|dP7b>+&wqGv|S?SB{kZhW^8 z6aNVVLfiP6Gg7k$y(51Q>Q_qiZaU1=(C?AxzmCCMTR76qrr2+7WhjmOpKKcr|Kh0M z2K&q77uqDjjn_U`4AA@QdcNNR^UQnfd+>?+iGH~0-thM3QP$8E+Bdj^hXjwp^F!;` z@DDeZQ58C;iDExA5;Eh_A&9%-6Arpp(lcf5-MY2)Tp0!lYg$DIZ68xPn-fSl6U=#7 z?&%CrsaarksLS|IUfER#Q>Of#%;~+*PZUeO z{_kzPJ`b+*RBDZZex;{!Y8*wW&o_cZgRVC7KAQi{`$1W_sMAAt3EUsCz0nwK7}66l z#me4FCcX6WQx{$VUa?g_wR2USx+(Ar=e^#F>hx50e)hUE{DzS385RF z0n(bWj7<}D?6Clc=Po@D8$waMUl8w&?On~4b)vh~XN_DtdmZz2id0$8PaPdPYk zme-p)mbKrL+$AD@QsjKy7`UHo(>pM9M;(y_Wi>~KFXLNzhnV+LJs8`Wa?RIV4lSR? z%7~Z5$*BI6C}ZjrqORRL-7w8Df#=>EUSktND8LS0$FQ-!I|_XW!Eb}`q3ak_HLvjC zKx)K2dhO&DkM+;BSV#~~?weqntilM$zwzO#mI_rrAxO9<@H<&$YT)nOg09dI=G|OE zB$@U0ml};$XH%u>E5ko;!gKV`MbVFVtzGi_&bw^xi4WF*-`rK&nQnu*QWVwGLRl#J zG}d8wX-CwdoBC+(<;zoQD~4P0*N#PY*v@slk{7Znd{ppkD-!M6DEzL);OFwIvdkVn z!AFEGz{&AZ#CWt)ieC87{zo3qGzIF#QmlfN<70RQoaW|~f8eK{OsH+1n(Pf_XE(^v z!rr91Lp#4LZpydt*yJ~#4pYYNs4nsDZ1=LfYsE3onx8Lh((b>nZoL{b>QNCY_}M6& zedjd)Di<<9F~Fj5C9WLy!Q?uE6*=&V^nKP5+d1yC@F7P3 zG~tXcd&9aLPVFc6Hf}`N8o;EsRIh?VscX|=+B~K@=*XfW6`Ii@q5VC$ym_$@;W% zK(_uTw1jiS7d45asG$9{N4Ri}sxscbLPFD)Z?KBaj^oeGLHEtAkLveIumg@wr@M!F z$Te#ua$ibminn-H*57w(A{3vs3yj#;u1q z<1{z|#?zcj@f$bZYtGj>O>D!xIYNB&Mt1>k7P#BW1eH#)-Tvb2WBQobn$ARt-uBT# z5h}X}W@G0Drk-+pJ6|>+3vVJPI5Cnfs~0N?vEAUeZIis48od*m{_gd;;3R}csqS5q zebAUn{j=o62QYX29ihkmBxOR1bxz73NBIP9345cVVj~-fd~`0(OnJ7?lPwOLSe-xe zM{EGIh+5KfR^X>EWsTjp21I+gQqCOjjPpVL3M{+LOQ+@NrM?}4*_9NT{xjB9MP%}#^WYG> zZ_%Xg3WW3Y-d(wdkCTFMcZq)ZeV=QWx?HV~Mj%HezD69s0^bL>vx^YX}f{?y$-&|zN4UvB}{mV(O7>_o@C$4I@Q9uF=;!YRd2 zX%u5wQFFay*v;o8SlHsiwVP2BPJv07d;LLE(msAwr(N$=(S19UYrdUy`(3dhv4gTR zUbFKwNx)7aXpM5QgGc_IZoT)ylB&XV7)Ui><^?cm{tItMA)I989M*3^|DmiB{{WEm zeB1Yi#t?0{Q~)PF=IJ}>ax#DF=Ti^AJY_(5gj#-2PLN-$^0oW=PTouFe62lY zRyA2As7zQvFFb1C(abaI6c(=!YSOJoxVVQaE~$Ig;yhTI_Acq)Z|Mow8+Y&!K;l;( z^#IV<d56qK>$b#ka{@uOb4jxBpYtEW0cMdclK>y6tMX>2 z`XgYm{SF>-Xig5=!o!C7K>s(%*Yj($xh4KU!ZIpJ-nFvA86}Tc#IvxBaUYUgNk#s8 z7-*2{lBemin1=)xJ2;2-6ep~_TVN|Pjk&TpZzUAe+KNY(@Jo|H6Vr9V*{$;!G z8Pjdh@p-pcUe8<@bKIrW741N~q8YY0j?#)aR;>jTV^;wah#g6w^1wS(hi<){%vf+! zsU$y?9e>A*J{52-RM7hJh)0a-fo(E1Q4=by)75AW_=zYq3y8#y$(Lfb1?J*H9bRa& zAcU!9%e>*Pp^0V30Brzvq;CVtvHBSopaRpn8{@a+KKU*5+h(yA!$#r6XIt9+Ct?QM z5@~VGnQ)B}rMyXpfr9&6ci*0BCG- zo~|x()oz`_$iOviRxyw;|6U>)mq-Z%d25DQqfJjC-+m3LfBF0`UE+^Q=l?hjD0`t} zoB6)60gvXOy-G(qZLEoy_nTKM7Mhv2B85h6p=a{RiJfwv_MnmPiafLRZ`!z7MJqdd{GHvf>A6prgLq_=3C*q&oFlANokBDME0oTsVdFg?A%iZ80h-Op za`xKq1uWvu^?X?C_ga;NvjDjl+;ky8o_*cygLR4x`SI1DHK*c zU}=kyN~A=a``l{Zv3CK`%n<;S50rP>Os0P9+al@TDru$G43>kJ5e@lYs-wDd^63fA z#;e`L#gVJ>Bl(g3>8|}#_wo&r4W2%3P}IS67b$d))Yc_d-rgyO=agWYI{B(Y5`SPh zc>GAdwDiaRc>uaI@lXEk11I=%9&pNv1l7l-=X7vrOE_aZ$@yyruWLBXJ1>Vm_6Pe{ z1DypaW&B;Bv9PqDvp$h#M8nTKH0LJrsPNQJK%=zX`p!ZpN#Oz_iq1j=7 z`3bC;xau!}Z2|x(RpAB*Z0F$HyIbdRP@sqY z4#&&`N+Uk$BvFTvyH{|rxF88Apl0OyuBZ@nES5wB_xp#^xracc8*K9z0JI-`B96RG z5k1_v!SDlk2z(4?@qN7=_uWr;2~vK6Ui_q4r7Pe$ zKQ$n9^T7Gd16TIh2-*{&QTqP|#H@}1{1k(deDDsFsxrl){9WaB)8V!h$q1&2L{p>Q zXmyX!`St$AggZanrd-#j)T)8WbvBgJAXfU<)z5kc{iNSESL9BPN0eSd@A3K8I71*g zywt15TP#M@$KTr2xQIbsDTMSNO^Gcp-FYDRdMs$_=}cRC2OH5Clxlg~U~9g~p-KF- z(9p-1=g`3$t(gsy6 z+B4NBoNf|zb8A`!bKIm3!G~?pVw$lTDqOokKJ($$BI`W_v|8Wy(McnYv;C@l`$JT_ zc75q`+&T9Y*$mCi$CGQ9hki>TIf9~v%|@rO!V^Y4zZyG}ac3N?>?OU?XM2JlCO?5n zTO?+{Yw0|Rm_gS|DO7T)<>O_Y9X1yA?v+ip0v9>x2{2r>Ob+&}a3U0~bnfxpD zN0+)w<3Vw26$OqF^}=8DPd}?vjBV=ewN@UJ*4ViS-BC{8jifg({4H}h^ETWRWBG^^L8pt=lqU^}6Aj&rLLZ!7e+Ik|Vf5G8e|iCYk=T7v zpzTF|{E+|8IpaTqx6}oIi^TEk?)P40?=C)d_9dN2p@h8{E4O`C$yeF62Yl6xN5Y5nsX|MACl zA-n5lWNFO*H^eYN_q~7jqQ9Lm|LeN{nFC`IwJU@iI5D{YKQHY+-olwZz(tQf)w=)p z*Bq1p?qIEwL;k-{#@W3$-KW4s_dkp|{`c4X-(~yrgYdu0_NNUI{Qu?C_QZYyplenF zGwiTkq|F=qGBGB3xBMsh8$NgqG5hoMecR%t;sR8i10F{_QvbV61L*hx!E)%FWdq}U zk$HplNUU12>Ya=CFNrGH)EiZi6`8;-h`d- zZ%_R}O625Oj>!%=@O2DM0Qh*Dr)}*?X54mbi}JDRJ6c{l=r=P91s1n9R~Ot#tSx2- zeV=vw#$lroDoyGQv8*I}b*<5MCk17YUP>0Pnz+ z;n}WKzy@c_aG|dv$_zur zT!(3&a=eVCwqOd}ZDqch6j*=RvD+&7@{`!kRAvHjitD_)kGl^@(!l-(f2k|}0S5n@;Fj%9H0e7O;7hyYDyBF#@ynUuw=XESV_HR$>lb8i0STzv5 z9G>B0QS~@^`&X-|7b4Wvz0Kn`n7Xm{Y+>58(&_ip_g0wmDeAE5!G<;7(*gcd?3R_! zbo37)-YbK>q3WfUV8scmRuNi*y-kOf0Q9VA4shpc1?DfBuN*q1 zuqp|F+nUwI2^+O!<*wxh zx)kA@0Ur;$KERHf?urogQWZ+N$UrvJ8&0gDMO##PBob^;LxhUV#c!}QuJ>=wWrrjcD0D6m%p$9h3c-d)qibK z8jwGUeX$l7oCoOb>7AR8tG$=|CpbS?X;Xi#wSUMq!EGYSfW(#ff7pA^fF`%CU37_J zV*xfqsWwDFK&2@qfCvZ(NUs4AsiBjE4k9AbJ4gvZdJnyXrl2Aqgx*6`N@yXp5Fmuy zm-T(;yKC=rSoi)v`;UJN$@`8u=9ptVb3D(W8Pr8JfvgiKY#SnFa-$#aTjtsjTiiB# zB)SUDx6f?|bd6E*JqR8ED!LeuxjdsXGnd+xxUvn9&eT4)eO;;KqI?Oxct|w69$hNH zz{-EiTsV~#4;TNNU;Xb5-DeJ|6z57$Z+>H0@TLW=4;(aTWVsu_mUhy0%XGDX=^DI@ ztIfEi6#zk13{nEfv2l9VWo5wd`aTA4YUS!Fkg8 z3w{s)=)ye=P-+V-drP$W%eN-bh!f4N_Zgq-E^hPHI7j9ScFN&QRPoauYd^0`u^PrJbJu$92bKYYD!(sip}q&Rn%xm*^fx-m>@_|=zSC_S z8X6kg9hO{)gDte}k%+b3&dt=aGrgrTU3t7?RO>rR{~YHS598ePKNHRaac;QZGDj1K zcU;{gPw}MHImB9Zw`^{Y$R#tKGHmQEJtW&U04l{&vZHT;862a_vJ}I=zd_A^VM9MZ z4pxrlN~8@pwol@#t|HU>(^Q!sz$T#1w(^3@RPuD(8~U?jGttO_5>riaN6Y<*q+;_!W_WIwspBZl#gEa8h;@Y<1psx9B=lEba>(STG^WpE_5IBQ z0P9Dp?!%y?m79%U`MWe{(%7+87=JynR4-seAc8 zi@}+E4MZHc5w4OU?ZxA_2X(blTk~c#jbibxM!YDteKxhIV)Xu z!xG_l9&^U6g{L6DvF+W{&0L-WLF}Xd^;rFvpg}tc_#W+@{fz%XM z`}NU34iR`Qr+Y{{3BC`%p5~9`$K5r#E{nq50XWu)I{BI%y`Lmb^k&JStL=C+GfcI! zJhd9~28Oj~2j1AMFL0dw*CqN}y%RWl5qM#%NoLtpeufsa%X8X%Oo0n$bWz4CY-05? z-t^l45UcoZ0d;8D+D)Za9@&E}wM%L_cTU79XAZG^o$c)4(#UAzJG%4?tUCN-)xNFS zunV`ori&f(h9ku6nf@Kr{U2YAd3LzWe-yjBHScUdwC;r7IQOuTj$mB(qPMI{t+)S3 zkIXtm7|wp!nh&G9p_udjk7l&?TSU;2q^2h-K;m-gnh%MaKVZK>>MW1e2|62tYdCtZ z9A&&s@2dPGwxd^5>jGd*`tSOf?HFKDlt11Ill!@HHb*T@|6TLY@(Nc%_Vs_+Zh!a1 zbAJ&CUi((6*WF2YeExJ!#HaVy{j}#lar}c3Zyhq;Z)C1&0I&G|QtV5>M%ob>$B&#M zcnc7yGPrOI<^k|S z8>t)ufluVH2*8Nv_-=eXqc&piMV2K|$;r-vLtJ|#@xk#QvMaQQ=Dqoo4N?p)J;5_SeK*nMxNi2re{ ze|da>_V(m3*ElhRJp)xd7JEbc8DOxme)ktg1>V4aSpr(y4M2V58qoXBQygF@02B$T zH8w!V92RF}IWkt9K7@1re10np_{pq<9qD1Y?g(k$*L+qfJLJvl~&^`4$(NF ztYMr~<=6COWZRSxnyV786visR4=6#_%A>zz1h^5EGX)xg`M8gC51Bcq}KdJTd zbt zxI`Q(Rw0DR$0tWUO=-2B6ad`h5YMvQy~a}3qRPYx0>tzEc?0&>^BGqY`M}y;3FyaB zsn_XR&6=LoI^upM0=XD$ne@igAh*&sx>)vLiU)}MLVy_N=Efdt+i(w(T$z<@EX61) zL~+VxeD-xXTL6r%cC!M?)Y@l!k9J1ojp{uMbwyxjw-?Y^Ph9!z4kn#`kW7OvOG>{C zb~8{F9~81Dlj5un_`SMWKm1}wm72-})D$b!sUD9(#^J*&*_B;NX%pf?Ij}bW1e!k8 zJB-hL94d^^Vf5}vkyag&h&{k$u)j*{ztngz&_+*q?tMrxPb6wyT)Dc}W%~%WR6YAW z7A#rg?D)pY?iE#-eJ;|KINrpT&_JASBy1!UirfNZ>@1>Lm#?|{dS&h2)E*n^MvBgF ze|{Sc@GfP?hOa(&6$6ryoS3VTMb!mz2OPK(eeZZ+%`|&uWkc$WyFcu#>+U*zVSHu*u&wy z&dXV!g_1@43O<*FZ<=3%EVRw5b5&cd+z+e|6lP>?{aQo}xOc33pzJhdabwp2wErSo z}Cj|Pya^4}O^_pF~>4k-j@Twz2j*Acwl)@fEc*Oej3Z5Su6evn3XywTB zGrn@zC3qM$*hkua0#5a*f!g62@$!%Ea-|fjpjnCLPoiTV?Lshia-6XN1 zi8+g#-^Ts`hUE}=bn>2M$yX$saq;|$vhiTrR5)(KCO5bRi0Hne1-9lqS$`?+k`q`| z;oNK!^@aaVG#^f{vtuY8thGus{FS_*yvrdqdqqq{y{7#KK3iVmF ze&A-)ov3qeT^xoLK0-ynNkfZBADOHhrD>}>Ll@O^Kk=|mJ`nAz282KO?CDI6dZjRB zgMc(a;v@Il7oYlm5w8VN~~@CG2bHG5^EVgCA+)}1#1 zDX=7fj1|u|T>So|^+`rAMSzgC%*5-;iRnJq@fgq*%Jm>K!I$rfJo%lxd3nN$aR5df z&;VE+M%CmIlf1el*}hx_MketeeZ=|GpifiMs09Xy#)HTiz;R{B5U+@<0CW>Eop&o^G#!W_Kx*B&LhIy*?Lx=V~|K z*b4ROl`e^8!TPuQb7(}@yki}3Om+40O+cWxTys)8K0_JFg>cYpD!u*b(eL33xF5=i z(}8nqYLA)2exgq_OGUHVQ}=Az&w|@@;H1E#u@9OSa3*Tm?#wblVT)r*l{Veh_!;9J zl7v|#>`FU@eYj30bDH`ht7O-r6q~nc(6E~;D*p1fX5~(r^IW_g(u=b6_~mi1)14=R z6a<;Z#MJNxg)$pHX~DElO5WnWC#s*R7L6kHSjd`IM{LA+#*Q?-1i26k`4%z7eQAP$`GiDhV6ym13PcF&Nzr5RC7VL6=KyEa>fnhfM(PqN#iSlkRKeHaD#IMw0KK)voBEkLw?>X7C9u|Hm_kK~JV z5;tTq*9+Rz1uE6!W~rh(164#hlyZy!Z$)6)Ne`S5OPBdv*qPnJ=4jEiSz>>#`@6*{ zi&UX|3TrZxduJL;+WD5s!%`1VId$#^oY1M`V78{EAp+6NOgv~;Yj)uAhGA6*!9Wmy z@}g~cwG)Zh(!<1B_*qmYNmfr(L1m5 zYrp-vM!K|m3)LxCDj@~8a>I`F%AJCcOYUepQcCYr!1KcrfK6s1uxyuswL1c1<0A&a z&8r*&r7IK7%4Qr7+DuRkfU?`+;of14N77V}Ye@c?(K|}|-4|Q2(I98A=2`I|c*o7C zUrjOSVJbk_NpjeOg(s`wzf1bCriHzcL! zgxSSAmz`URiyhFL+um-Db7$cxa-*_$e2>Hj_j&*F*YU$_3)>uIn`t|_&H2FL%N3M0 z(4PsuZ?*L2uJyQ^)SniBwsPpn8t4Z(#k!t)SGn^8BBxL3-csHt!tR`stk`SuRqQ%2tA1#CYf2^}RD{q{o8wkcS+`0bD&ki1x*mXl$Q1>}P|e_CWe@o};%e|G$Zanfx7uhv;Q>-Dqc zBpku?`Qa*p*`d66UR^9_UVU_u)c+^6#*tJ%+wBoZctoCSqR$g-Wo7Ke`m2E_+clY= zW_DL@9y#Cor|hBd-dRTOQn}HKJ>0Qv!3+~@c5C+2`zWh zSi4Amx}4|b+|-gfR0zu!n#kFN*)@1ayvS4&Inh%sDEbM%p)3OMF%=N3cXje20t{A> zNbhHi19mt1NX%#dA)c%J70Cu%|-Tayus!&!?#akMN3 z^_ZK8(wdW2wFkB8)4r7B{c~;xiLpwItCf@c3j1^aoxB~F0P^M-AdPU|JQ1C5C1_av z#VLHb1}WWO>fZ`co0qPZxp6=7b_gozZ;NPT3n{ny@>R??vX-DHH{VmYov)sN>8tx=X*g1Oz_Ce zYo=^>3+IIx^VMH z3n^cIyV#%8ep2aX!;ICvz=Wh|vCz7woRa0Z*VijHY?%w%g3o?vK#PZzIG6m;+R188 z0pu+V0YynZAmn5q?;83(Udr3dv|C%k$a>I+pV@8x#Ix%dBk)k!`1_Ag(?WSof?&Ra z7+UwcVV%Qz0d?x)g7yw~}(a~CYfP(&P*!wKG!Z@DU&lEQAcF`pch|`#=%#Uc<()haU zbI@CTEMJwf?;Q2mfmMI)5CRpTyk}0XDvslIFMn^Oplq2oG?m>phm^1OO$5_qVjHfugP} zA|v8RctsU}SBl@hmQ0s3xO`3}>jzL}Ctj7gdL#gqaCo+!IR<+eSa$bX+>;cb^5GZp zesCl{bmt@Rh8O6TTVgnLr@kh*0kLeQi|nt?C&1f76E z=HuOs5n2?xzEj@pyQ6B6*K$dKeXNdcyfgEd5N_@5eyn;c@KYDdm~Fc;986{skkD~E z5+#zmrUpD}gX4zS~0Ie!Y4 z4EJgMZ1gnr);YrlI>e;8t;y*4%Q|@ zg#JorB24?jk+M;DZUXC_`v=Mf?zh$HNpaiz6Bvox0xV3^CAaG;Ck8GSJa1_lF4Aj< z7c@o^#yur6Iup6reU5}-XAgU(I0|ls9XZ(YFTC*ip&QFz962(%`s+Xb^JRbcssGsQ zZ`SyaYy8a^*Z$)gf3wDaT;p%X_`l0F;v+^+#~w8j@xQyAYd#6VB-EQLKA4<_vRsU6 zKl;o+{DUri;Gi1cikInveC;A`P`1_UOe_Z4LTGuEO zWu{Pw^xK{FuG_#I65}2=u~MyUY>9ObTW$)E9V&~C*eW}6TQlWlPqgfa__XJYUUkBy zqX%CFRvz|d@{n6Rw(C5Uqt^>-oIMITp+Ap(RoyLK94wvd%%7ZU*u2nzlbGd_TKazT z0&;Xq0@b>nhxNl`KE7IHfO3kKh6a43yh5++ zwNimeW`Mhj*y1y()BesHDlK(nX@0F74K!SJX)~I88=dmmZeaDb_oHmp#+zj5mLy0) zo7&zn2T0;7kdf@&wi)lg&uB{989)~=8|kb$4i_y?BoE$ZnR@<4EFa-t8_Bhqp=t4D zjn{$LynR>RUXfeUeg5vGP3v)L6h#7y$?)8d&0-ECRCunxlsb5uR100yHi4AZtPCd` zRQHcL`5syC$z3}{t*r_tD$fHbGO^XM1HpDnHRnZ_zKN4vnv=0qMU;efzg#k%u@s@& z@>r6+Uh#N#wDj{Aek%GUD{{$QMdx1)w(JZ#q`i6pjf}A2nP)D zxx`Lhw1E!~kMgJvwVHDmn-<1#x<9M>A{p72va@@O`;YOd+zOxUcl1iYAoX?L?RMrW z>itvP(APaFo8d_P3MVXKo90;6Gos((yZgi} zB|+9T#%Gh*N}IqVHEY*brzECq6^iS+d+7(5DEaOn3)~tTa4R^QuGGg;+qCn$yLK#* z&#*ULtovIO_4{Q(<5^??FnpYLT>d%&5Do8`-N{V@xljnp69?*P6Gk>jgBMQ>hHPzE zT_%0QdmOhKEmuOutH4;%%6E+HJF&Xgv1|OaMe_hl zIGk2s7GRk`GtK_C*Rq0laY{ zU_{nl2ZCy@6_W}pym#@=(M$9FQ25@c2>mLs?6bmZ9lQ6`toy=rX*eUN}?TP#piq&vt5)TJikqtPd~zm`#s!@7+je*les8gRE2SL%vNe9 zke7!lnl(+hjccX*S6AHA)YGW&En2brG3eXc3-! zMiAUbot+P!GArc@4stXp^_%5wajUy;sqK)wz_Om&v2WZ(O{mrBVKlt;_N_T{(z6;N z1L(67oxb(=*-`4gBkby&1)1q@%#GWIg0-@%Fp}-lF!?fDPRNt#@2nXW#x?ePGh3Sc zG)D0PD@=svz^SHc+}lv(WPgX~sEN6RaPy973yfD(wl_)0#QLUk%Pjc}O?v0k3n@VT zgAl76Vq7=YV9z}#;<_KUdAegQ>}aG)2mlVdv`bl77E}{^u7_~E0%A=oBbzUhv@kA! zb2|G&w@~}*DIPN)DkEeVt^TsHmP#eR`C>zFbZKSa0wa_M?&5rDVd>`^$quB8cHlLG zF;ojXc57?RqIMO%Jj(zLCajM>_b-i3zTe`!^+;#kk3kCQDG6*{r2?=Y>@yx_hwiv% zEOY|>d+~{EtLTQ2#Gbu@#GYdmr~mAIKEJm6opfLell>YX2Alo?`Jh)~*e^;~;uW66 zH)wEfDK{3gt7W7}kN&;;7E`(JFr~b`1VNdQi_1DkHL4v4C(W@%F)%1tpB)Z~Kf<$+EAa8U1d=ZvJr2Z(JnskD=W0#_ibD9n#sSzKDma|mcpcMcI4goO|9vE9=3(Bvgnr93Ooru-C#q}>z4xrSyml^u|XAHoBma` z$+c<;%SEdO#*^WbV|^VVM#%z=l$lFVw(Xs4|F?R;L?1%0zXwm{Qj*m3@2GrPUaNWrGs`Fqv1fssYh%_;he)&%N0+87GZ`5zbpl zQIXLL11*xuMv$HP7|YUh1EAvf8a#Eta*eIFRD%QtDcem;dlAW9KkP!pKXD^Xy41rEA&Cpz3U5kWK8S5 zf!i9U=c^%J+6=`;RZ1^$&|YP`C&_WZEVLP2bG*2(|XR-3j?8Sz{qCHY4G|pK|)Tf3DS3?5J#(>mY7`3N4(Supzlu=$BsHj zN4X3ZpbgCZuLC!Z2IkMUvxy1c2n5!dK2ibjza#DK>*%5Nd_@CTIBKyO-hoCHf zEx_5bN;Y!;w7#7yP)StI!(Y_oWm~YH^7N2e z{vqM;8lPlFPe!Io z4YZERdYkEwCEe}Ijn%8$m+;HkZml5`MrdV?q><>RrKv&%5Y5)ef0tx1S=yiBZ%*AL z8R)EZqw-kyBFgS{32@}tWG`N|GZw8}`CyrPe&OuHrX!noOOg)3W8G3<-%o%?M#@$O z9>tp64lJv1GDgQ61CoftP;X@}SI--Wg|r|h$xqJV8sMFsz7WD|YT6zaI0j!$j$j|T ze!rTJw4durRP$wWuAOpY;Jz0gr4JYe=Pxm+A-$-JeB1TVb)WZo105j=>=Wu|#=kCpv(?_{|7-0Vp1q%i5xV$# zp&%z&2?SweFZ&Rssk5mL4@|TdLNH29eTu%2X+NLv#nGdoLOQqA*Dxj~ zExrw6h<(AR1{UC}%lcvqj*Zw2>JC;<+Le=NC_mYuZI(~L%vg_hXH>K(W z2Cnw!{$U(^mZk0b(W`weX7I1&_RGDxpnX33SW$MAbX&CC9vK%mI|vrp8zh=jS7%arW!l5)XF)jkGrnhUB#%$@3F!>+W`ST zCiU}s_2Cpw4`ZAZg+h9;1hZr|sr5|(_vfbZWfsmY6{9D_+iOFXv9p{5?cf^c9X_Fu^$yKj-7Bjys}8*F9jM;L~MF0XQpg3&kcpK zHWI!%t`w~b36sO(@O*$CDc5H*22F`@Il5IxYNw$3ZH zv@oNcRADkI1YhJ~-SvI&2*)K^a}lbS7s8yPxu)jAqClT=U}rUI504OFu3A>(t!aVxUMQcI5o3tnchyX_ zGj$KwFHsg|Xqr3?gBns3X{grp@9`2x?~2)?!@I6wQ>bWU#?@RKWh=HjdxW}O{eV%d zU+5*#UAKuG-(1Rk+Ew@@r=R1>81p85yy#Madp?tXp*BxzfJLAXp&Td-;G2TY1Z=;h z!?y_Mi(VW7Qxzb?%Ngv6cDso&U1KQPA4vG=E=TjaGBC8C9^Tc-%d>HolIITEWpd-r z@U?#nTN&bML4U$$(SbD?xh;}COk*`Xm69*);*kL>6U*LX-&xI7Pt-as`&Bg`d`sK@ z#-8m9!hkjt#O#`sTO{?%wE`>UgNU1yp|a*s1bm5aWPJm^n-QG%Vy3j2Sgz&Vj95$V z{}{rxvV8xUT8cwz+pA??>s6#JJICxBs}7FAAI{p(D>RY*hp7*5FKWQvO`K$*A`Ug4uR)QBxmc0iyc z_l=`n@!D&-)x$&@i_V_?r%!!i6O_w@M3(t$aQz)_`uDf|4hDX*!NenhAu*pq-vA1AcOTg61m?wyiEai@c5Q>1Z(}RU2N8D{Fzg= ze7-Q>yjlXs28dyMq>7DaTb$AE!Yc4;?hfqXsILjlqpR>~Bi?-J*nyIegcR%YUwiD7 zv1r+zifIX09ck2DIOwJbj)ZAFs5NUQR$lJlv{Gl2sD4FQm>Zq0#G85c@U{#mrH#)K zgvyx^D34=b?cpYz91y!?940fe0fqE&Q06%3B*Dnw6|(@3A3XOn=4WN zL4={C4wLqiQu#k_tOg7x60}!MlGydF8`WVMNX-muKv*#yW*_j;v0lD3r;%FaVxd3j zJn&4_VlLU-&(e>G#v}oV$6R-EUE|)?mLt>(E1>GFx;TwgOT{d~eO;$+`+|nwF$REK zIi)xE1;k+!25{KuHqURBk&^;wZt0S)@Rn|;+Qr-OiO!B>`}_HR_pw}Dl5KEYX<&_m zD>Sev!*l6T+Cw|zmwsPsMk*a^r@NL$C;PW;hptt$+`>&>3|%|e;$gOxTjWV1 zc|7CfkTAve+mB8^BoUM3|+2S_vOpV_hlrn6CSL+*!R+tWKZL#RcgwCs?jfJ2&hqN6KzlD|NdO);w=5HJ4*ahzZ)R@{}5Q zAFC$ZI#E>CSXSfcUdt_LfQ=MgmRLI`+a5kEAqF(}_^Jo#J5 z=zE^rgvs4IznoqcgSTa7PoX z`*m+r80+!Zbuz*73&;F?CX9t*SioD^C+W|6;7VJ}n_c=}Qnnk7{GZIN9 zy!cYTEW3B)#hg;V!Exag9o^SFE392HD`E)02akk|cmuX}9Pq5_CY-(9$@m7QkNY~@ zED{*oiO%T|Xq6W8`j*1PI!Vz_Xw_9QH_gGPvMKEB!d)=>$3!jBDFl6OtW)SpQg7T{ z% z+H>by6rc_s9|`sCwViQy%Z2oe*Et8V!v3cF?vjhIAl!3^p6V=o++FMrET$CrUB@+C zK=P%XK&8qDN|@_SnziPYFbmoOq)42V8JZh!CP{g+X*T;tpCAc_ESEJ#AxFq>^z%(- zupW`Czm&l!_^hx^~oHhr(ZK)kFS+P#3i zas2kDo0`UHo0u(~Sn53rFD-nhooQTl9Wo$KmazuL8>u{t*8TX{Yk59zV(`vKnXwlPWS&z?4mH6Dm^Xv0U_@AnjRdrD`!d9T<^1+~{h8GiP!$#WN|^&L7u%7i zQ~N1Pi`O}V%@OzGgI%h)@=Z*yB=t$LcCOpl*XUXigDKyJI^=6`7udsV_N}m4UB?y~* ztb+>KbT;3ng-Ri|ZZGbr)cRo^_rLSx?sh?()ACq`bWPcvs{_+ioh78^3lx0Ix3KrQ z(fuwr2^-x)<%HVO_&bjuYVJDYmlO$_a}H?di>;cHq)bjd<#wc3KW&K`Eqkz$&7%QY zzXGXW77A^OYxbgGE4DWv+0Qy`$6Z4RE4~s%W#xr@_?|Q(N}f9cY`=agsb>TBw@+h8b{9a&cNhk113R8{3R&RG)=D?oKGxI$3i zud{WpI<<+|{jqjxg31IhSKpZodu0H`pHPyfWXWn!iXv^jKL;^SjV9Y|_COllWz^I5 zwTEi50c|{~UZrMghO-C%==dsnSf;T2Jow|Y(q7P4P3mqpYSTeS;;|kgv=vRhnc>$d zqvn!PCp$uY0E)P#<*zslDS}+9UogwFVbP$tpw>1t%u z17QoFDRXIb#pMt|t$XC#JMVmy$KWJVGqt8jE6X@+(uu@T*S4C7W)l7C^nu^tCIc*B zr!CTc#S6>w@nNWey1+PT$!vFG4&+0yuu1oR6U^iv$>d7{)RjlbvU~7JC{9Lw)P1Kv z*ddEhSKzMpip%Gs<7R4?&gfh}4O>+WktAJOMf5~Tk4Y5}_)0}I9Dn6k>+;s;u?~}p z6-O`k0NHI-U{tcR>nzlg!-?F1cW)tq-A=EQc~;)0)chs@s2V4mGZ z78&u1Y>$Y~`>6-=Ms3)hAS~vKl3UJlw2F8;v=GlVJ1s1&&glOOv-Ql^`MU&qci0ZvCw=yZC7zw?PgF0$NdNEWD^W97%?LLLI8U^ z?3|Lg(sA$}mq&J5k{*GuaFbc3FL-mp8TrjAwRJv}<=MNVqaFj=5v*N`82li$Zun9DUaZ-51az5yh1-vlI<34tb`fJ3qGmGr zNglZHv&J(y0wpPMcM{^_Fv)<~#z_oqhlyNNX!)`8uUO2r@~*?yTy4ECw2((o|AHz_ z`D95GM<;Kt*IYo*2rTNIV3y?~9%njx*|w`)1}lQxVi<)?33rjYoz$yH9ozwXa=`=W z(osGA9@zf7UJDj;BpDibmReM!-$M$Q7DrU_VS&K$ZEuU)K@6NqX>j|o?_aA94HSm? zniKPEBUZBu5p2YY;^lIr6Rv0f^Ql-MY=9Q;h;!y^u+e8-^3}Sx42*8vL=1#*qgxTs zwlEu^g25+dOJfrq^x6J-)N*hc#07B5p$ZAB6Vn{uVC_hlDqfc7yM+E ztZl=$EqSkmASVhurZ*9v*-ES6iTq_wWEcEaICylEY48zeInAsk$R|ReHREQiy>|l? zqv*e68;8u`som6=q=;wP$SoQ@v5#+DsSZM-HCz4lpDP|yr9V~HRAdl{cP1^$e({JQ zH~J}}`|WlHN>9}+PW!FGjzy-l^zg?#oToyWy*MlWp^Jq8J@Dbl&rG)8BslN2T`Hu!C`9 zojm_JDVb>4AUEIGMHvkui!{}vvvoVL*nO@PqKLV7cGTem`d&4AO}4bhR8FV)*dV5P z<;{CwK3n!)ZFAZ?hi<_scl66a`CNBG!BmiG+9<3LAIRrSpICEgr1o*jP|`4nWle{- z@lDy1uH)DXR*zPr(c$q9kYEkHqguY14*2+IMXxe^Mt(l!J)Q8`^a7azuZ=+ZzhFwvN9=Pdd&_e(-;i zDL49YRkH7bV=FKi`o`vWc6z*-w^ahorvi{z*}Jy;j6B0M_pu4ev^b}u*n%guw69ryv@|b|5nl{>Dhqm#6BuH;B+WQFw!a&&)4I+NumARVl4;cUArJMt zVqn_6W^mE=#XQ?hN3VSJfpaCDuri1DYlP=xkpS6|nu6tPz9F_~&5HrJ=xJo4vliKp zpp&54!?p~myTR&}=Z+LEG7B=VmssF{l`c6q&}pE_#Tk_d^8#V&Lq(i0G1j$clE%^ zKcR&R&bdPIt*K!9-3D`2Jh-n`Z5lYrS2kTZj~!GkZ#|t|e8c9CSe{iaaYumt5$yf3 zh7z-%J2odwC&Y-!=J?wq4ZZZONcP>C(FXIc6bu-ROBhpj9(W{!sMI3P7slO77~Ss(1MbD_X^#69 zwoJ7_Kn#JpRjvi*v=u=s87;4mx=hU9`#_!yv$QJ12*sR(cbimux@L3{-&_xcb?MOf zBFo=OPkZB!xAO1M2dy;}p!KnZfA-IjBygl<-Byd;g_(6PqTib9j^R(Scy}%TvMRpV zZ_c!Fa`SS_7s=)ZpT3(d96~P^MvH}}M7_P7(A+eyUv0tujoX74CXi{VeF02Lio@-v zEIW3grua%(4O#)$&@{Fn=?i)%ty8DvyF2E1mwW38O^3L;Uf~JQMRqLvsLEnpvCO-< z#uOaBd4I{I%%62nuX4!iogxf{v7q1rgxBouAj>rz6_7sU240T&$%v7LKh$4>zsn{X z*-CumZ0OZNma&T^K2>nm&GSD_Brgv%!-w*uJeE^1kS`*_yIa|0r0>-R=8w;uhIC&J z&S--0(LQcF452WmxAEq9r5C%f#Sus)A1H0=`wYAC2Uv8ANleW%W>em}yJD8qO5pNB$m!BL9Kw+G zuYLae6K(pfI8hR{={R6hbAH$veVBO&J&b+Y9qGMgHM0AbXMFb?k!5f4O6#{RvSP+B zHU>wjDPDy=GLu*JCqLe4f2diRB}g;CJv&?){OLgj|G39{&z8k$@*qj&RmN}ig6Ou@ z&BY)jm0c}y4W#8S} z7;5=VZv66%Hl1T1)Fri4gdM)UWiE*`dC*+f(VdIwoJb!j{*xcc@MJODd^R7Ja!`7@ zL)jePOYO*f)L!}BGGRwuO23C*;#cY)--+C`=?vlXpnqcs z%%;oPee(}%GrUO$UL7sj9sy&*K zysQxNzGU_s_;JVxfK!o*xjlDOxNn87W|Pl8E>O71wGal6gUCv#ej?^GO}KzMyGnm5 zpMFK(+E>8>Vx2E+ii2$wZ>r7d#wZ^XO8i*B168^V7gAO5m%xeNGk0^k^cXZ#ejh3q z!t}E*g|(M&sE|E)f%yzU75hAbH8}JzaDY&-AdN6UkmGJ z2MVW$u9v>flI;Ag)}*6t-Tz0@w!ysye2>SxlrCpaf3G!X{)X~#F-bYQ;1|%TyVYrZ zQE~Lf3GW7& z&kKw>g}$0H)GUnUXITMBp1CWl0sR{SwgNW=(eHH2Yc@fPl)eR@-Hlc8qq&yQe#D6`fWAk5doox)o*M;5tyd8Tf* z9XJ&|rg_})jj9Fiiv>AY%@opTKigd`^a4kr%Y0K(+4Aw{TwJrmFD%}c!YwqnMOJQ; zP*EET58OwX{r&zrw#c33&XsZo76hBsCY-N}{IO9NO$Iw23e$Ov1eW5D23;rWq86u^ z2MR}BxavsFb#nUlb<8q*3Fb(`{%=w)x8;IQC_76UKCgQdRzeguZiAgGTeOwxhYurG zRel{@ek0~&tn8@K064lNaqaje&h$lc(n9lngpW@p-ED~bgyyLCKIqbcetoPhs1Ulu zZIj?nWLtH~vo^Qz9^$Eg!0ScUS@`|3gYJO|I6W9y?L%6$S2)C)ZTT#?~f zP#))?43OmdE#~Dn%=F4T6`_*+Q`6|^T?N`eowas|3_lnEXtIu#*hJMcy`x)h58K2@ z^=Q_xA~yIPC=P9}+!1d_a%a9CT)iqWJ%BlY4{l-ys!YbdwxZ3oD2uzlby%6$T+~_? zFNrb6FuX{~SkL@c;O;b&@u;dXP4XUk;q%(Yu)D)=|8>l-dQUK>n^WGYO3?uiqd5Vi`2(L zcT>Mrcy<{NzB9wt_&xm53fdn#ElWZ!sp_OD=3fl2OM8CGyEh{P z16)PRyxU`#{Eb4x0aSc149<}fr$Y&*D16Wt^e+K*bmzb@(pS}B;vT`o4&A-;N#z$wrcb$^<*!7A+>p~cM=RVwtN*K zRipAI-4IB=e4!yw=e|x2M8>=`QmCd|2!vT`wAoQyerlI%9JgzaTNn!7OOJ%JHyhY{ z8}8akR&4h62562v>UGz-e>)BR+^Ro|g3NOa*SMIvWC}&?0AR!g@@&1j7QXgI2aU!) z(Uf76%V!oZxHzJ-?`>c#v@nz5QV z%#b;A3n7?Dvvr;GOYZe}JN|kAavmz8!(Fcy@>i#C&swGQyX#5i4~h%Y=E*i0FMimQZ#Sx2Dd^0QQ^wZPTs0uqDB7@G8O$C zQz(_WXhy%9()nlh=!ZOqk*z8TGaSzx&T~#rm;KMVGmiHMiGq?KU{`+U-D9Iov(o(%Q{b8=uxIes`Tnu#oxp9)bqG=cyIQ8;YhK%c@{iks?-_mF>Pk%daPmVE1qu z%x))rCAnfRxJYSd!C+>cb^yI;T>aKuQaJ!oL|WuX(-?2lRgMN7RL7~KZL1UfuNV9M zo*Tm78#dX05n0sdx|irxIn$;Kl}x@D$CI-Tz)%N<>PFd$tr;W2=`-Oz83{OHAa}i) zoTXMEK`oNquQbmN_#Lif3UH$T+J9r7U|#l~3){oCT!?soQt4*@og7`Yonb!BB7(QEe@uJ(gzw z^C;@j_HdMT^VLH8sxzSy-YxU&e3|t4a6@$PJh*I7a*@jO3~@Pc@7ajcp~-%-bLxE* z&#vWc9krvyRZs3TYY(mj_Xn$*ge$$fL+n#BC=uLouI%UeW!m;M$t6MQ_M$hV8h@HD z{&ExpN+@%FGO(9-IL=AtE6O96HSb2G#$wWnZ+6GQI-Wjget~)$3JG(Pu4r?V^gPZTdgh%0FEj0k|eg? zo1o#c4ZTLaW=JR8|Fd8OXrLNpk3uUKH!n{OaQ5eJ1XI3Bh_q~uDs4H3)bg5y*$4gl z@Y~RkQUTOI`pQvbG9JAAE4A-5NN{l|5qEzZGm7)YV`0wy->27*)=!`NEjX(g zZh1&}`vsHMN7tyr{>MRAra<_2P*h6Xm?%|ow^4Lp~)a5MUsj={>v}P0vXe4 zMv`VwKc47NR9_C}ytr<6emEr`pZnG>Hx8EiMasGT(o0G4yB|08y}ld=_b*OWBlDU< z;qMEMYsr6G7?S&_gQM<^+MFkc2QbaOAB8Q-&s0ziw&+563|Xg65a0Z{(?yR+|Oy_OP~!t*i)?%Q`5zq(pVdu6?N5}n{A`ubxnE8~kVJ--RJ zf!hMlUURL{s9wJ5sP-*CNxZIF)3XU-L~kE-S!`VFoW~if@Kb1A+Qy%w)!RDdy7;2S z!s>!vJ!XXvnPu6KfHvK{!*pmz&wGaV8I#H`L<}@+T?vnWcx~hM6fqlPF*p>^bra~Z z8g`l?tIDXU?LI-Rmq&M*k7a3)ceY-1PJ|8Ix!af6+wisfW2+dy$7%bw1bel!R~8Rq z{>w)b@5aPy#K_CLQ(39PtRRt zga&V4q_SUW%^PSY<}Y9DieC~#{Fh@YnDw`Ie#E{b%8vI6=PWC6&sB}=zH54L1{y4W z`5;MYSv1i?((xXLNjeZ!ff~l=`^~-7k{&ysO3DlA|A)QzU~95#)`k^DQKX9~NLNr% zklv&tC|!CBh=BA?=ta7KbODhL(joK~N@#-A&`UxHy@lQ(1m4U0+56ky?Q=i-cz?iq zkON3^wKZ$4nYCulIcJdaoa>Z0ql#Cg0#agvW;+DQ-&G5i{`V!jRoY)G_FRk#}WTde5N}kN(+{@z&{G zmxyFdj3NJ+K_xSB4Z?Pvnm_iZ-qZGz(V%DPx#ERuxk*m2iX*k}7T*OFsq!ADIH5Lc zYI3n#7EUVzm0AYA$U^mk4$jwM(=G?T;>N)}RPyQrEVkvjd?`Ud0R&gQvb@PPD#dqiiG|hYZ zAfIq!M~b8jL1SmK9f9k}Ls$IdbeUvS^rqew>PfU;41mN9pllBgGcYjS>cU*ld`w2Q zhpp}l#iXPX7aEvW&nc8=0Vd;ylh3m>2MMs`}yX0 z6Ev;C6*}~#IiipqSfUIMyW7<&(jtAZHs?*6$G7XPpo%ZL5m8%=ko_6uWW;=3cCkM; zc~yqx>{XT;)x||u`m+D-g{S0*ck-kkcH0J^Hq>giP>q*;`Fx`}9Rg3iwsoxb(rUfa zL>_t^ppvV8NWDeWV~i2fJHyOGH18g3R!`BO(2r0~m8pg&4SeTGiG4VNcJIq6VesR4w?Lzsz4^z;jdsqTr!!f`;lf~}3O=n&$-Qms-2%dq zk|KLskFh452}vLl=ZGfZh_c<=Mn(CYqicy1YmU$sJm9%6c4jP)QoNhzwM^%!Uz9o1 zEPcoSz{C_I@Jt24bJS$3>kc+%BTjy8^D;UoX3Rk=xVj<9al6@T`7=H(8%kh^AY*l6VO0eqk?hh21%ZZ>pMQ-NT=_EA7CnU0mx4={?)3XV1L0{VkjL z5Z*ovxYl0{;*;yu27$ zZK)McLw5jK3&-{PcI9M6vh8WEMg8T6C$e$t${hOOWb-g$g|p<*~~1Lu0aw(J$?))R@I4p zVf!c!@a0ds&RRAICDM!{yIq@)*BwlIJ{1aGk!?PgXd~cSnSoXz5pbMFj zR#a^O(1UG;$&OK7W$!b+cS(iNhQWI500+4OncnR2-rSN%f}+N?J2D)b>FUR3*a0FYpL2o`TghC$lWdS&(yr>HU2Sj&22#e|l z(pWskQ6gJ{J_xOFMB>p9H^pI}xQOt}sn{NLMDf9wI#%u^}9w*Aap6 zyhmGc4V${~-;fl-VT}W9j+*i=26VHqNq>BoI$SinS5a|2J%rMmMW@1etn;qDn%RZS zR&$Bf#*y&$;jtR7e{e4jiyeiFz!hDfu&0ARGCX-^6AE!pvaYi{P8#krJPh=f_e<^Ll2zXL0dKJ>!A|eH3NFmW)B7}-sh?D7OZy5(%0kBh zS?j#h+=sO*+{f+ry~ne*?^7AtCtMoI{}N6<#plPDxH*lDzLZe-CEL2mFB7pV)-xDI zFn`=<5-g2q1*f1+=)!tGVDg|)}p=b0FPD+ zyW`>5FIMwquONExy`!#or;vz+|-L4{Ev$ zmki23{4AcP2zRp06tUv@SO}wajZM2^o1g$laq12_hareJZc`$jpQNzEbV zceM(09-ZdMsrkL-S1h!to4fI|ISOil*RLpu@$Dcd>dWh?ti#A-Jmo9)ejt})X^X&X zqZ)4C`X(};46l;PAF9#GV%I}FV7(5_-t+m;i1L1 z0DR+kpziWRiVdC-U zodakYvB!GfsCgb1tZgWX<(yj5^bL;2QKyd9OC{U)&Z~Wvj_!G$oa=M`u~XsFFoGI01!QTa?M;QTRq7YX z8{B5{IUVD#IsKTtnC!@%IH1go<2lTPaLYI@srJA7qXwFv{%@{wD_$A*|I8iqoA99V ziXS`mf8=)j3s5Sq^c#}fZJ9mzr_BGp^1lu>zxKZ$A@~%4a0`1^GLMUh4H z$47s>UHk#aI;EKJcEtY%c?AmBh4cM^I{4#lbQ~~h94in{c>boX{x}LCIbuiruXpcYLhpx>;QU;2Coa z#DOGEmbA;R^cn!qrxmKvx7RbZ*Vy=cMck2qK_vkXqhVP4ypq-KpD+C6v7gw5zKYz_ zQl0Ju4y}Py`!t$6Rlmj!8Y;%LOFatkfG%g+o_622sgJJ(DE-VhHDV>N_-O#tRuhE) zVB{ZJH~cmLezKl4(>G5!l;6cI)ku_38)=Li0$gFC>df^C&-_@f)5&G$5lgE@VK7jPzCykvOZZbFtzNKYJcL{0RZr=fi(@ zU-|fxzZ;wX?!MPRg7z~+ex2uYor+(4`Q<(ZuPVdEwH&M-JEq7uz0fllaC@kAJ*bwT zI2X8%=1AC5)gLgszuu0!0a&}7iR%ZGklRc9a;EB3K)%E%5)SLDu70KoKp+~Q|BxXk zU?_VA+)p>&Dx~5c`La~JfXn=>Wn2H{$Ua;>8nKQr$tDUz@Gfj#hjOv7xwVjp4V3q2 zj6bdO;{`5;0A#7VS@@OWA7#|?yJ@K4GPfbIn$uhwGZ6}4I50f^i?&XjK^DMmF1>mE z4k!lsKeYq?zyG*?7ad^K4hE_e^pHNE+RC9EV+P8RCW8rz(|*eV?(rWI1^_ZSs10BR z?s@%-{*6?-{Wo-ej_A|KAIQd~&M!wqP&+!+5-E};xy5Cx|@v8mVKdFkM z65!k65?!DzJO9;|_e4~{SPAC8qV!M9gYT<>k)#3DkO%npngKrxz+p#FJO?yleCI~X z+CQn8;u_#vxmru01+9sHvGC#f@dBo8f_mnPfcHPh%Wv#AC(N4W3!nujKvMu7L58y6 z-#y(G5GUMK*QRsE|x?gKq;cB#qKy8pqf{0stck(+TDbC5WEzxrT3c| zfp@~0?VXP1cI24l+v7Rl!A*-{a0uALE~CJmZ2Zr9yvm5NNLq z(4IFJ&4b@9zBQ?9ClTwbNw!_Lk);LK(Yj6#Jpiz6p7fK0naYUQRH=3iTe@P|#`4By zC~n)j%tTs`RSB2*(0+Ml!r?!-7qp44j~|F@VH>(8PfAzr>Biv@eATELWX7Pks?DRp zcsZ7U!+N?^p?Xt!?0QjYF+hpzvgN*FYQOvuNW_Do#$dkl-zLy*?R*IH?2eVO&Hi-X z3kxj2jV0h{f<(}9cRo|kFN3Cj&|Puz!HkPePE_mh7a78wg{Td^4>ddwVq~plsl=hn z->p;=*qYG|fE#KPS-lVDSud%Tt9EKIg!vrJUL(<-fPgkfN0j7sI-U7`i~nSvB(X=o*Q=wOeUdRY`srpM4NRsMoG1{rrBIAdH&A|C8FHyt6~ zxGHP2{xp~Zp>ldPj)2P{lI@x|w!~gv?ij;x#N{BDsi_-8&|WGNjYjqgHWNM@M50S8 zzRl*e6TD&{F2!ZDW!rQ}O!cH;hFBoOLZ&OBbAa1ypn`iq0IpkOZM2CrRHfF5 zX8HU4X|ZHbNfDoj+P^p1kbY@S@6FlO{EZjwGE_~|QhmyQmP7Al^IhZw zLDhUi_E?b7RB;Qvs{<>kYh?#Ys921kqcMIEJ-hm!25xis4b`r8KrS_ISakO{9mLU; zrycB46uV)3xIPA?kQdLSzS?fY)sPU-`JBv+V{4G-_59OdtxKWBW^A#;LZ<~br@?qd zDOEpYE!o8ZSu>uinCwi#HikO_A2z*j#hc(1{&h5MRjS&J31v-tS=}!aK|OViMD!b1 z)QJ!G$G(^N^EFc+X=q-Ve2(WZ6kRHRj2fH|q)<4Ga67X=0`>ajzD|F?ze@g1Miv4H z|8Yg{ax3GT-GdS3$xfOT%|U~!ac75^DUsuYUY57RRsJLS?>I__nf07F2OsZfY(n24 z2;|CQ0VdKNfxCZSngIMF035$Rt)WF1y_cgLm$(eJ5Ls_|a``qbrYEe~rv zb2)oW%%3GqykcPdErouxl)R81#Ch*1 z^zmul!crnq&(=isEgooXIo%p&f|>V&m|!0X#xo(4B5zszyr#W-a2v^C9ZpXnN>vTQ zQdYwOM&->xDdO_)yjI_3A}L*PfnJ23{U0-|RtImQoU_n%7L<8s??MhS+w*w7dMmvb zo3-dewfL=CwCL%$?|%Os*Nr<=RW^(87f=VF4%^XMUiy?B3@8lpb$mZtg-2ILEp4k> zD{d9SPI?xV%(dmD<^gm~#AdyjF?K$jV-sNVI8gJl;8`rzL1SZRzx(`71w861(!cG+ zYunhMn0`?$c2h9~3Y-Le%EaHBD0>q9w!V#XcY@ycyqiVw`-dpevpVl%ltRN^kCM-i z*gceaHW6;?wy4+19P3v72eDJ9@yZ4sa@MH|03n_%1pa=h3}-;PucS^pfA0=4xZLXn zP#q1sM?h+{m&jAN0%C+8?S;WKn8B01b&yqp=!u40)o^w>-?qR|fSEvt&$dzaLq`k( zOJmo#gv0@}cy++8q$86N_o8O9PfU3;JF1h?9^ttyQsQxF8EQQ2t~Tmk)cBkDr)CBk z+3QNd|9DOMQh^_T-(}Lsld6N=Om1Nx&YFy9i4?ZABckE^l?dh;%z3)ZqMSBw_iE~ z5XEgdUQ8x3T1!>M@RBO617= zD~F=@p!asV?>&#isY+iHX|1b_ntyC~I`Dxo4X%9F(8_5=IF}5RS2v_*_E3Lf+|>_~ z6Pk@p5?19qzcmrqxoU+J3&aw%`Pac4))c)H4m`teaKb{osXGrY#dpPB+- z1NQ{ZRw?1jZp%q#3fm2a;CKs?Bc`I6;>g_c;$CXgQDx|4`&gP!>T$O!Tr-tr8fZ6&`bte z+n^c%GKMXW8m$77s(?^=fq-lTb^Mbq2oa60FaK*JsbfS}b#0k>PX|QHzykWw<6xCc zlM2h5?8gqC7d@W-J?_afHqi!m{WE0LiXB zSbIcnZwG;0SkgZVhxGo?A6l5-MtkksBwj@9j*T}QD>gfU{K5h1uWqpIWt?4l0Ey_J zSUa0g#9Ez0dOdcH^7LpVWDC`xUHOr13v|{9a9Z*A&mM|?1Hz{7uKbXVvhsS*RLr(< z2xM$mW4Ao>TiCao_@9-tk6L{Q^31+6L&UGYAmg_=uZ8x(rZ%Ib^J4WYOhUIiv>4_x zLvCO(^TYDOGI6`x^ltB?b7)?@J#K9OYVQ#|0_fR|0A0ofV3{yhs~qt>oINiMF^ocU z$Q9rAV;6zHg=50lZ_+w{622_jN0s{Ezz~q*+ne+Ppc8ogSPvrts7oxeQbTC{?M{j(2-(e-^0vPyfwYG)lC8D9?3mRHAEl^|rZz}})eBpa z0dr`}(WY?2?2+4X?fS0tOI^RMg-GG;uNh>Cicqqz4~K$YE_0C2}O)jM15BpKZNn6F_8{ zguo5S*6tt|)Ks2nPc@!)Z#{@k##(%k5;k3L%NY2Bo?e{GPXP~9>c!P->ocz=oY9)= z(k`f2+=$^P?wuB+(h0{H?$W2jGLy#gHK$+`2f-Y^JR{0Z+C?6eNT;rd%>${LTB2#s z7q?@pX#_fn(Z=#cYmpB)muUbeN$6&!yp1qsaMK2(Scn`=+Al%kj7u^Jc~JI8m%gJH zo65erA)YgpC01Is!W>GzG6G99c=~f1m@r+gDgGa?GuYmIo#mal23mo(MEhSuP46ct z2_C9XJa8*TdBR0R?X%a`vcmCR!(U;GMKBj?88WcI@rrsvIcp$WRFw3k$ALM(bYbe# zZNucu5WJ0CQN(nZ-y!@4LUp& zO0|QoUBVIX>e~cUe9oRiO15Vaoef8eam?q3htp2VXIiEyZ|l7z2v23N4goYx`>n-* zX@)B8+yu=+V`0n7@bqo}RjL!mr|*Y^v5p|J<%WJ8NE~3kF^4(C!09 zD&0zt|E}u%rJd?~DZj&n7jXO2u|qif@HnEHe^)r@)0gLP!4(CxMCtn!|0{)gM(uRq z<6-ayuw!=2c7`7=d0m|5d1Y(@tBQkGGFsPG{FVAEEvoz;mbO<}@o(}}aHKv)?Gs`Nd$3j-I$I`iYLFeG!Y zP@&WAw!C{UeU#gIlCt2Yfwkx=* zQvV?C=_fBmgr8lPk`kV{3hcREmzpPlI1>C-{{%q9=6Olj{JJzVR-XzQ6+~ugsnzw+ z>1%_*xQm#!I}4e>z+%0&Ww<3PP`vlZVog6wVh%8UD8&M#ZV~Pmw8jS6Ff`Cr?`h#F zrmE+sAK@c3|5*yP+kg8-Z<1Nf-fax7^=Z=<=P@p&1Q#rc3u=%7gPdc zcj~v_0#r*9*zUzpuTx3MTnZF~ne+Wj9yz9TyRh043~&{U=DVg0jWqjgL5oW1*Ct_? zC?8Zm|8ZHa;<4fnY#%pVFI^Oe@`gkJ?uKrN;@%g?bz-|ed}FG&O~_Z*u?NzgM>v4% zBnW%*N#oU!JNsm{c{dl*ys5fmUU$i6S_`J#<%vFS*lg%!IodxDI_i(|{gx}xh>6YB z1aF32?7P>(Ofl*Py~-hVzBomjaSq=pSN1D4b7xk_=w6bl!|@VpS;NCD^u>IHtllB` z*(ndb`+|JKc6`D3@;A`*YQ^Tl&!-1Y1iC>JlCBqa#w^(21I;dRfMrLwpm(wKW1^f7 z8h#=YI z^|pPYwfW7q=zUPius-GD5l*pz(-+%Cb|065UY#8^G>8LW>3X(pjGgbVWZZsZV>51h zw~K$O$YREfB5(OAN;X(D{*Bt2@8v>iD7;@UC6?bEG2xybnVynotiJ4|SH0`rh@FuQ z#hnq|UmWo@>kA15+eOhOC)&CLh^Z~#4tiv!npV0`KFw0WrGW=n{R4ow%3RQ3r!A@= zx!}Ng5V^pyTiDA9T#{Ms5%ypMtjC0lb}gJ3fm<#`33loH()_69+|*ery!pk+TZp51 zFZZ(e+-?gsF|n^Paima;0r%2g2-py=-iR6&Kx+v^a0UU@>m4 zawQu9w<1tsn)=pLL$5e(`WmVg@NTTB`(xbpr-1KEyfgbPjar7j!iKt9`tBu$V(Xin zp@)dtpKr}rL_-=wrZ4l+#|sV==QTm$i6?NxWu72x8F@L5JUg{nsj*DfMwjnv_@Hjm zN6HtgAxfKeYYa>GaJv&?K1hOmgrY^7PRw*!wl&RcH^n%7DSUx*7#V zUcBI3wWy{5AE+~irr8XAb^igjqyO6RQCBzmn-|7E>3I1C*_W3`BW7?xNaX>Q1R z>6pX1eL4h%{q9j#b6kzx4d6>n6-p8II;b-uFQ->dc2YYyCT><9^*!D=6xhNZM;F|b z)w9Eb%J-=9h*y9TuGR>d1Df)XvgVUWOzY^8pK-cRK)>KI_ub@`54d#Sq>CAwNy+0e zAPdiEh2#4Q?ZSOg$6~c@x^Ng=uXdXYH|P3U1YK`srxt?SFbE){kK#R)_tmyQKrWr) zPA`sx72Du}sjd(bo`hYBW}Z!03Rk;DxJWpsG0D8pmsWcE26}#5#Js*$I^4kfh9LMz zxA;_+2&wDNPQwo>;x3xS!;`YtSVegxxNbZXV>x}kY4`Kr2_V+m5adkQ>6D^Kmmr^BECSc z-lz67(r2Y)dcMkv+cK$Od-qgMZNh!}_P%$UzgP;Nsptt}v;^t6lnQn>u2^vp^Qw{6 zqaWk^R#QWNetA&$#32Xg_P`2{r$TO~T41I)l3k!|c1E(*FkdV?b6W*)T|LH6_tAAq zJ!iCQynq(y6-B~ z7^}=X?cI4z9X7Ch*;c1)<<^)|?`XjuW1Q9*PV@c_f!57qjnLj>=_c4ikil|{{C1Rz za;auXq0%Rt)t8C-xygwEJ z5Baf@F?vm0d27)_@}e+?Ib`ygghAmm4FhgQ3bao{jan3m zeKvSwQZe;?{HO%=?!)Y6Fou{Vg=yM|;3@xy4$=kU4ne)F1ry{cT|iPTxaaGbebgq; zM)nIno=4LH;Pug}u`@f_>m!l0$oq*8m9%BGkToSdcR_(869!jgXS9jZ`7K9PDnm*} ztj|w4{k>GS@RVkGt;W@Hv@x1JiKWHFjYvfR94Wk*=CPJTKox^p`6Z^&bf%}YD&p$7 z$Z*Z>Um9Q_#qQvE^2*v!b|o}0-3i_EO3W>CI+DFEAzBt*I|{)i#BKKpl_WgZdB2w& z|5U9tr8h>Z{l1ki_eU?&HeBoB=jM&Zm43bV`YyIz*-a$VHj@im<&)8$wnQ4=OGw{W zch#2k^|E%8I!^l(?z}Z^=R89%I48JS{r;NFmj7XiAuM#XL{Hvw!e=C<@LhIpw?*yy zR_g^1X#%*Gp*OSY=hqF&-s#M)=yL_UN8*3JS_Pnm$NJ`bPW@{TNFkC*U&D`pPQXR8 z&^eg{f6wHr#I`}`8^@g!oq}m_^Q#!)fLr_oA&Fq;evHn*kB8#v*r=2kEzjrn5cC0@ zvw+_`wB^BggDoT2J2jU7%!`e)J2~j0M7Wpk9Tn(SZpkL*qqFn7)rum_?)@juMgZ8- zq@~4Ai9>VxenBQt%yxJBU^U>?w!pQTMyOs2L|Cj5SloUoMlaL8;6Mk0RnwG2Kc-Rz4Wwal5m)*J}JBRu^Come6 z_%8z4IriP=9Mj5mo*jVK@9xG<2fua}njra=P5EVT>0a(Mu1<_&R9{=zWy3y@J@7gtR_TC1OW;RxZ%dLi4!X_`m?>?Ph{NAoD`aneYb?_XE zY$6fJ{B`a3YD)`&%6l%q%7udER^<}628s-trRw}%`aP5a<`vJvokwq`^p)B}WEWQ} zIHFM#h)||<-6Sr`)Wie6K<$h&&tI+A<~o`QYB^F%G6HvVC)y+Vv2; zL2gf4XuG#qszAVt_9-63pKH{w?>Q6B`_^5)gdD(muMWnrc%?NA96JTv*#2hkO~ZGd z^WlvrdW6NKSZ{QX)tS-yVMkhin4Q6oYd49hTZtza4rtrPPZPvbypX3pBp+w|t_Sj3 z`DhqH;%`NS+6UH5MtfWYLO7aF6keZg)D#FZ>4R1VP={0InVHPJp_I-{ynCm3?||yv z&sk*S#mcYud*i3)$osympF9_l(!6rSKAu>v)8Aj@^-#U0?#)b5d>MvW~qdiTm) z^NZ)MbxNMhzQ(Qf2``Y}y{!3IC$ZbLF&afap?6)z(AyDZWlkRzf!$#VeC&E$a=A%& zMJ%t?y?xZh)u5(v+Th{HwT{rZX9Te4a8bQCQYtcwn95Ay24{=X%kg;owjAg_{tZN4 zjUnCJPuJi4@w#%|pA70@;R8ASo(Gf{7CzA)ICzJ5ZaQ++-FNi1`_dE^NTiWXttrE! z^!A0`(hz{Ty0bdZseNy-`V=f(wKSY#|8(3j_(zjSQs0K=xH@bGt2gDn^HrQQx!$qi z%lG}fs*)H(1Ft<>&!m-X%94$t-k{d1Y8)`-9({0V)S(46GkvAX?0w;*_)UN_w&?oj znD)oOrs--Bge8K;N;Hao6ucKUI_Ra*{Mc!^FISmEa^gA|Or-AmwbbnZh~0i~Wc%e> z?X;rQ6@PEcjo*5bp>!-iljDH?>sv!FG7Ig$=GYqmw8xNsTi@YVuW9c~N{!n^`5)!W zB&p8&E;}fe_adJOh!hj^qy2fc8(eIPgCIAEXx0r|Ciqbe_~mo!#h$t#UxUtFF|egk z>g*5yeRzWA`W99b<*m6|fq4LG_;wpKK7`+eBN4sIHS(>}< z>-w~JGu>e~e~|?>pT{@4JIObDx#Lpz^j4HtE4Ek(NJ*>CDFwbx3x1xSzrRQ4eYR$f zL!}A~=UfQ9wj`aEf4NWmh@nGzm3qzba57UT=2zG~`~8-&0B_qMGUPZNqd!9;^|+w# zMVINX=&MIx1>1H3G&W_NRNt0U!;S4mwTZ=BPKISxT7kw~>XH*DyA+C|0>Z4vP1d$g zYX_G^=~pYkLX65Zj`b1uIT4Ezz27(k+-D5kGOLzL*fJoUGsBruR+O-Geo*U+5Hu^Z zjQ%D;;K=PrHgvi6Zn?w4jb|Q-JkPxa^~!r*L@xvb6Mm>Mw9@&4S%FTFps~4cv)Xng zG4;6F8>^VsKxOD<5Y$aTRZr=?$!+s<@OAjIC0%K6w^vejFVkYozs&=N+jy-6Aa0*& z_JG_Ama(wqSQ$$C$kC$QH_>MSFe-t1Q5y|aG>2jAg!;D%;^)YdleK--<^_HCoit%n zlgg~fL3{}-nTd+CE#|Bou#ZRzbLt&CawC^-Rc@7JwL-Ad9|{J0wbq)6RMH%aXSZv# zhkEFm^ywA!kn}<9@=W(+Z-bcloagGDR#M9R2|(Hx6~Y8_YvUz)X42tu)QFeS*}YIR zQgY%p3hA45(z7*h6WLQQa=a9oKf94ZeQ<|b1!}@f>WM@;D=xwj4}>+_ABb#NG%ko? z6$ZP2xH{W5pE;>Tt@hP2vr+@PiLT&y!7 z&qH&AY~FB%9mIqZ9!#vojK_}^X%TAI+M#CxlkuXu*Hiq9AaCPXmd}L`*OS;ktgp5S z5z+D~0QB$dt<=Y=H=dx`bss(8GKqfQ<;N@WONt=;aK;BIuv!clwX(*V_Qq*Xu4l4T zfiT4Sprqd?Eg+Z=zIVPD*k2_%5kyF}_?DJAjOf(7xnr-Z`W}?@0||%23OwD^U>Z56 zCmTcwRZ17lHk$2`M^srTO!C!Uxe_w6Gnf4^lhAv#XhJ0jsW^|xa0q{`d8Lda6~$59 zBn0)RO-wa(mi5IOSRFFPSK_Z#Bfwdso26#FDk03ynZ&kqb<}x^$eL#yQA;vtH=oOb<-m6_cU!B=UjW*KMvf%%u|Q%L8&EMgftq)QOHy%d`0)}DQ*V%a zE>52i2kKott6m96a(xm60b+?5M59rNQRKB7cgD*+D^2G+l$T~aw>59By`i+vm2o6_ z4Q)BZ_rIBzUhkAAOMZ1TOSi5`tAo8OCf2B$H^8!^AE-xBoNGlHo5W7s$Y?9T84rUO3v`-iP2wpD$unkya{w|@Tf(qwT%%oIVQml_?Ico1dVTa#>zP_kCj-5R(b6KOQ> znrwP7EtaEOK0Tb@CKWjtRQ|=*Kw%G+dXL_8`aEK9stvZNK{-ZkcyG*Fpgl!LEg2&x z5(5%yBHI-m)xX<8!lpZwohTn*VTH{eG=1AK|0WX?Hi-Xap(`qpzgH~hYRee#Y;c_E15dZy_{ox_>d`KcoPQ3z4Y=WS98hBmjPn+CyJ#VW0z1)DT} zNf|(T;-4jPLh*TB-Cp-y&1A0p*l%gKeY|?_lp(7%^hVyp+m1?Os#gbJJp8G?TEQPO zYr6kMBVYSndkvU7ukp(@c7`3C;(lZ1d${#XvpBC0z@1Ou!mI34NI-6Y;%w8&*1z_-lJr zeo-p|iSHgVcu&x3+V(DnAfWdaZ!y5UIJrlu3GA4y_S|`G&mqfJg_e6>jH5Ku`o>+6 zqbVw`-{x9RgE=3Hc=QDZ23%RF7GL#e5Oi8rXC2^zRJ#XvMqnoi2x?%v3#=B%so^RM zDc1BjBJYrDi2kW5m|=|9%K9gRr%ebkXbHQWXz`?0`OvlKm=p0z!C;y8jrix#Y{G#m zPGcDXU-2uP_p+?z`V~+5`MLXG_pXqW*32mGkscpXCaO?@)+V;4D9e|>eFW-}zH4QjClOT%aeA^ft$Sj--|oj5XeYq% zuI#3PXGy|?Y*_iErxC}3pmv>ojSj0*`NdAx&6OPm&7z~+<(XHuX8;ekEdKo0hDob; z{0{R1lmgqFdGIt5D{0H(nEt7crFvE7<+q1L`YVHW@6hnQxfH1*zs)o#T0OD<=FhDZ zu(@I&{W2JI1hWx&q!=RG2oEA8Y#Z$%!aDQ>GD~Z>znU8Uw(X>~`L)^b!m^D-G+nIW zp{TpkJscG2ML(AH%|t1gD|RrROXmZ{`cAr3p2-@tDHk^MB21X+fsoBB>!f0&%j5yN zbcTEH0GHIa^4|APyViB`+<9|6MV^Y?;6S+HQ7DD{QNuPLgq48Y23ZqtIaY*`PkYCu zDpi4h8}xbqx3h)s{4}q*IE;V;@GBh2#EnMq+8}sj&)wDw^1d|WD)P}b*#ZL-<#X-9#ZE^;IRnJ$0NXbl9@!xwkzy!k`YQ*?uzPGDm&Xj9+NZotk8MXj z_VZYSXzz$UTGAy15#D>wmD&07Bt>w0WaW9u`CczIbK|V(_RbucAk>}?J^)Camt%c* zSSWS*DzXVnzyHA#cP7Sk3q)!uEeoAR%oVl6WHRKnNn}pUjZpo`QmtyS2WuDzi^jt=TQyYd!o$A4qlb z#D!?5>BG;x&IbnzQAG+TuMVb4^twJV9w^)t6+={Gtn0+iR@?EqZnwcQt&ugTV){IN8^eEnia$?5M_uE5!R|5~KjCSn3{qU53A?+r(U`5`+ zi^oM1?l;LvKQKA_abCQ5`Y!9+R2Q&&I+9dw79#mZN+2wZ%2z2mg!puJ zN1^Vu5rG_OkILm@-09(QJ2Su!%V*OtN+vxmnTQ-B$a{EQp$_S?HEq+mI~zc9-w(g# z16F@Fse?|sl{*A&MQUCaEU==i&lQn|K{yNnEjFfKoJ?Y;9tNCj5oepjkyg9U zvl{p0@b|y8;)3ecOAUUy+ivjkJ$l+4urgW@$9${X@2H?DP4?fdE%_C{_d)FX`xx|1 z)5tJwv5R`RKn;V6ND_axgMq2)ZVxE8bdp6a(T2CaTE&n-S!Q9;pN-HIGqa$o2m0`n zzDa)TO_KD-l4(Cn<^@Be+Q+3C&Mn&<^VPg@vEV@t@R)udS zd#4$k*TV}n*dAOk%la@i3r5Vgofy!*0tVhCbJQyn$-sDa5PR%kP_w;g)+ROBY>PAN zssRJZ1Fq>hXz`Vou3KKF2*YVftqj3QFS#a9*YK~(J-u~1)>|l=T`G?I=`jCO=3`aN zIIYRgu=sfw=S-1~2Lg2r_cFx=EXIq=g+x50$)WbdjGKTp&7L2RQu6$Khv0DtwvO=r zd)r?Df&Nr$uftwhwc#_-tG>iP-kh!vA>+2EZClS#qTQXA>iGV0j<8E31lhs7w<-A|^=$ zI#^>Ljc1<6VQpWt^>}g2hz$ea&7zPVS4u@Boh#L~2*Ep}E~PKOWyg_mtB^QQF>XyW z?y5wgC$eRuxcwPB^zVK)0LDJWqMB18t+0#5MZr5_wG=DVnfK}81R6-|Yk+U^lGb!a zrJGB7W)WMVnmk!8hO(-cA&nDcP|00=>#2&d4cJ)G#5Xh!C>EkFi|~e)Qe!C-UjptP zy^Y<}MDQIbt?E{D**3#Vi8qpdq-aIOxrl|Uc5pVCq zZ6ajL@>JU~KH*0UtScs+kjpO*fZu{V6_ead&bgF!O~@i?a+pG}2n_RE|zSk*q#@y)yhzn0=W=ec9B248rb{XRj`Y?jYG5~vI16X9iz;v|M<3JS!>LseyaX0 z3YM=D&z>+HnC8m?^WobUd`24Od`}`tQ+0d@sbS3hwjBV8^OsWzfVs`-ZV9Cb7n)bBMEhU>s|6!#+_eS z0*%#eVrU2#WMJhlNRMWf_E_&54%)k8k2XgdJ#?fhZjd(Gi`8kfX+@H<;ziK2S1OA> zAX{^OvO-(1aK#YVi?IPj5>9Qa_~suAN$6gg=-Uj5(EMTxI*&3)VltZmw#~fs#+~`L z^b1bfo&=F^W2Gw9i{ShiV}h=ExqCg z7i=kz@Co_VJucftTHUf)y?!xXm3<`2l-+M1UofpRf~GX`D+|jbIwc19J9rxIhkWAE z`hDp9mrBN+G*3v`!TQ)(uT8ei0$88m#mDwLH`eb)CaQc^@H>N1u2a3x{UALe?i7@L zZ{SHn9rKl;a=qG_u(v_GZut+X|1JSQ zAT5eAvoe5x(5GDHUh$0So(pg3dH&a@vm*Qru*X>ER+&@hR>KP=kcJjE$V(OAm?|wi zR+Y%?CCo8LsH}$FAXScQA(EcWTCT$GM~U&E2kv!$6|4-#_T&ggeAuoCC6@5I2elSu z>ASl1BCtuyO)PL%>ZCeKjLtGrAd1wB;AgNC!MlVPdVD$!NBpeTNy0D^L|5*0Zph?x z$V6;Qgya=G=O{XF#Z~FRh2Hc_2k(1up*czbWBiVYQRt;=WC4w-ZRUN}Ac%^J3H`r^ z8aNs4n}Q%^`__)AGap;cd;$dX^{q&@Y%rh=hnQYhPfBzz#!oM{=e|^4T&@}PpsqcYU z8ZF~5HA)aW8`9M}vNe*8?|8oYk$pC)xIum3t?X`9&uoR@g@4$;`_X*65(KFOy}Auz zoR$BdE8E`9#N0DvQA+TOW%$&J~A|8$A3Mx`3wnRu-umCC(tmwI>I59}VSpXVM#1uifr>rw?edqkY%A)6)4E}w#ygKB z>h5t6NsX~USUe9-Zc)~g|H_fiq<)HJyh{GL^!gL$pA+$Ob$`Yv{(EG-VQ~3+*Y6cfwBH%wqtMfXZXq^(D%_;9*bD*Bj>}VyHLnS@@(olD7%4rfG%GAV;$h( z{*sg}B4?@NKRv0f!*5A_ycQT@7z^xqJ3^BQnR5pz+rYxkC;9rZfw7-HXb}`tu#tZ@ z{N19`!&&qHbg4HC>cAmEVTDll1nKUQZiYq#1f^r> z84wsmLXa9#Bt&VY8OlX!q;rt&mhSvr_u1WNfBSV^=dXM3+xT-w|>(JxFUAMW6UQ_&bj7f3DvG{6Ys3)EM`VfZ%((<6uBG6X1?!nb`0B z{g0U9Kc(`Qq60bx0yM}e%x0GEUuF2Wv;DuhR^}U;4H=R!MZjtS7@H-Em2vjoU!wWf z4LbPs*S%yx`yfIwR*BQAQKfH*DEpNMeyv!DmJ&iE2Og=Gg+iHNCt~O3?gtJ@|DuKe?c4fO;`T;7VwH`w!_PqH)1%da zH>w;Z@c}aMv&T_tCvKr0b`$cY*4@jda~CcEcuy(0>W+@T11rRdX4&*gJl$Q44c)AJ zjH2cBPji?oHKPgGli58V*(L!xa~j=%hAA|3N}|6|n_4;PUZx+LP|K2(kW=FIY}We` ztx#rUvGVIuzaGgIz6T+L{`KMF`qO>VU9;sE!Nm0oleJ|c_T+DcY8BIkoQL#j;A|O# zMMht#Cbk#rqcHWiiGC^8yjMPw`0vI>*YHhXcpRnHE782rR-yh%8#%NmB=Nr278$DP z(TaBoI|Z7NU2(AF6y?s9f6crVoG*A& z$>F4QR(c?`g4ucF6icH6vb^Kg6PVtz6;jf>U_*o{y_!q5ejY@oQ@6JXz0G49YRA3H zs|XrK?j7$yReWm%wQh0yi2&b~RIVArsg>M-A0_|$@B?v*g@6^^^*xq(2v@ZUIhQdD z7yQtRBi zO_%D)JeqY8K;x_P@H-{|02Rsxxa;BlHhp`X zno-_I_qhe8-Z`Y1=f1oMqh*h$7KfyCaD(3TwJ`h42uEYZH+-`V(gc7)^&x& zIUxSpG(#|F!qEps2CJw!J~%h2|Eg=AK~0+F?FsYH2h@-?tG0H=|H)MT=6;)nzAe5F z6aeA|Y*uR9kwuTaIfNW&M5A@zM)l%uUuhwr0f~VQTTUJQ+TUDqDfcmGs%FXKFPzfC zd9%Rsx9Y%$5TGhzYnGOKWvy;Me?H*MGGGFo+gOEGVwBXU=d2)QPqOg5EQ1W^(S?8` zK`mj(-E==9UiYWHAtxDdBaUa+XcJg~))fB-;6hQ)TMx{~-zvkN5$Ie?l_b6`o&%Gl zch(DN8TVU1;*K)J)&OGzKAWK+z|0n>fF|8UWt2a!Yw0O1j6uxvg7KO}5~S4nGn@Tz zF<+M9IjV$GW_`0r%(~_CkYaOiQj!AhOWRjmbYPf&1Q|*zLuU6ang5O}U)z*^OtAMh ze8Bh7ICu@UPnG4AuBx+~#~SJKi#E2Ef<}cH*Rz7Xue4Z7hm*8upb_NFh&y6_{YzQt zDsn1P3wU*~T#{;`m|M9_OmMsF`Uw89)h8Cd$p(qhl#0-5M4d{~rvcH{X4FLoWI@6XHbf!7P4v@`4B2N zK0SUMVKa$E*2urd*qp16vgN7NX(8<3ATz(Gi>MBT28RVVydNGJYk(yaV6K6(`idA+ zOa#G(Je%iKG#&$sj??hFZ(J39EpHF(M>t)M@yMn%^BGA~j%RQq@ySH(mTx{y z-9ev4e|Eg1n}Wi=Mnej!y_@jCrgxE^(WJ zIjW~|nxhp|6IBZq_nC*Nt2(@yhGkPTpdhy3hX`?yU}ZOfne)32 z!iP5qnkA}@%}WkS!}X7&!USg`N^preg%h-=0a_`#14g zgLyB<-mFdu%*0yF%_L(6+2^xHj%#pR0aUoadqTJWZLO|omHnev zPi;B^5(jL%?|-2PPhH!>KA))xiiwa?s5(HOJPo`%;U0kA$FucMDOWRPsspQwh7hGBN|Ihv?Lr zz6@6CTI3mS_>ctkPJ%V+C9#!6XXBwyGB+bv2EIHhHJ)&XN555{OGX9&HNqrR^;ceZr?F zn!u$OqBka{9vLz#>S^cEZM>Wc<~4C&nyHCaDUVv)f6gxq|DhdwD}J+M&M=BhLgK5! z(hijOtjljhmLjDx2_3;%j{OimwVTrZN*cNO37|?Tk)q(8OUYrkvxEiz}{PIUKRlWKO?jB^AEm)=6D_+$^V9967 zB=jl$^>EW6_5MhW`wl#nW+zK@>~mgOQBUCY)?ciWzl0i$Jki>`3KUksok!nS&Ue8w z96g8nfRfE=y5<_fG|j^azbaQX0eDH=sa{S`p6-NY6NO^_AoqjdwQ*vODZwM+%w~eL zgR_J5TAPp4eJqi5-)q$0)O zC%xnxzQ#+K!kVdk!KELK5p0X<%<9e(PQ z08Z)=`PKU@3LE-#xToWyZU3S*?AX#7mmQ{mBtN18s<(LJBr1HO1@^pQ|J0U^J(Hst z!`fSIr1_&vZ~G|_)(aaVs==uBd)HfCK3N2GoFP=z8&-To_bd(eVg$;S&TB&qe5E=DJDeK z`f$^>B`=|XpEC=!6jQ>1TwLC{+m7$4;@$~e7X3KCCOgSx;vmU0hAi@;tuM}&x{ki< zu`oDpU`~lw3pYQITAX)|98M2&iC5-&_ROP)wuk$+704po-)e{*8F!mKKMUTxj88@G zt`si|F)fMqEBtO@^ux2z( z_c!+bnu}m1gxB)uRG$4%1LSB+J0o?IojgP{U6T6@C^+pt&VXD#I_Vo%(@5XMgH{Q{ zrpKo(tv;nztj~^dnzmpKAVW2YAb4o~B}d%iA>{VU(di2qUK9}q>@c&+$#ZVkF5ve( z4=+O7iZvPaN$EwhAbY-uRx$PThS!d z-c9+0E?8>TZH?9&6~x+dU85 zm%rRtdtW(id+J$ITMr+5VYVm=Jxj_5yi-V(-9*~Mlo^GcI7OpueeK7ss!34Dt;z*F7e?61I^W- zwt7S}#eIr?oJ}lICb&^?P=%DMm-G(pP7_&+45t0p>>a=W=p;$14MwA-e#O`Fxtq{v z|7I02+PA;?G`PR$P{|E4+^Li6p)KMrsMcUq-CSMh>)`~v5+>&$qAw%czN0c-r@`Tl zBP|zDavPVPB8Tr&9#fU4-VqurZp7ON%XB;bJj8okkQ~bjA--cg|M*MEF=CO+kilh- z?rhW|wJOC_r)JP7xHrJ%m@Yn(K+p9C3QT{7Iy}Fxks-?HB4qP9UdEu<*s4j~Am30? zh1_qrbKz(^uD@7SiiG`!Nc$_lgFDLcY<>E>0y&DpD&-pT5mV=Mk9DT#sf~pM zhy@1sPhH|sWwD>#5rjac2j#-44TnuzcR(@0#8LxVimHM5IDdN3=S>qKOU|75f)|j5 zt&BDxaxem0OQ2F>4&nUi$P?d1f9;p@;j8tu?MM^7*Ksww6sab-bIVP*f`wK<))Te1 z(L2K^x&&jfel>;=@Ey0-wp86&tkEQp*bl+a6gf@ZjI=6-t+mD)`UJ&=!yW~Y4vSl| zKM4*Ezlhko;0`3-BWn|~SZcQ(MZ zAXAR7ZBwSsk24E&$8uSE5^;BDdU@RDmw8wVsm+s?s#$C4dPoV(OM7&Au(Ln}A`{aS{7P<}Rp?rgQ4Znxw)$}>nl@Z6lMG8OV@I2Utlg5=R_>{`>GJ1xO zI*_`y5vZ40oqY&Xi}nN?caNFT?6wyXC`4p?$mX<8)(9Vq2SdXc7Ck8IhDrhXzB&!3 zUpGxr?>+$kEYvb{%;yk5*}ylaV5iU;K1ZrCJwc~Y1^{N_z$2#Ng|>gytFxoOW0n;* z&^m<&Qq^sE#epZlN=49WQe_~(ib>f-Wp}+WySyJjHoQ7iOdl<=6NuJ7*k&bJ05T|- zQPMD9LyC}u5bvEiO?w=AV%VV(Mc1c9UK@hA><;sYns?ZpA)K8_lIePb$Jb~?4Zfi@ z_>eG9(3CI`Tz45WP~6;6n(%V=%sW=pUvN{fLx8Frj@je5i7@(leUE0cXd}Z6mWm+S z3sv4mN&ybT04$*xb>1|;EW0clzkD-WT@AyXe;y4#kGj4Of(bu5yR<%iF>%E1eL&;4 zC*z~)KKqoJ;1x-Th`c#he4!C9r6t*nisK8e!PBvhOw$bd6#J_|-fe>FkulHxO84TgbVlS z8`8vmL}Qm{$E2FCTtzP?Q!idQ)Yo$lytQ(MAdF>i-w)I}${?Wn&3x$<$zsrX485G_ z+!eQs-4n`O6_D?S>zUtlj40^YP5K)_InF@Y!8)Ud}m-OWh**rs%S;HYxbC5Sp3g)?z@@ZQvT9aMI`l^G}RITKpSRNptttXR<$v zVE=gf>GUfIu1(+*Lq9PJI+}0jZZvtY+>bJsKUH>117JbC3>k*j7gZ%HxetmWm@Y;amt^%R7X<%CZzakz-Gd!yUj}+{2Sh^ z%|E9nFQmP_s~yHmHEG(@_fe5|pAyrZ8Bh9jbYsU#bfm{}KAyGrF`10`nOSbe=}vEC zt39NCX?kc)PB;vYV5$qBaS^016Kz4j`` zrH09fH|IGz$>-`%DCb8g%lpk~9%VXwX7^A%j3X09VY$iSQX2w^ZGO&Vg%S(tE?%p= zsN?zXd*LWt(J01#rui8Waj7f_bvNsAn+V}jJL!F?>&CL4mV=T&SxiL=+os%4n5&CB yV1iKhB39dvkMUo86|=>Gr=Z{SG) literal 0 HcmV?d00001 diff --git a/docs/management/connectors/index.asciidoc b/docs/management/connectors/index.asciidoc index 18f2c28d10f042..c5233ad4f4934e 100644 --- a/docs/management/connectors/index.asciidoc +++ b/docs/management/connectors/index.asciidoc @@ -4,6 +4,7 @@ include::action-types/crowdstrike.asciidoc[leveloffset=+1] include::action-types/d3security.asciidoc[leveloffset=+1] include::action-types/email.asciidoc[leveloffset=+1] include::action-types/gemini.asciidoc[leveloffset=+1] +include::action-types/inference.asciidoc[leveloffset=+1] include::action-types/resilient.asciidoc[leveloffset=+1] include::action-types/index.asciidoc[leveloffset=+1] include::action-types/jira.asciidoc[leveloffset=+1] diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 18273319ecd08d..db36248ef194f8 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -283,6 +283,7 @@ A configuration URL that varies by connector: -- * For an <>, specifies the {bedrock} request URL. * For an <>, specifies the {gemini} request URL. +* For an <>, specifies the Elastic {inference} request. * For a <>, specifies the OpenAI request URL. * For a <>, specifies the {ibm-r} instance URL. * For a <>, specifies the Jira instance URL. diff --git a/oas_docs/examples/get_connector_types_generativeai_response.yaml b/oas_docs/examples/get_connector_types_generativeai_response.yaml index a97199e0a3927b..8299da35581502 100644 --- a/oas_docs/examples/get_connector_types_generativeai_response.yaml +++ b/oas_docs/examples/get_connector_types_generativeai_response.yaml @@ -31,3 +31,14 @@ value: supported_feature_ids: - generativeAIForSecurity is_system_action_type: false + - id: .inference + name: Inference API + enabled: true + enabled_in_config: true + enabled_in_license: true + minimum_license_required: enterprise + supported_feature_ids: + - generativeAIForSecurity + - generativeAIForObservability + - generativeAIForSearchPlayground + is_system_action_type: false diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/use_load_action_types/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/use_load_action_types/index.tsx index 3213322463d517..81c8c2a4ea7e44 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/use_load_action_types/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/use_load_action_types/index.tsx @@ -40,8 +40,18 @@ export const useLoadActionTypes = ({ http, featureId: GenerativeAIForSecurityConnectorFeatureId, }); - const sortedData = queryResult.sort((a, b) => a.name.localeCompare(b.name)); + const actionTypeKey = { + bedrock: '.bedrock', + openai: '.gen-ai', + gemini: '.gemini', + }; + + const sortedData = queryResult + .filter((p) => + [actionTypeKey.bedrock, actionTypeKey.openai, actionTypeKey.gemini].includes(p.id) + ) + .sort((a, b) => a.name.localeCompare(b.name)); return sortedData; }, { diff --git a/x-pack/plugins/actions/common/connector_feature_config.ts b/x-pack/plugins/actions/common/connector_feature_config.ts index 5ba316f47d59be..cffa4c433b8f72 100644 --- a/x-pack/plugins/actions/common/connector_feature_config.ts +++ b/x-pack/plugins/actions/common/connector_feature_config.ts @@ -46,7 +46,7 @@ const compatibilityGenerativeAIForObservability = i18n.translate( const compatibilityGenerativeAIForSearchPlayground = i18n.translate( 'xpack.actions.availableConnectorFeatures.compatibility.generativeAIForSearchPlayground', { - defaultMessage: 'Generative AI for Search Playground', + defaultMessage: 'Generative AI for Search', } ); diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/connector_types.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/connector_types.yaml index db6262f04c0107..1db9e155f2eec1 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/connector_types.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/connector_types.yaml @@ -4,6 +4,7 @@ description: The type of connector. For example, `.email`, `.index`, `.jira`, `. enum: - .bedrock - .gemini + - .inference - .cases-webhook - .d3security - .email diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/inference_config.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/inference_config.yaml new file mode 100644 index 00000000000000..8b1219d079f32c --- /dev/null +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/inference_config.yaml @@ -0,0 +1,23 @@ +title: Connector request properties for an Inference API connector +description: Defines properties for connectors when type is `.inference`. +type: object +required: + - provider + - taskType + - inferenceId +properties: + provider: + type: string + description: The Inference API service provider. + taskType: + type: string + description: The Inference task type supported by provider. + providerConfig: + type: object + description: The provider settings. + taskTypeConfig: + type: object + description: The task type settings. + inferenceId: + type: string + description: The task type settings. diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/inference_secrets.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/inference_secrets.yaml new file mode 100644 index 00000000000000..5630a180976339 --- /dev/null +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/inference_secrets.yaml @@ -0,0 +1,9 @@ +title: Connector secrets properties for an AI Connector +description: Defines secrets for connectors when type is `.inference`. +type: object +required: + - providerSecrets +properties: + providerSecrets: + type: object + description: The service account credentials. The service account could have different type of properties to encode. \ No newline at end of file diff --git a/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap b/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap index 94bc911557c218..d778849347d18c 100644 --- a/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap +++ b/x-pack/plugins/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap @@ -5617,6 +5617,353 @@ Object { } `; +exports[`Connector type config checks detect connector type changes for: .inference 1`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "input": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .inference 2`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "input": Object { + "flags": Object { + "default": Array [], + "error": [Function], + "presence": "optional", + }, + "items": Array [ + Object { + "flags": Object { + "error": [Function], + "presence": "optional", + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + ], + "type": "array", + }, + "query": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .inference 3`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "input": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .inference 4`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "input": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "inputType": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .inference 5`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "input": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .inference 6`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "inferenceId": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "provider": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "providerConfig": Object { + "flags": Object { + "default": Object {}, + "error": [Function], + "presence": "optional", + "unknown": true, + }, + "keys": Object {}, + "preferences": Object { + "stripUnknown": Object { + "objects": false, + }, + }, + "type": "object", + }, + "taskType": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "taskTypeConfig": Object { + "flags": Object { + "default": Object {}, + "error": [Function], + "presence": "optional", + "unknown": true, + }, + "keys": Object {}, + "preferences": Object { + "stripUnknown": Object { + "objects": false, + }, + }, + "type": "object", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .inference 7`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "providerSecrets": Object { + "flags": Object { + "default": Object {}, + "error": [Function], + "presence": "optional", + "unknown": true, + }, + "keys": Object {}, + "preferences": Object { + "stripUnknown": Object { + "objects": false, + }, + }, + "type": "object", + }, + }, + "type": "object", +} +`; + +exports[`Connector type config checks detect connector type changes for: .inference 8`] = ` +Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + }, + "keys": Object { + "subAction": Object { + "flags": Object { + "error": [Function], + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "subActionParams": Object { + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", + "unknown": true, + }, + "keys": Object {}, + "preferences": Object { + "stripUnknown": Object { + "objects": false, + }, + }, + "type": "object", + }, + }, + "type": "object", +} +`; + exports[`Connector type config checks detect connector type changes for: .jira 1`] = ` Object { "flags": Object { diff --git a/x-pack/plugins/actions/server/integration_tests/mocks/connector_types.ts b/x-pack/plugins/actions/server/integration_tests/mocks/connector_types.ts index a26c775a74a5b1..fff112de59f163 100644 --- a/x-pack/plugins/actions/server/integration_tests/mocks/connector_types.ts +++ b/x-pack/plugins/actions/server/integration_tests/mocks/connector_types.ts @@ -32,6 +32,7 @@ export const connectorTypes: string[] = [ '.thehive', '.sentinelone', '.crowdstrike', + '.inference', '.cases', '.observability-ai-assistant', ]; diff --git a/x-pack/plugins/stack_connectors/common/experimental_features.ts b/x-pack/plugins/stack_connectors/common/experimental_features.ts index 495921a95c60eb..7adcad74aad855 100644 --- a/x-pack/plugins/stack_connectors/common/experimental_features.ts +++ b/x-pack/plugins/stack_connectors/common/experimental_features.ts @@ -15,6 +15,7 @@ export const allowedExperimentalValues = Object.freeze({ isMustacheAutocompleteOn: false, sentinelOneConnectorOn: true, crowdstrikeConnectorOn: true, + inferenceConnectorOn: true, }); export type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/stack_connectors/common/inference/constants.ts b/x-pack/plugins/stack_connectors/common/inference/constants.ts new file mode 100644 index 00000000000000..b795e54f5d32a3 --- /dev/null +++ b/x-pack/plugins/stack_connectors/common/inference/constants.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const INFERENCE_CONNECTOR_TITLE = i18n.translate( + 'xpack.stackConnectors.components.inference.connectorTypeTitle', + { + defaultMessage: 'AI Connector', + } +); + +export enum ServiceProviderKeys { + amazonbedrock = 'amazonbedrock', + azureopenai = 'azureopenai', + azureaistudio = 'azureaistudio', + cohere = 'cohere', + elasticsearch = 'elasticsearch', + googleaistudio = 'googleaistudio', + googlevertexai = 'googlevertexai', + hugging_face = 'hugging_face', + mistral = 'mistral', + openai = 'openai', + anthropic = 'anthropic', + watsonxai = 'watsonxai', + 'alibabacloud-ai-search' = 'alibabacloud-ai-search', +} + +export const INFERENCE_CONNECTOR_ID = '.inference'; +export enum SUB_ACTION { + COMPLETION = 'completion', + RERANK = 'rerank', + TEXT_EMBEDDING = 'text_embedding', + SPARSE_EMBEDDING = 'sparse_embedding', + COMPLETION_STREAM = 'completion_stream', +} + +export const DEFAULT_PROVIDER = 'openai'; +export const DEFAULT_TASK_TYPE = 'completion'; diff --git a/x-pack/plugins/stack_connectors/common/inference/schema.ts b/x-pack/plugins/stack_connectors/common/inference/schema.ts new file mode 100644 index 00000000000000..07b51cf9a5aa3e --- /dev/null +++ b/x-pack/plugins/stack_connectors/common/inference/schema.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 { schema } from '@kbn/config-schema'; + +export const ConfigSchema = schema.object({ + provider: schema.string(), + taskType: schema.string(), + inferenceId: schema.string(), + providerConfig: schema.object({}, { unknowns: 'allow', defaultValue: {} }), + taskTypeConfig: schema.object({}, { unknowns: 'allow', defaultValue: {} }), +}); + +export const SecretsSchema = schema.object({ + providerSecrets: schema.object({}, { unknowns: 'allow', defaultValue: {} }), +}); + +export const ChatCompleteParamsSchema = schema.object({ + input: schema.string(), +}); + +export const ChatCompleteResponseSchema = schema.arrayOf( + schema.object({ + result: schema.string(), + }), + { defaultValue: [] } +); + +export const RerankParamsSchema = schema.object({ + input: schema.arrayOf(schema.string(), { defaultValue: [] }), + query: schema.string(), +}); + +export const RerankResponseSchema = schema.arrayOf( + schema.object({ + text: schema.maybe(schema.string()), + index: schema.number(), + score: schema.number(), + }), + { defaultValue: [] } +); + +export const SparseEmbeddingParamsSchema = schema.object({ + input: schema.string(), +}); + +export const SparseEmbeddingResponseSchema = schema.arrayOf( + schema.object({}, { unknowns: 'allow' }), + { defaultValue: [] } +); + +export const TextEmbeddingParamsSchema = schema.object({ + input: schema.string(), + inputType: schema.string(), +}); + +export const TextEmbeddingResponseSchema = schema.arrayOf( + schema.object({ + embedding: schema.arrayOf(schema.any(), { defaultValue: [] }), + }), + { defaultValue: [] } +); + +export const StreamingResponseSchema = schema.stream(); diff --git a/x-pack/plugins/stack_connectors/common/inference/types.ts b/x-pack/plugins/stack_connectors/common/inference/types.ts new file mode 100644 index 00000000000000..9dbd447cb45786 --- /dev/null +++ b/x-pack/plugins/stack_connectors/common/inference/types.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TypeOf } from '@kbn/config-schema'; +import { + ConfigSchema, + SecretsSchema, + StreamingResponseSchema, + ChatCompleteParamsSchema, + ChatCompleteResponseSchema, + RerankParamsSchema, + RerankResponseSchema, + SparseEmbeddingParamsSchema, + SparseEmbeddingResponseSchema, + TextEmbeddingParamsSchema, + TextEmbeddingResponseSchema, +} from './schema'; + +export type Config = TypeOf; +export type Secrets = TypeOf; + +export type ChatCompleteParams = TypeOf; +export type ChatCompleteResponse = TypeOf; + +export type RerankParams = TypeOf; +export type RerankResponse = TypeOf; + +export type SparseEmbeddingParams = TypeOf; +export type SparseEmbeddingResponse = TypeOf; + +export type TextEmbeddingParams = TypeOf; +export type TextEmbeddingResponse = TypeOf; + +export type StreamingResponse = TypeOf; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/index.ts index dd1c5e5c63a2ac..92c10bc6ccd57d 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/index.ts +++ b/x-pack/plugins/stack_connectors/public/connector_types/index.ts @@ -14,6 +14,7 @@ import { getJiraConnectorType } from './jira'; import { getOpenAIConnectorType } from './openai'; import { getBedrockConnectorType } from './bedrock'; import { getGeminiConnectorType } from './gemini'; +import { getInferenceConnectorType } from './inference'; import { getOpsgenieConnectorType } from './opsgenie'; import { getPagerDutyConnectorType } from './pagerduty'; import { getResilientConnectorType } from './resilient'; @@ -80,4 +81,7 @@ export function registerConnectorTypes({ if (ExperimentalFeaturesService.get().crowdstrikeConnectorOn) { connectorTypeRegistry.register(getCrowdStrikeConnectorType()); } + if (ExperimentalFeaturesService.get().inferenceConnectorOn) { + connectorTypeRegistry.register(getInferenceConnectorType()); + } } diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx new file mode 100644 index 00000000000000..8973f3124bc860 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/additional_options_fields.tsx @@ -0,0 +1,360 @@ +/* + * 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, useCallback } from 'react'; +import { css } from '@emotion/react'; + +import { + EuiFormRow, + EuiSpacer, + EuiTitle, + EuiAccordion, + EuiFieldText, + useEuiTheme, + EuiTextColor, + EuiButtonGroup, + EuiPanel, + EuiHorizontalRule, + EuiButtonEmpty, + EuiCopy, + EuiButton, + useEuiFontSize, +} from '@elastic/eui'; +import { + getFieldValidityAndErrorMessage, + UseField, + useFormContext, +} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import { ConnectorConfigurationFormItems } from '../lib/dynamic_config/connector_configuration_form_items'; +import * as i18n from './translations'; +import { DEFAULT_TASK_TYPE } from './constants'; +import { ConfigEntryView } from '../lib/dynamic_config/types'; +import { Config } from './types'; +import { TaskTypeOption } from './helpers'; + +// Custom trigger button CSS +const buttonCss = css` + &:hover { + text-decoration: none; + } +`; + +interface AdditionalOptionsConnectorFieldsProps { + config: Config; + readOnly: boolean; + isEdit: boolean; + optionalProviderFormFields: ConfigEntryView[]; + onSetProviderConfigEntry: (key: string, value: unknown) => Promise; + onTaskTypeOptionsSelect: (taskType: string, provider?: string) => Promise; + selectedTaskType?: string; + taskTypeFormFields: ConfigEntryView[]; + taskTypeSchema: ConfigEntryView[]; + taskTypeOptions: TaskTypeOption[]; +} + +export const AdditionalOptionsConnectorFields: React.FC = ({ + config, + readOnly, + isEdit, + taskTypeOptions, + optionalProviderFormFields, + taskTypeFormFields, + taskTypeSchema, + selectedTaskType, + onSetProviderConfigEntry, + onTaskTypeOptionsSelect, +}) => { + const xsFontSize = useEuiFontSize('xs').fontSize; + const { euiTheme } = useEuiTheme(); + const { setFieldValue, validateFields } = useFormContext(); + + const onSetTaskTypeConfigEntry = useCallback( + async (key: string, value: unknown) => { + if (taskTypeSchema) { + const entry: ConfigEntryView | undefined = taskTypeSchema.find( + (p: ConfigEntryView) => p.key === key + ); + if (entry) { + if (!config.taskTypeConfig) { + config.taskTypeConfig = {}; + } + const newConfig = { ...config.taskTypeConfig }; + newConfig[key] = value; + setFieldValue('config.taskTypeConfig', newConfig); + await validateFields(['config.taskTypeConfig']); + } + } + }, + [config, setFieldValue, taskTypeSchema, validateFields] + ); + + const taskTypeSettings = useMemo( + () => + selectedTaskType || config.taskType?.length ? ( + <> + +

+ +

+ + +
+ +
+ + + {(field) => { + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); + + return ( + + } + isInvalid={isInvalid} + error={errorMessage} + > + {isEdit || readOnly ? ( + + {config.taskType} + + ) : taskTypeOptions.length === 1 ? ( + onTaskTypeOptionsSelect(config.taskType)} + > + {config.taskType} + + ) : ( + onTaskTypeOptionsSelect(id)} + options={taskTypeOptions} + color="text" + type="single" + /> + )} + + ); + }} + + + + + ) : null, + [ + selectedTaskType, + config?.taskType, + xsFontSize, + euiTheme.colors, + taskTypeFormFields, + onSetTaskTypeConfigEntry, + isEdit, + readOnly, + taskTypeOptions, + onTaskTypeOptionsSelect, + ] + ); + + const inferenceUri = useMemo(() => `_inference/${selectedTaskType}/`, [selectedTaskType]); + + return ( + + + + } + initialIsOpen={true} + > + + + {optionalProviderFormFields.length > 0 ? ( + <> + +

+ +

+
+ +
+ +
+ + + + + ) : null} + + {taskTypeSettings} + + +

+ +

+
+ +
+ +
+ + + + {(field) => { + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); + + return ( + + } + > + { + setFieldValue('config.inferenceId', e.target.value); + }} + prepend={inferenceUri} + append={ + + {(copy) => ( + + + + )} + + } + /> + + ); + }} + +
+
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export { AdditionalOptionsConnectorFields as default }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx new file mode 100644 index 00000000000000..44632e8b083315 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.test.tsx @@ -0,0 +1,353 @@ +/* + * 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 ConnectorFields from './connector'; +import { ConnectorFormTestProvider } from '../lib/test_utils'; +import { render, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { createStartServicesMock } from '@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react.mock'; +import { DisplayType, FieldType } from '../lib/dynamic_config/types'; +import { useProviders } from './providers/get_providers'; +import { getTaskTypes } from './get_task_types'; +import { HttpSetup } from '@kbn/core-http-browser'; + +jest.mock('./providers/get_providers'); +jest.mock('./get_task_types'); + +const mockUseKibanaReturnValue = createStartServicesMock(); +jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana', () => ({ + __esModule: true, + useKibana: jest.fn(() => ({ + services: mockUseKibanaReturnValue, + })), +})); + +jest.mock('@faker-js/faker', () => ({ + faker: { + string: { + alpha: jest.fn().mockReturnValue('123'), + }, + }, +})); + +const mockProviders = useProviders as jest.Mock; +const mockTaskTypes = getTaskTypes as jest.Mock; + +const providersSchemas = [ + { + provider: 'openai', + logo: '', // should be openai logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'text_embedding'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 3, + required: true, + sensitive: true, + tooltip: `The OpenAI API authentication key. For more details about generating OpenAI API keys, refer to the https://platform.openai.com/account/api-keys.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 2, + required: true, + sensitive: false, + tooltip: 'The name of the model.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + organization_id: { + display: DisplayType.TEXTBOX, + label: 'Organization ID', + order: 4, + required: false, + sensitive: false, + tooltip: '', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + url: { + display: DisplayType.TEXTBOX, + label: 'URL', + order: 1, + required: true, + sensitive: false, + tooltip: '', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: 'https://api.openai.com/v1/chat/completions', + depends_on: [], + }, + }, + }, + { + provider: 'googleaistudio', + logo: '', // should be googleaistudio logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'text_embedding'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 1, + required: true, + sensitive: true, + tooltip: `API Key for the provider you're connecting to`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 2, + required: true, + sensitive: false, + tooltip: `ID of the LLM you're using`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, +]; +const taskTypesSchemas: Record = { + googleaistudio: [ + { + task_type: 'completion', + configuration: {}, + }, + { + task_type: 'text_embedding', + configuration: {}, + }, + ], + openai: [ + { + task_type: 'completion', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], +}; + +const openAiConnector = { + actionTypeId: '.inference', + name: 'AI Connector', + id: '123', + config: { + provider: 'openai', + taskType: 'completion', + providerConfig: { + url: 'https://openaiurl.com', + model_id: 'gpt-4o', + organization_id: 'test-org', + }, + taskTypeConfig: { + user: 'elastic', + }, + }, + secrets: { + secretsConfig: { + api_key: 'thats-a-nice-looking-key', + }, + }, + isDeprecated: false, +}; + +const googleaistudioConnector = { + ...openAiConnector, + config: { + ...openAiConnector.config, + provider: 'googleaistudio', + providerConfig: { + ...openAiConnector.config.providerConfig, + model_id: 'somemodel', + }, + taskTypeConfig: {}, + }, + secrets: { + secretsConfig: { + api_key: 'thats-google-key', + }, + }, +}; + +describe('ConnectorFields renders', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockProviders.mockReturnValue({ + isLoading: false, + data: providersSchemas, + }); + mockTaskTypes.mockImplementation( + (http: HttpSetup, provider: string) => taskTypesSchemas[provider] + ); + }); + test('openai provider fields are rendered', async () => { + const { getAllByTestId } = render( + + {}} /> + + ); + expect(getAllByTestId('provider-select')[0]).toBeInTheDocument(); + expect(getAllByTestId('provider-select')[0]).toHaveValue('OpenAI'); + + expect(getAllByTestId('url-input')[0]).toBeInTheDocument(); + expect(getAllByTestId('url-input')[0]).toHaveValue(openAiConnector.config?.providerConfig?.url); + expect(getAllByTestId('taskTypeSelectDisabled')[0]).toBeInTheDocument(); + expect(getAllByTestId('taskTypeSelectDisabled')[0]).toHaveTextContent('completion'); + }); + + test('googleaistudio provider fields are rendered', async () => { + const { getAllByTestId } = render( + + {}} /> + + ); + expect(getAllByTestId('api_key-password')[0]).toBeInTheDocument(); + expect(getAllByTestId('api_key-password')[0]).toHaveValue(''); + expect(getAllByTestId('provider-select')[0]).toBeInTheDocument(); + expect(getAllByTestId('provider-select')[0]).toHaveValue('Google AI Studio'); + expect(getAllByTestId('model_id-input')[0]).toBeInTheDocument(); + expect(getAllByTestId('model_id-input')[0]).toHaveValue( + googleaistudioConnector.config?.providerConfig.model_id + ); + }); + + describe('Validation', () => { + const onSubmit = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + jest.spyOn(global.Math, 'random').mockReturnValue(0.123456789); + }); + + it('connector validation succeeds when connector config is valid', async () => { + const { getByTestId } = render( + + {}} /> + + ); + + await userEvent.click(getByTestId('form-test-provide-submit')); + + await waitFor(async () => { + expect(onSubmit).toHaveBeenCalled(); + }); + + expect(onSubmit).toBeCalledWith({ + data: { + config: { + inferenceId: 'openai-completion-4fzzzxjylrx', + ...openAiConnector.config, + }, + actionTypeId: openAiConnector.actionTypeId, + name: openAiConnector.name, + id: openAiConnector.id, + isDeprecated: openAiConnector.isDeprecated, + }, + isValid: true, + }); + }); + + it('validates correctly if the provider config url is empty', async () => { + const connector = { + ...openAiConnector, + config: { + ...openAiConnector.config, + providerConfig: { + url: '', + modelId: 'gpt-4o', + }, + }, + }; + + const res = render( + + {}} /> + + ); + + await userEvent.click(res.getByTestId('form-test-provide-submit')); + await waitFor(async () => { + expect(onSubmit).toHaveBeenCalled(); + }); + + expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false }); + }); + + const tests: Array<[string, string]> = [ + ['url-input', 'not-valid'], + ['api_key-password', ''], + ]; + it.each(tests)('validates correctly %p', async (field, value) => { + const connector = { + ...openAiConnector, + config: { + ...openAiConnector.config, + headers: [], + }, + }; + + const res = render( + + {}} /> + + ); + + await userEvent.type(res.getByTestId(field), `{selectall}{backspace}${value}`, { + delay: 10, + }); + + await userEvent.click(res.getByTestId('form-test-provide-submit')); + await waitFor(async () => { + expect(onSubmit).toHaveBeenCalled(); + }); + + expect(onSubmit).toHaveBeenCalledWith({ data: {}, isValid: false }); + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx new file mode 100644 index 00000000000000..35314dc06167d1 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/connector.tsx @@ -0,0 +1,445 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { + EuiFormRow, + EuiSpacer, + EuiInputPopover, + EuiFieldText, + EuiFieldTextProps, + EuiSelectableOption, + EuiFormControlLayout, + keys, + EuiHorizontalRule, +} from '@elastic/eui'; +import { + getFieldValidityAndErrorMessage, + UseField, + useFormContext, + useFormData, +} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + ConnectorFormSchema, + type ActionConnectorFieldsProps, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { useKibana } from '@kbn/triggers-actions-ui-plugin/public'; + +import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import { ServiceProviderKeys } from '../../../common/inference/constants'; +import { ConnectorConfigurationFormItems } from '../lib/dynamic_config/connector_configuration_form_items'; +import { getTaskTypes } from './get_task_types'; +import * as i18n from './translations'; +import { DEFAULT_TASK_TYPE } from './constants'; +import { ConfigEntryView } from '../lib/dynamic_config/types'; +import { SelectableProvider } from './providers/selectable'; +import { Config, Secrets } from './types'; +import { generateInferenceEndpointId, getTaskTypeOptions, TaskTypeOption } from './helpers'; +import { useProviders } from './providers/get_providers'; +import { SERVICE_PROVIDERS } from './providers/render_service_provider/service_provider'; +import { AdditionalOptionsConnectorFields } from './additional_options_fields'; +import { + getProviderConfigHiddenField, + getProviderSecretsHiddenField, + getTaskTypeConfigHiddenField, +} from './hidden_fields'; + +const InferenceAPIConnectorFields: React.FunctionComponent = ({ + readOnly, + isEdit, +}) => { + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const { updateFieldValues, setFieldValue, validateFields, isSubmitting } = useFormContext(); + const [{ config, secrets }] = useFormData>({ + watch: [ + 'secrets.providerSecrets', + 'config.taskType', + 'config.taskTypeConfig', + 'config.inferenceId', + 'config.provider', + 'config.providerConfig', + ], + }); + + const { data: providers, isLoading } = useProviders(http, toasts); + + const [isProviderPopoverOpen, setProviderPopoverOpen] = useState(false); + + const [providerSchema, setProviderSchema] = useState([]); + const [optionalProviderFormFields, setOptionalProviderFormFields] = useState( + [] + ); + const [requiredProviderFormFields, setRequiredProviderFormFields] = useState( + [] + ); + + const [taskTypeSchema, setTaskTypeSchema] = useState([]); + const [taskTypeOptions, setTaskTypeOptions] = useState([]); + const [selectedTaskType, setSelectedTaskType] = useState(DEFAULT_TASK_TYPE); + const [taskTypeFormFields, setTaskTypeFormFields] = useState([]); + + const handleProviderClosePopover = useCallback(() => { + setProviderPopoverOpen(false); + }, []); + + const handleProviderPopover = useCallback(() => { + setProviderPopoverOpen((isOpen) => !isOpen); + }, []); + + const handleProviderKeyboardOpen: EuiFieldTextProps['onKeyDown'] = useCallback((event: any) => { + if (event.key === keys.ENTER) { + setProviderPopoverOpen(true); + } + }, []); + + useEffect(() => { + if (!isEdit && config && !config.inferenceId) { + generateInferenceEndpointId(config, setFieldValue); + } + }, [isEdit, setFieldValue, config]); + + useEffect(() => { + if (isSubmitting) { + validateFields(['config.providerConfig']); + validateFields(['secrets.providerSecrets']); + validateFields(['config.taskTypeConfig']); + } + }, [isSubmitting, config, validateFields]); + + const onTaskTypeOptionsSelect = useCallback( + async (taskType: string, provider?: string) => { + // Get task type settings + const currentTaskTypes = await getTaskTypes(http, provider ?? config?.provider); + const newTaskType = currentTaskTypes?.find((p) => p.task_type === taskType); + + setSelectedTaskType(taskType); + generateInferenceEndpointId(config, setFieldValue); + + // transform the schema + const newTaskTypeSchema = Object.keys(newTaskType?.configuration ?? {}).map((k) => ({ + key: k, + isValid: true, + ...newTaskType?.configuration[k], + })) as ConfigEntryView[]; + setTaskTypeSchema(newTaskTypeSchema); + + const configDefaults = Object.keys(newTaskType?.configuration ?? {}).reduce( + (res: Record, k) => { + if (newTaskType?.configuration[k] && !!newTaskType?.configuration[k].default_value) { + res[k] = newTaskType.configuration[k].default_value; + } else { + res[k] = null; + } + return res; + }, + {} + ); + + updateFieldValues({ + config: { + taskType, + taskTypeConfig: configDefaults, + }, + }); + }, + [config, http, setFieldValue, updateFieldValues] + ); + + const onProviderChange = useCallback( + async (provider?: string) => { + const newProvider = providers?.find((p) => p.provider === provider); + + // Update task types list available for the selected provider + const providerTaskTypes = newProvider?.taskTypes ?? []; + setTaskTypeOptions(getTaskTypeOptions(providerTaskTypes)); + if (providerTaskTypes.length > 0) { + await onTaskTypeOptionsSelect(providerTaskTypes[0], provider); + } + + // Update connector providerSchema + const newProviderSchema = Object.keys(newProvider?.configuration ?? {}).map((k) => ({ + key: k, + isValid: true, + ...newProvider?.configuration[k], + })) as ConfigEntryView[]; + + setProviderSchema(newProviderSchema); + + const defaultProviderConfig: Record = {}; + const defaultProviderSecrets: Record = {}; + + Object.keys(newProvider?.configuration ?? {}).forEach((k) => { + if (!newProvider?.configuration[k].sensitive) { + if (newProvider?.configuration[k] && !!newProvider?.configuration[k].default_value) { + defaultProviderConfig[k] = newProvider.configuration[k].default_value; + } else { + defaultProviderConfig[k] = null; + } + } else { + defaultProviderSecrets[k] = null; + } + }); + + updateFieldValues({ + config: { + provider: newProvider?.provider, + providerConfig: defaultProviderConfig, + }, + secrets: { + providerSecrets: defaultProviderSecrets, + }, + }); + }, + [onTaskTypeOptionsSelect, providers, updateFieldValues] + ); + + useEffect(() => { + const getTaskTypeSchema = async () => { + const currentTaskTypes = await getTaskTypes(http, config?.provider ?? ''); + const newTaskType = currentTaskTypes?.find((p) => p.task_type === config?.taskType); + + // transform the schema + const newTaskTypeSchema = Object.keys(newTaskType?.configuration ?? {}).map((k) => ({ + key: k, + isValid: true, + ...newTaskType?.configuration[k], + })) as ConfigEntryView[]; + + setTaskTypeSchema(newTaskTypeSchema); + }; + + if (config?.provider && isEdit) { + const newProvider = providers?.find((p) => p.provider === config.provider); + // Update connector providerSchema + const newProviderSchema = Object.keys(newProvider?.configuration ?? {}).map((k) => ({ + key: k, + isValid: true, + ...newProvider?.configuration[k], + })) as ConfigEntryView[]; + + setProviderSchema(newProviderSchema); + + getTaskTypeSchema(); + } + }, [config?.provider, config?.taskType, http, isEdit, providers]); + + useEffect(() => { + // Set values from the provider secrets and config to the schema + const existingConfiguration = providerSchema + ? providerSchema.map((item: ConfigEntryView) => { + const itemValue = item; + itemValue.isValid = true; + if (item.sensitive && secrets?.providerSecrets) { + itemValue.value = secrets?.providerSecrets[item.key] as any; + } else if (config?.providerConfig) { + itemValue.value = config?.providerConfig[item.key] as any; + } + return itemValue; + }) + : []; + + existingConfiguration.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); + setOptionalProviderFormFields(existingConfiguration.filter((p) => !p.required && !p.sensitive)); + setRequiredProviderFormFields(existingConfiguration.filter((p) => p.required || p.sensitive)); + }, [config?.providerConfig, providerSchema, secrets]); + + useEffect(() => { + // Set values from the task type config to the schema + const existingTaskTypeConfiguration = taskTypeSchema + ? taskTypeSchema.map((item: ConfigEntryView) => { + const itemValue = item; + itemValue.isValid = true; + if (config?.taskTypeConfig) { + itemValue.value = config?.taskTypeConfig[item.key] as any; + } + return itemValue; + }) + : []; + existingTaskTypeConfiguration.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)); + setTaskTypeFormFields(existingTaskTypeConfiguration); + }, [config, taskTypeSchema]); + + const getProviderOptions = useCallback(() => { + return providers?.map((p) => ({ + label: p.provider, + key: p.provider, + })) as EuiSelectableOption[]; + }, [providers]); + + const onSetProviderConfigEntry = useCallback( + async (key: string, value: unknown) => { + const entry: ConfigEntryView | undefined = providerSchema.find( + (p: ConfigEntryView) => p.key === key + ); + if (entry) { + if (entry.sensitive) { + if (!secrets.providerSecrets) { + secrets.providerSecrets = {}; + } + const newSecrets = { ...secrets.providerSecrets }; + newSecrets[key] = value; + setFieldValue('secrets.providerSecrets', newSecrets); + await validateFields(['secrets.providerSecrets']); + } else { + if (!config.providerConfig) { + config.providerConfig = {}; + } + const newConfig = { ...config.providerConfig }; + newConfig[key] = value; + setFieldValue('config.providerConfig', newConfig); + await validateFields(['config.providerConfig']); + } + } + }, + [config, providerSchema, secrets, setFieldValue, validateFields] + ); + + const onClearProvider = useCallback(() => { + onProviderChange(); + setFieldValue('config.taskType', ''); + setFieldValue('config.provider', ''); + }, [onProviderChange, setFieldValue]); + + const providerSuperSelect = useCallback( + (isInvalid: boolean) => ( + + + + ), + [ + isEdit, + readOnly, + onClearProvider, + config?.provider, + handleProviderPopover, + handleProviderKeyboardOpen, + isProviderPopoverOpen, + ] + ); + + return ( + <> + + {(field) => { + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); + const selectInput = providerSuperSelect(isInvalid); + return ( + + } + isInvalid={isInvalid} + error={errorMessage} + > + + + + + ); + }} + + {config?.provider ? ( + <> + + + + + + + {getProviderSecretsHiddenField( + providerSchema, + setRequiredProviderFormFields, + isSubmitting + )} + {getProviderConfigHiddenField( + providerSchema, + setRequiredProviderFormFields, + isSubmitting + )} + {getTaskTypeConfigHiddenField(taskTypeSchema, setTaskTypeFormFields, isSubmitting)} + + ) : null} + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { InferenceAPIConnectorFields as default }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/constants.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/constants.tsx new file mode 100644 index 00000000000000..8427caaf49ffc1 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/constants.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SUB_ACTION } from '../../../common/inference/constants'; + +export const DEFAULT_CHAT_COMPLETE_BODY = { + input: 'What is Elastic?', +}; + +export const DEFAULT_RERANK_BODY = { + input: ['luke', 'like', 'leia', 'chewy', 'r2d2', 'star', 'wars'], + query: 'star wars main character', +}; + +export const DEFAULT_SPARSE_EMBEDDING_BODY = { + input: 'The sky above the port was the color of television tuned to a dead channel.', +}; + +export const DEFAULT_TEXT_EMBEDDING_BODY = { + input: 'The sky above the port was the color of television tuned to a dead channel.', + inputType: 'ingest', +}; + +export const DEFAULTS_BY_TASK_TYPE: Record = { + [SUB_ACTION.COMPLETION]: DEFAULT_CHAT_COMPLETE_BODY, + [SUB_ACTION.RERANK]: DEFAULT_RERANK_BODY, + [SUB_ACTION.SPARSE_EMBEDDING]: DEFAULT_SPARSE_EMBEDDING_BODY, + [SUB_ACTION.TEXT_EMBEDDING]: DEFAULT_TEXT_EMBEDDING_BODY, +}; + +export const DEFAULT_TASK_TYPE = 'completion'; + +export const DEFAULT_PROVIDER = 'elasticsearch'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.test.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.test.ts new file mode 100644 index 00000000000000..201df82f6412cc --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { httpServiceMock } from '@kbn/core/public/mocks'; +import { DisplayType, FieldType } from '../lib/dynamic_config/types'; +import { getTaskTypes } from './get_task_types'; + +const http = httpServiceMock.createStartContract(); + +beforeEach(() => jest.resetAllMocks()); + +describe.skip('getTaskTypes', () => { + test('should call get inference task types api', async () => { + const apiResponse = { + amazonbedrock: [ + { + task_type: 'completion', + configuration: { + max_new_tokens: { + display: DisplayType.NUMERIC, + label: 'Max new tokens', + order: 1, + required: false, + sensitive: false, + tooltip: 'Sets the maximum number for the output tokens to be generated.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'text_embedding', + configuration: {}, + }, + ], + }; + http.get.mockResolvedValueOnce(apiResponse); + + const result = await getTaskTypes(http, 'amazonbedrock'); + expect(result).toEqual(apiResponse); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts new file mode 100644 index 00000000000000..a4fbbd6a6288bf --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/get_task_types.ts @@ -0,0 +1,606 @@ +/* + * 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 { HttpSetup } from '@kbn/core-http-browser'; +import { DisplayType, FieldType } from '../lib/dynamic_config/types'; +import { FieldsConfiguration } from './types'; + +export interface InferenceTaskType { + task_type: string; + configuration: FieldsConfiguration; +} + +// this http param is for the future migrating to real API +export const getTaskTypes = (http: HttpSetup, provider: string): Promise => { + const providersTaskTypes: Record = { + openai: [ + { + task_type: 'completion', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'text_embedding', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], + mistral: [ + { + task_type: 'text_embedding', + configuration: {}, + }, + ], + hugging_face: [ + { + task_type: 'text_embedding', + configuration: {}, + }, + ], + googlevertexai: [ + { + task_type: 'text_embedding', + configuration: { + auto_truncate: { + display: DisplayType.TOGGLE, + label: 'Auto truncate', + order: 1, + required: false, + sensitive: false, + tooltip: + 'Specifies if the API truncates inputs longer than the maximum token length automatically.', + type: FieldType.BOOLEAN, + validations: [], + value: false, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'rerank', + configuration: { + top_n: { + display: DisplayType.TOGGLE, + label: 'Top N', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the number of the top n documents, which should be returned.', + type: FieldType.BOOLEAN, + validations: [], + value: false, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], + googleaistudio: [ + { + task_type: 'completion', + configuration: {}, + }, + { + task_type: 'text_embedding', + configuration: {}, + }, + ], + elasticsearch: [ + { + task_type: 'rerank', + configuration: { + return_documents: { + display: DisplayType.TOGGLE, + label: 'Return documents', + options: [], + order: 1, + required: false, + sensitive: false, + tooltip: 'Returns the document instead of only the index.', + type: FieldType.BOOLEAN, + validations: [], + value: true, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'sparse_embedding', + configuration: {}, + }, + { + task_type: 'text_embedding', + configuration: {}, + }, + ], + cohere: [ + { + task_type: 'completion', + configuration: {}, + }, + { + task_type: 'text_embedding', + configuration: { + input_type: { + display: DisplayType.DROPDOWN, + label: 'Input type', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the type of input passed to the model.', + type: FieldType.STRING, + validations: [], + options: [ + { + label: 'classification', + value: 'classification', + }, + { + label: 'clusterning', + value: 'clusterning', + }, + { + label: 'ingest', + value: 'ingest', + }, + { + label: 'search', + value: 'search', + }, + ], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + truncate: { + display: DisplayType.DROPDOWN, + options: [ + { + label: 'NONE', + value: 'NONE', + }, + { + label: 'START', + value: 'START', + }, + { + label: 'END', + value: 'END', + }, + ], + label: 'Truncate', + order: 2, + required: false, + sensitive: false, + tooltip: 'Specifies how the API handles inputs longer than the maximum token length.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'rerank', + configuration: { + return_documents: { + display: DisplayType.TOGGLE, + label: 'Return documents', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specify whether to return doc text within the results.', + type: FieldType.BOOLEAN, + validations: [], + value: false, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + top_n: { + display: DisplayType.NUMERIC, + label: 'Top N', + order: 1, + required: false, + sensitive: false, + tooltip: + 'The number of most relevant documents to return, defaults to the number of the documents.', + type: FieldType.INTEGER, + validations: [], + value: false, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], + azureopenai: [ + { + task_type: 'completion', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'text_embedding', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], + azureaistudio: [ + { + task_type: 'completion', + configuration: { + user: { + display: DisplayType.TEXTBOX, + label: 'User', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the user issuing the request.', + type: FieldType.STRING, + validations: [], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'text_embedding', + configuration: { + do_sample: { + display: DisplayType.NUMERIC, + label: 'Do sample', + order: 1, + required: false, + sensitive: false, + tooltip: 'Instructs the inference process to perform sampling or not.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + max_new_tokens: { + display: DisplayType.NUMERIC, + label: 'Max new tokens', + order: 1, + required: false, + sensitive: false, + tooltip: 'Provides a hint for the maximum number of output tokens to be generated.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + temperature: { + display: DisplayType.NUMERIC, + label: 'Temperature', + order: 1, + required: false, + sensitive: false, + tooltip: 'A number in the range of 0.0 to 2.0 that specifies the sampling temperature.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + top_p: { + display: DisplayType.NUMERIC, + label: 'Top P', + order: 1, + required: false, + sensitive: false, + tooltip: + 'A number in the range of 0.0 to 2.0 that is an alternative value to temperature. Should not be used if temperature is specified.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], + amazonbedrock: [ + { + task_type: 'completion', + configuration: { + max_new_tokens: { + display: DisplayType.NUMERIC, + label: 'Max new tokens', + order: 1, + required: false, + sensitive: false, + tooltip: 'Sets the maximum number for the output tokens to be generated.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + temperature: { + display: DisplayType.NUMERIC, + label: 'Temperature', + order: 1, + required: false, + sensitive: false, + tooltip: + 'A number between 0.0 and 1.0 that controls the apparent creativity of the results.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + top_p: { + display: DisplayType.NUMERIC, + label: 'Top P', + order: 1, + required: false, + sensitive: false, + tooltip: + 'Alternative to temperature. A number in the range of 0.0 to 1.0, to eliminate low-probability tokens.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + top_k: { + display: DisplayType.NUMERIC, + label: 'Top K', + order: 1, + required: false, + sensitive: false, + tooltip: + 'Only available for anthropic, cohere, and mistral providers. Alternative to temperature.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'text_embedding', + configuration: {}, + }, + ], + anthropic: [ + { + task_type: 'completion', + configuration: { + max_tokens: { + display: DisplayType.NUMERIC, + label: 'Max tokens', + order: 1, + required: true, + sensitive: false, + tooltip: 'The maximum number of tokens to generate before stopping.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + temperature: { + display: DisplayType.TEXTBOX, + label: 'Temperature', + order: 2, + required: false, + sensitive: false, + tooltip: 'The amount of randomness injected into the response.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + top_p: { + display: DisplayType.NUMERIC, + label: 'Top P', + order: 4, + required: false, + sensitive: false, + tooltip: 'Specifies to use Anthropic’s nucleus sampling.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + top_k: { + display: DisplayType.NUMERIC, + label: 'Top K', + order: 3, + required: false, + sensitive: false, + tooltip: 'Specifies to only sample from the top K options for each subsequent token.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ], + 'alibabacloud-ai-search': [ + { + task_type: 'text_embedding', + configuration: { + input_type: { + display: DisplayType.DROPDOWN, + label: 'Input type', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the type of input passed to the model.', + type: FieldType.STRING, + validations: [], + options: [ + { + label: 'ingest', + value: 'ingest', + }, + { + label: 'search', + value: 'search', + }, + ], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'sparse_embedding', + configuration: { + input_type: { + display: DisplayType.DROPDOWN, + label: 'Input type', + order: 1, + required: false, + sensitive: false, + tooltip: 'Specifies the type of input passed to the model.', + type: FieldType.STRING, + validations: [], + options: [ + { + label: 'ingest', + value: 'ingest', + }, + { + label: 'search', + value: 'search', + }, + ], + value: '', + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + return_token: { + display: DisplayType.TOGGLE, + label: 'Return token', + options: [], + order: 1, + required: false, + sensitive: false, + tooltip: + 'If `true`, the token name will be returned in the response. Defaults to `false` which means only the token ID will be returned in the response.', + type: FieldType.BOOLEAN, + validations: [], + value: true, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + task_type: 'completion', + configuration: {}, + }, + { + task_type: 'rerank', + configuration: {}, + }, + ], + watsonxai: [ + { + task_type: 'text_embedding', + configuration: {}, + }, + ], + }; + return Promise.resolve(providersTaskTypes[provider]); +}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts new file mode 100644 index 00000000000000..0e1e4cdaa41ad2 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/helpers.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isEmpty } from 'lodash/fp'; +import { ValidationFunc } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { ConfigEntryView } from '../lib/dynamic_config/types'; +import { Config } from './types'; +import * as i18n from './translations'; + +export interface TaskTypeOption { + id: string; + value: string; + label: string; +} + +export const getTaskTypeOptions = (taskTypes: string[]): TaskTypeOption[] => + taskTypes.map((taskType) => ({ + id: taskType, + label: taskType, + value: taskType, + })); + +export const generateInferenceEndpointId = ( + config: Config, + setFieldValue: (fieldName: string, value: unknown) => void +) => { + const taskTypeSuffix = config.taskType ? `${config.taskType}-` : ''; + const inferenceEndpointId = `${config.provider}-${taskTypeSuffix}${Math.random() + .toString(36) + .slice(2)}`; + config.inferenceId = inferenceEndpointId; + setFieldValue('config.inferenceId', inferenceEndpointId); +}; + +export const getNonEmptyValidator = ( + schema: ConfigEntryView[], + validationEventHandler: (fieldsWithErrors: ConfigEntryView[]) => void, + isSubmitting: boolean = false, + isSecrets: boolean = false +) => { + return (...args: Parameters): ReturnType => { + const [{ value, path }] = args; + const newSchema: ConfigEntryView[] = []; + + const configData = (value ?? {}) as Record; + let hasErrors = false; + if (schema) { + schema + .filter((f: ConfigEntryView) => f.required) + .forEach((field: ConfigEntryView) => { + // validate if submitting or on field edit - value is not default to null + if (configData[field.key] !== null || isSubmitting) { + // validate secrets fields separately from regular + if (isSecrets ? field.sensitive : !field.sensitive) { + if ( + !configData[field.key] || + (typeof configData[field.key] === 'string' && isEmpty(configData[field.key])) + ) { + field.validationErrors = [i18n.getRequiredMessage(field.label)]; + field.isValid = false; + hasErrors = true; + } else { + field.validationErrors = []; + field.isValid = true; + } + } + } + newSchema.push(field); + }); + + validationEventHandler(newSchema.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))); + if (hasErrors) { + return { + code: 'ERR_FIELD_MISSING', + path, + message: i18n.getRequiredMessage('Action'), + }; + } + } + }; +}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx new file mode 100644 index 00000000000000..9b28d35aaaf3a6 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/hidden_fields.tsx @@ -0,0 +1,88 @@ +/* + * 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 { HiddenField } from '@kbn/es-ui-shared-plugin/static/forms/components'; +import { UseField } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { getNonEmptyValidator } from './helpers'; +import { ConfigEntryView } from '../lib/dynamic_config/types'; + +export const getProviderSecretsHiddenField = ( + providerSchema: ConfigEntryView[], + setRequiredProviderFormFields: React.Dispatch>, + isSubmitting: boolean +) => ( + +); + +export const getProviderConfigHiddenField = ( + providerSchema: ConfigEntryView[], + setRequiredProviderFormFields: React.Dispatch>, + isSubmitting: boolean +) => ( + +); + +export const getTaskTypeConfigHiddenField = ( + taskTypeSchema: ConfigEntryView[], + setTaskTypeFormFields: React.Dispatch>, + isSubmitting: boolean +) => ( + { + const formFields = [ + ...requiredFormFields, + ...(taskTypeSchema ?? []).filter((f) => !f.required), + ]; + setTaskTypeFormFields(formFields.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))); + }, + isSubmitting + ), + isBlocking: true, + }, + ], + }} + /> +); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/index.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/index.ts new file mode 100644 index 00000000000000..cdac663d16b89f --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/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 { getConnectorType as getInferenceConnectorType } from './inference'; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.test.tsx new file mode 100644 index 00000000000000..0f37564fd560c1 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.test.tsx @@ -0,0 +1,142 @@ +/* + * 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 { TypeRegistry } from '@kbn/triggers-actions-ui-plugin/public/application/type_registry'; +import { registerConnectorTypes } from '..'; +import type { ActionTypeModel } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { experimentalFeaturesMock, registrationServicesMock } from '../../mocks'; +import { SUB_ACTION } from '../../../common/inference/constants'; +import { ExperimentalFeaturesService } from '../../common/experimental_features_service'; + +const ACTION_TYPE_ID = '.inference'; +let actionTypeModel: ActionTypeModel; + +beforeAll(() => { + ExperimentalFeaturesService.init({ experimentalFeatures: experimentalFeaturesMock }); + const connectorTypeRegistry = new TypeRegistry(); + registerConnectorTypes({ connectorTypeRegistry, services: registrationServicesMock }); + const getResult = connectorTypeRegistry.get(ACTION_TYPE_ID); + if (getResult !== null) { + actionTypeModel = getResult; + } +}); + +describe('actionTypeRegistry.get() works', () => { + test('connector type static data is as expected', () => { + expect(actionTypeModel.id).toEqual(ACTION_TYPE_ID); + expect(actionTypeModel.selectMessage).toBe( + 'Send requests to AI providers such as Amazon Bedrock, OpenAI and more.' + ); + expect(actionTypeModel.actionTypeTitle).toBe('AI Connector'); + }); +}); + +describe('OpenAI action params validation', () => { + test.each([ + { + subAction: SUB_ACTION.RERANK, + subActionParams: { input: ['message test'], query: 'foobar' }, + }, + { + subAction: SUB_ACTION.COMPLETION, + subActionParams: { input: 'message test' }, + }, + { + subAction: SUB_ACTION.TEXT_EMBEDDING, + subActionParams: { input: 'message test', inputType: 'foobar' }, + }, + { + subAction: SUB_ACTION.SPARSE_EMBEDDING, + subActionParams: { input: 'message test' }, + }, + ])( + 'validation succeeds when params are valid for subAction $subAction', + async ({ subAction, subActionParams }) => { + const actionParams = { + subAction, + subActionParams, + }; + expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { input: [], subAction: [], inputType: [], query: [] }, + }); + } + ); + + test('params validation fails when params is a wrong object', async () => { + const actionParams = { + subAction: SUB_ACTION.COMPLETION, + subActionParams: { body: 'message {test}' }, + }; + + expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { input: ['Input is required.'], inputType: [], query: [], subAction: [] }, + }); + }); + + test('params validation fails when subAction is missing', async () => { + const actionParams = { + subActionParams: { input: 'message test' }, + }; + + expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { + input: [], + inputType: [], + query: [], + subAction: ['Action is required.'], + }, + }); + }); + + test('params validation fails when subAction is not in the list of the supported', async () => { + const actionParams = { + subAction: 'wrong', + subActionParams: { input: 'message test' }, + }; + + expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { + input: [], + inputType: [], + query: [], + subAction: ['Invalid action name.'], + }, + }); + }); + + test('params validation fails when subActionParams is missing', async () => { + const actionParams = { + subAction: SUB_ACTION.RERANK, + subActionParams: {}, + }; + + expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { + input: ['Input is required.', 'Input does not have a valid Array format.'], + inputType: [], + query: ['Query is required.'], + subAction: [], + }, + }); + }); + + test('params validation fails when text_embedding inputType is missing', async () => { + const actionParams = { + subAction: SUB_ACTION.TEXT_EMBEDDING, + subActionParams: { input: 'message test' }, + }; + + expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { + input: [], + inputType: ['Input type is required.'], + query: [], + subAction: [], + }, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.tsx new file mode 100644 index 00000000000000..e16d03306c1668 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/inference.tsx @@ -0,0 +1,92 @@ +/* + * 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 { lazy } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { GenericValidationResult } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { RerankParams, TextEmbeddingParams } from '../../../common/inference/types'; +import { SUB_ACTION } from '../../../common/inference/constants'; +import { + INFERENCE_CONNECTOR_ID, + INFERENCE_CONNECTOR_TITLE, +} from '../../../common/inference/constants'; +import { InferenceActionParams, InferenceConnector } from './types'; + +interface ValidationErrors { + subAction: string[]; + input: string[]; + // rerank only + query: string[]; + // text_embedding only + inputType: string[]; +} +export function getConnectorType(): InferenceConnector { + return { + id: INFERENCE_CONNECTOR_ID, + iconClass: 'sparkles', + isExperimental: true, + selectMessage: i18n.translate('xpack.stackConnectors.components.inference.selectMessageText', { + defaultMessage: 'Send requests to AI providers such as Amazon Bedrock, OpenAI and more.', + }), + actionTypeTitle: INFERENCE_CONNECTOR_TITLE, + validateParams: async ( + actionParams: InferenceActionParams + ): Promise> => { + const { subAction, subActionParams } = actionParams; + const translations = await import('./translations'); + const errors: ValidationErrors = { + input: [], + subAction: [], + inputType: [], + query: [], + }; + + if ( + subAction === SUB_ACTION.RERANK || + subAction === SUB_ACTION.COMPLETION || + subAction === SUB_ACTION.TEXT_EMBEDDING || + subAction === SUB_ACTION.SPARSE_EMBEDDING + ) { + if (!subActionParams.input?.length) { + errors.input.push(translations.getRequiredMessage('Input')); + } + } + if (subAction === SUB_ACTION.RERANK) { + if (!Array.isArray(subActionParams.input)) { + errors.input.push(translations.INPUT_INVALID); + } + + if (!(subActionParams as RerankParams).query?.length) { + errors.query.push(translations.getRequiredMessage('Query')); + } + } + if (subAction === SUB_ACTION.TEXT_EMBEDDING) { + if (!(subActionParams as TextEmbeddingParams).inputType?.length) { + errors.inputType.push(translations.getRequiredMessage('Input type')); + } + } + if (errors.input.length) return { errors }; + + // The internal "subAction" param should always be valid, ensure it is only if "subActionParams" are valid + if (!subAction) { + errors.subAction.push(translations.getRequiredMessage('Action')); + } else if ( + ![ + SUB_ACTION.COMPLETION, + SUB_ACTION.SPARSE_EMBEDDING, + SUB_ACTION.RERANK, + SUB_ACTION.TEXT_EMBEDDING, + ].includes(subAction) + ) { + errors.subAction.push(translations.INVALID_ACTION); + } + return { errors }; + }, + actionConnectorFields: lazy(() => import('./connector')), + actionParamsFields: lazy(() => import('./params')), + }; +} diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/params.test.tsx new file mode 100644 index 00000000000000..49773edc2246ac --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/params.test.tsx @@ -0,0 +1,167 @@ +/* + * 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 { fireEvent, render } from '@testing-library/react'; +import ParamsFields from './params'; +import { SUB_ACTION } from '../../../common/inference/constants'; + +describe('Inference Params Fields renders', () => { + test('all params fields are rendered', () => { + const { getByTestId } = render( + {}} + index={0} + /> + ); + expect(getByTestId('inferenceInput')).toBeInTheDocument(); + expect(getByTestId('inferenceInput')).toHaveProperty('value', 'What is Elastic?'); + }); + + test.each(['openai', 'googleaistudio'])( + 'useEffect handles the case when subAction and subActionParams are undefined and provider is %p', + (provider) => { + const actionParams = { + subAction: undefined, + subActionParams: undefined, + }; + const editAction = jest.fn(); + const errors = {}; + const actionConnector = { + secrets: { + providerSecrets: { apiKey: 'apiKey' }, + }, + id: 'test', + actionTypeId: '.inference', + isPreconfigured: false, + isSystemAction: false as const, + isDeprecated: false, + name: 'My OpenAI Connector', + config: { + provider, + providerConfig: { + url: 'https://api.openai.com/v1/embeddings', + }, + taskType: 'completion', + }, + }; + render( + + ); + expect(editAction).toHaveBeenCalledTimes(2); + expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.COMPLETION, 0); + if (provider === 'openai') { + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { input: 'What is Elastic?' }, + 0 + ); + } + if (provider === 'googleaistudio') { + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { input: 'What is Elastic?' }, + 0 + ); + } + } + ); + + it('handles the case when subAction only is undefined', () => { + const actionParams = { + subAction: undefined, + subActionParams: { + input: '{"key": "value"}', + }, + }; + const editAction = jest.fn(); + const errors = {}; + render( + + ); + expect(editAction).toHaveBeenCalledTimes(1); + expect(editAction).toHaveBeenCalledWith('subAction', SUB_ACTION.COMPLETION, 0); + }); + + it('calls editAction function with the correct arguments ', () => { + const editAction = jest.fn(); + const errors = {}; + const { getByTestId } = render( + + ); + const jsonEditor = getByTestId('inputJsonEditor'); + fireEvent.change(jsonEditor, { target: { value: `[\"apple\",\"banana\",\"tomato\"]` } }); + expect(editAction).toHaveBeenCalledWith( + 'subActionParams', + { input: '["apple","banana","tomato"]', query: 'test' }, + 0 + ); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/params.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/params.tsx new file mode 100644 index 00000000000000..0013f943e36392 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/params.tsx @@ -0,0 +1,238 @@ +/* + * 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, useEffect } from 'react'; +import { + JsonEditorWithMessageVariables, + type ActionParamsProps, +} from '@kbn/triggers-actions-ui-plugin/public'; +import { EuiTextArea, EuiFormRow, EuiSpacer, EuiSelect } from '@elastic/eui'; +import { RuleFormParamsErrors } from '@kbn/alerts-ui-shared'; +import { + ChatCompleteParams, + RerankParams, + SparseEmbeddingParams, + TextEmbeddingParams, +} from '../../../common/inference/types'; +import { DEFAULTS_BY_TASK_TYPE } from './constants'; +import * as i18n from './translations'; +import { SUB_ACTION } from '../../../common/inference/constants'; +import { InferenceActionConnector, InferenceActionParams } from './types'; + +const InferenceServiceParamsFields: React.FunctionComponent< + ActionParamsProps +> = ({ actionParams, editAction, index, errors, actionConnector }) => { + const { subAction, subActionParams } = actionParams; + + const { taskType } = (actionConnector as unknown as InferenceActionConnector).config; + + useEffect(() => { + if (!subAction) { + editAction('subAction', taskType, index); + } + }, [editAction, index, subAction, taskType]); + + useEffect(() => { + if (!subActionParams) { + editAction( + 'subActionParams', + { + ...(DEFAULTS_BY_TASK_TYPE[taskType] ?? {}), + }, + index + ); + } + }, [editAction, index, subActionParams, taskType]); + + const editSubActionParams = useCallback( + (params: Partial) => { + editAction('subActionParams', { ...subActionParams, ...params }, index); + }, + [editAction, index, subActionParams] + ); + + if (subAction === SUB_ACTION.COMPLETION) { + return ( + + ); + } + + if (subAction === SUB_ACTION.RERANK) { + return ( + + ); + } + + if (subAction === SUB_ACTION.SPARSE_EMBEDDING) { + return ( + + ); + } + + if (subAction === SUB_ACTION.TEXT_EMBEDDING) { + return ( + + ); + } + + return <>; +}; + +const InferenceInput: React.FunctionComponent<{ + input?: string; + inputError?: string; + editSubActionParams: (params: Partial) => void; +}> = ({ input, inputError, editSubActionParams }) => { + return ( + + { + editSubActionParams({ input: e.target.value }); + }} + isInvalid={false} + fullWidth={true} + /> + + ); +}; + +const CompletionParamsFields: React.FunctionComponent<{ + subActionParams: ChatCompleteParams; + errors: RuleFormParamsErrors; + editSubActionParams: (params: Partial) => void; +}> = ({ subActionParams, editSubActionParams, errors }) => { + const { input } = subActionParams; + + return ( + + ); +}; + +const SparseEmbeddingParamsFields: React.FunctionComponent<{ + subActionParams: SparseEmbeddingParams; + errors: RuleFormParamsErrors; + editSubActionParams: (params: Partial) => void; +}> = ({ subActionParams, editSubActionParams, errors }) => { + const { input } = subActionParams; + + return ( + + ); +}; + +const TextEmbeddingParamsFields: React.FunctionComponent<{ + subActionParams: TextEmbeddingParams; + errors: RuleFormParamsErrors; + editSubActionParams: (params: Partial) => void; +}> = ({ subActionParams, editSubActionParams, errors }) => { + const { input, inputType } = subActionParams; + + const options = [ + { value: 'ingest', text: 'ingest' }, + { value: 'search', text: 'search' }, + ]; + + return ( + <> + + { + editSubActionParams({ inputType: e.target.value }); + }} + /> + + + + + ); +}; + +const RerankParamsFields: React.FunctionComponent<{ + subActionParams: RerankParams; + errors: RuleFormParamsErrors; + editSubActionParams: (params: Partial) => void; +}> = ({ subActionParams, editSubActionParams, errors }) => { + const { input, query } = subActionParams; + + return ( + <> + { + editSubActionParams({ input: json.trim() }); + }} + onBlur={() => { + if (!input) { + editSubActionParams({ input: [] }); + } + }} + dataTestSubj="inference-inputJsonEditor" + /> + + + { + editSubActionParams({ query: e.target.value }); + }} + isInvalid={false} + fullWidth={true} + /> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { InferenceServiceParamsFields as default }; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/alibaba_cloud.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/alibaba_cloud.svg new file mode 100644 index 00000000000000..1ae552d509c3a8 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/alibaba_cloud.svg @@ -0,0 +1,3 @@ + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/amazon_bedrock.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/amazon_bedrock.svg new file mode 100644 index 00000000000000..f8815d4f75ec56 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/amazon_bedrock.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/anthropic.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/anthropic.svg new file mode 100644 index 00000000000000..c361cda86a7df7 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/anthropic.svg @@ -0,0 +1,3 @@ + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/azure_ai_studio.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/azure_ai_studio.svg new file mode 100644 index 00000000000000..405e182a103949 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/azure_ai_studio.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/azure_open_ai.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/azure_open_ai.svg new file mode 100644 index 00000000000000..122c0c65af13cc --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/azure_open_ai.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/cohere.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/cohere.svg new file mode 100644 index 00000000000000..69953809fec358 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/cohere.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/elastic.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/elastic.svg new file mode 100644 index 00000000000000..e763c2e2f2ab6e --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/elastic.svg @@ -0,0 +1,16 @@ + + + + + + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/google_ai_studio.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/google_ai_studio.svg new file mode 100644 index 00000000000000..b6e34ae15c9e4b --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/google_ai_studio.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/hugging_face.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/hugging_face.svg new file mode 100644 index 00000000000000..87ac70c5a18f41 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/hugging_face.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/ibm_watsonx.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/ibm_watsonx.svg new file mode 100644 index 00000000000000..5883eff3884d6f --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/ibm_watsonx.svg @@ -0,0 +1,3 @@ + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/mistral.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/mistral.svg new file mode 100644 index 00000000000000..f62258a327594b --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/mistral.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/open_ai.svg b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/open_ai.svg new file mode 100644 index 00000000000000..9ddc8f8fd63b8b --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/assets/images/open_ai.svg @@ -0,0 +1,3 @@ + + + diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.test.tsx new file mode 100644 index 00000000000000..06548b36a635ae --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.test.tsx @@ -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 React from 'react'; +import * as ReactQuery from '@tanstack/react-query'; +import { renderHook } from '@testing-library/react-hooks/dom'; +import { waitFor } from '@testing-library/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { httpServiceMock, notificationServiceMock } from '@kbn/core/public/mocks'; +import { useProviders } from './get_providers'; + +const http = httpServiceMock.createStartContract(); +const toasts = notificationServiceMock.createStartContract(); +const useQuerySpy = jest.spyOn(ReactQuery, 'useQuery'); + +beforeEach(() => jest.resetAllMocks()); + +const { getProviders } = jest.requireMock('./get_providers'); + +const queryClient = new QueryClient(); + +const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} +); + +describe('useProviders', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should call useQuery', async () => { + renderHook(() => useProviders(http, toasts.toasts), { + wrapper, + }); + + await waitFor(() => { + return expect(useQuerySpy).toBeCalled(); + }); + }); + + it('should return isError = true if api fails', async () => { + getProviders.mockResolvedValue('This is an error.'); + + renderHook(() => useProviders(http, toasts.toasts), { + wrapper, + }); + + await waitFor(() => expect(useQuerySpy).toHaveBeenCalled()); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts new file mode 100644 index 00000000000000..109266c1273fc5 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/get_providers.ts @@ -0,0 +1,1054 @@ +/* + * 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 { HttpSetup } from '@kbn/core-http-browser'; +import { i18n } from '@kbn/i18n'; +import { useQuery } from '@tanstack/react-query'; +import type { ToastsStart } from '@kbn/core-notifications-browser'; +import { DisplayType, FieldType } from '../../lib/dynamic_config/types'; +import { FieldsConfiguration } from '../types'; + +export interface InferenceProvider { + provider: string; + taskTypes: string[]; + logo?: string; + configuration: FieldsConfiguration; +} + +export const getProviders = (http: HttpSetup): Promise => { + const providers = [ + { + provider: 'openai', + logo: '', // should be openai logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'text_embedding'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 3, + required: true, + sensitive: true, + tooltip: `The OpenAI API authentication key. For more details about generating OpenAI API keys, refer to the https://platform.openai.com/account/api-keys.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 2, + required: true, + sensitive: false, + tooltip: 'The name of the model to use for the inference task.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + organization_id: { + display: DisplayType.TEXTBOX, + label: 'Organization ID', + order: 4, + required: false, + sensitive: false, + tooltip: 'The unique identifier of your organization.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + url: { + display: DisplayType.TEXTBOX, + label: 'URL', + order: 1, + required: true, + sensitive: false, + tooltip: + 'The OpenAI API endpoint URL. For more information on the URL, refer to the https://platform.openai.com/docs/api-reference.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: 'https://api.openai.com/v1/chat/completions', + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: + 'Default number of requests allowed per minute. For text_embedding is 3000. For completion is 500.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'googleaistudio', + logo: '', // should be googleaistudio logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'text_embedding'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 1, + required: true, + sensitive: true, + tooltip: `API Key for the provider you're connecting to`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 2, + required: true, + sensitive: false, + tooltip: `ID of the LLM you're using`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: 'Minimize the number of rate limit errors.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'amazonbedrock', + logo: '', // should be amazonbedrock logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'text_embedding'], + configuration: { + access_key: { + display: DisplayType.TEXTBOX, + label: 'Access Key', + order: 1, + required: true, + sensitive: true, + tooltip: `A valid AWS access key that has permissions to use Amazon Bedrock.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + secret_key: { + display: DisplayType.TEXTBOX, + label: 'Secret Key', + order: 2, + required: true, + sensitive: true, + tooltip: `A valid AWS secret key that is paired with the access_key.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + provider: { + display: DisplayType.DROPDOWN, + label: 'Provider', + order: 3, + required: true, + options: [ + { + label: 'amazontitan', + value: 'amazontitan', + }, + { + label: 'anthropic', + value: 'anthropic', + }, + { + label: 'ai21labs', + value: 'ai21labs', + }, + { + label: 'cohere', + value: 'cohere', + }, + { + label: 'meta', + value: 'meta', + }, + { + label: 'mistral', + value: 'mistral', + }, + ], + sensitive: false, + tooltip: 'The model provider for your deployment.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model: { + display: DisplayType.TEXTBOX, + label: 'Model', + order: 4, + required: true, + sensitive: false, + tooltip: `The base model ID or an ARN to a custom model based on a foundational model.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + region: { + display: DisplayType.TEXTBOX, + label: 'Region', + order: 5, + required: true, + sensitive: false, + tooltip: `The region that your model or ARN is deployed in.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 6, + required: false, + sensitive: false, + tooltip: + 'By default, the amazonbedrock service sets the number of requests allowed per minute to 240.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'googlevertexai', + logo: '', // should be googlevertexai logo here, the hardcoded uses assets/images + taskTypes: ['text_embedding', 'rerank'], + configuration: { + service_account_json: { + display: DisplayType.TEXTBOX, + label: 'Credentials JSON', + order: 1, + required: true, + sensitive: true, + tooltip: `API Key for the provider you're connecting to`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 2, + required: true, + sensitive: false, + tooltip: `ID of the LLM you're using`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + location: { + display: DisplayType.TEXTBOX, + label: 'GCP Region', + order: 2, + required: true, + sensitive: false, + tooltip: `Please provide the GCP region where the Vertex AI API(s) is enabled. For more information, refer to the {geminiVertexAIDocs}.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + project_id: { + display: DisplayType.TEXTBOX, + label: 'GCP Project', + order: 3, + required: true, + sensitive: false, + tooltip: + 'The GCP Project ID which has Vertex AI API(s) enabled. For more information on the URL, refer to the {geminiVertexAIDocs}.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: 'Minimize the number of rate limit errors.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'mistral', + logo: '', // should be misral logo here, the hardcoded uses assets/images + taskTypes: ['text_embedding'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 1, + required: true, + sensitive: true, + tooltip: `API Key for the provider you're connecting to`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model: { + display: DisplayType.TEXTBOX, + label: 'Model', + order: 2, + required: true, + sensitive: false, + tooltip: `Refer to the Mistral models documentation for the list of available text embedding models`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 4, + required: false, + sensitive: false, + tooltip: 'Minimize the number of rate limit errors.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: 240, + depends_on: [], + }, + max_input_tokens: { + display: DisplayType.NUMERIC, + label: 'Maximum input tokens', + order: 3, + required: false, + sensitive: false, + tooltip: 'Allows you to specify the maximum number of tokens per input.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'hugging_face', + logo: '', // should be hugging_face logo here, the hardcoded uses assets/images + taskTypes: ['text_embedding'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 2, + required: true, + sensitive: true, + tooltip: `API Key for the provider you're connecting to`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + url: { + display: DisplayType.TEXTBOX, + label: 'URL', + order: 1, + required: true, + sensitive: false, + tooltip: 'The URL endpoint to use for the requests.', + type: FieldType.STRING, + validations: [], + value: 'https://api.openai.com/v1/embeddings', + ui_restrictions: [], + default_value: 'https://api.openai.com/v1/embeddings', + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 3, + required: false, + sensitive: false, + tooltip: 'Minimize the number of rate limit errors.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'elasticsearch', + logo: '', // elasticsearch logo here + taskTypes: ['sparse_embedding', 'text_embedding', 'rerank'], + configuration: { + model_id: { + display: DisplayType.DROPDOWN, + label: 'Model ID', + order: 1, + required: true, + sensitive: false, + tooltip: `The name of the model to use for the inference task.`, + type: FieldType.STRING, + validations: [], + options: [ + { + label: '.elser_model_1', + value: '.elser_model_1', + }, + { + label: '.elser_model_2', + value: '.elser_model_2', + }, + { + label: '.elser_model_2_linux-x86_64', + value: '.elser_model_2_linux-x86_64', + }, + { + label: '.multilingual-e5-small', + value: '.multilingual-e5-small', + }, + { + label: '.multilingual-e5-small_linux-x86_64', + value: '.multilingual-e5-small_linux-x86_64', + }, + ], + value: null, + ui_restrictions: [], + default_value: '.multilingual-e5-small', + depends_on: [], + }, + num_allocations: { + display: DisplayType.NUMERIC, + label: 'Number allocations', + order: 2, + required: true, + sensitive: false, + tooltip: + 'The total number of allocations this model is assigned across machine learning nodes.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: 1, + depends_on: [], + }, + num_threads: { + display: DisplayType.NUMERIC, + label: 'Number threads', + order: 3, + required: true, + sensitive: false, + tooltip: 'Sets the number of threads used by each model allocation during inference.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: 2, + depends_on: [], + }, + }, + }, + { + provider: 'cohere', + logo: '', // should be cohere logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'text_embedding', 'rerank'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 1, + required: true, + sensitive: true, + tooltip: `API Key for the provider you're connecting to`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: 'Minimize the number of rate limit errors.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'azureopenai', + logo: '', // should be azureopenai logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'text_embedding'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 1, + required: false, + sensitive: true, + tooltip: `You must provide either an API key or an Entra ID.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + entra_id: { + display: DisplayType.TEXTBOX, + label: 'Entra ID', + order: 2, + required: false, + sensitive: true, + tooltip: `You must provide either an API key or an Entra ID.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + resource_name: { + display: DisplayType.TEXTBOX, + label: 'Resource Name', + order: 3, + required: true, + sensitive: false, + tooltip: `The name of your Azure OpenAI resource`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + api_version: { + display: DisplayType.TEXTBOX, + label: 'API version', + order: 4, + required: true, + sensitive: false, + tooltip: 'The Azure API version ID to use.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + deployment_id: { + display: DisplayType.TEXTBOX, + label: 'Deployment ID', + order: 5, + required: true, + sensitive: false, + tooltip: 'The deployment name of your deployed models.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: + 'The azureopenai service sets a default number of requests allowed per minute depending on the task type.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'azureaistudio', + logo: '', // should be azureaistudio logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'text_embedding'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 1, + required: true, + sensitive: true, + tooltip: `API Key for the provider you're connecting to`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + target: { + display: DisplayType.TEXTBOX, + label: 'Target', + order: 2, + required: true, + sensitive: false, + tooltip: `The target URL of your Azure AI Studio model deployment.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + endpoint_type: { + display: DisplayType.DROPDOWN, + label: 'Endpoint type', + order: 3, + required: true, + sensitive: false, + tooltip: 'Specifies the type of endpoint that is used in your model deployment.', + type: FieldType.STRING, + options: [ + { + label: 'token', + value: 'token', + }, + { + label: 'realtime', + value: 'realtime', + }, + ], + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + provider: { + display: DisplayType.DROPDOWN, + label: 'Provider', + order: 3, + required: true, + options: [ + { + label: 'cohere', + value: 'cohere', + }, + { + label: 'meta', + value: 'meta', + }, + { + label: 'microsoft_phi', + value: 'microsoft_phi', + }, + { + label: 'mistral', + value: 'mistral', + }, + { + label: 'openai', + value: 'openai', + }, + { + label: 'databricks', + value: 'databricks', + }, + ], + sensitive: false, + tooltip: 'The model provider for your deployment.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: 'Minimize the number of rate limit errors.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'anthropic', + logo: '', // should be anthropic logo here, the hardcoded uses assets/images + taskTypes: ['completion'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 1, + required: true, + sensitive: true, + tooltip: `API Key for the provider you're connecting to`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 2, + required: true, + sensitive: false, + tooltip: `The name of the model to use for the inference task.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 5, + required: false, + sensitive: false, + tooltip: + 'By default, the anthropic service sets the number of requests allowed per minute to 50.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'watsonxai', + logo: '', // should be anthropic logo here, the hardcoded uses assets/images + taskTypes: ['text_embedding'], + configuration: { + api_version: { + display: DisplayType.TEXTBOX, + label: 'API version', + order: 1, + required: true, + sensitive: false, + tooltip: 'The IBM Watsonx API version ID to use.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + project_id: { + display: DisplayType.TEXTBOX, + label: 'Project ID', + order: 2, + required: true, + sensitive: false, + tooltip: '', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + model_id: { + display: DisplayType.TEXTBOX, + label: 'Model ID', + order: 3, + required: true, + sensitive: false, + tooltip: `The name of the model to use for the inference task.`, + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + url: { + display: DisplayType.TEXTBOX, + label: 'URL', + order: 4, + required: true, + sensitive: false, + tooltip: '', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + max_input_tokens: { + display: DisplayType.NUMERIC, + label: 'Maximum input tokens', + order: 5, + required: false, + sensitive: false, + tooltip: 'Allows you to specify the maximum number of tokens per input.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + { + provider: 'alibabacloud-ai-search', + logo: '', // should be anthropic logo here, the hardcoded uses assets/images + taskTypes: ['completion', 'sparse_embedding', 'text_embedding', 'rerank'], + configuration: { + api_key: { + display: DisplayType.TEXTBOX, + label: 'API Key', + order: 1, + required: true, + sensitive: true, + tooltip: 'A valid API key for the AlibabaCloud AI Search API.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + service_id: { + display: DisplayType.DROPDOWN, + label: 'Project ID', + order: 2, + required: true, + sensitive: false, + tooltip: 'The name of the model service to use for the {infer} task.', + type: FieldType.STRING, + options: [ + { + label: 'ops-text-embedding-001', + value: 'ops-text-embedding-001', + }, + { + label: 'ops-text-embedding-zh-001', + value: 'ops-text-embedding-zh-001', + }, + { + label: 'ops-text-embedding-en-001', + value: 'ops-text-embedding-en-001', + }, + { + label: 'ops-text-embedding-002', + value: 'ops-text-embedding-002', + }, + { + label: 'ops-text-sparse-embedding-001', + value: 'ops-text-sparse-embedding-001', + }, + { + label: 'ops-bge-reranker-larger', + value: 'ops-bge-reranker-larger', + }, + ], + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + host: { + display: DisplayType.TEXTBOX, + label: 'Host', + order: 3, + required: true, + sensitive: false, + tooltip: + 'The name of the host address used for the {infer} task. You can find the host address at https://opensearch.console.aliyun.com/cn-shanghai/rag/api-key[ the API keys section] of the documentation.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + http_schema: { + display: DisplayType.DROPDOWN, + label: 'HTTP Schema', + order: 4, + required: true, + sensitive: false, + tooltip: '', + type: FieldType.STRING, + options: [ + { + label: 'https', + value: 'https', + }, + { + label: 'http', + value: 'http', + }, + ], + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + workspace: { + display: DisplayType.TEXTBOX, + label: 'Workspace', + order: 5, + required: true, + sensitive: false, + tooltip: 'The name of the workspace used for the {infer} task.', + type: FieldType.STRING, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + 'rate_limit.requests_per_minute': { + display: DisplayType.NUMERIC, + label: 'Rate limit', + order: 6, + required: false, + sensitive: false, + tooltip: 'Minimize the number of rate limit errors.', + type: FieldType.INTEGER, + validations: [], + value: null, + ui_restrictions: [], + default_value: null, + depends_on: [], + }, + }, + }, + ] as InferenceProvider[]; + return Promise.resolve( + providers.sort((a, b) => (a.provider > b.provider ? 1 : b.provider > a.provider ? -1 : 0)) + ); +}; + +export const useProviders = (http: HttpSetup, toasts: ToastsStart) => { + const onErrorFn = (error: Error) => { + if (error) { + toasts.addDanger( + i18n.translate( + 'xpack.stackConnectors.components.inference.unableToFindProvidersQueryMessage', + { + defaultMessage: 'Unable to find providers', + } + ) + ); + } + }; + + const query = useQuery(['user-profile'], { + queryFn: () => getProviders(http), + staleTime: Infinity, + refetchOnWindowFocus: false, + onError: onErrorFn, + }); + return query; +}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.test.tsx new file mode 100644 index 00000000000000..84a32286b75322 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.test.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 { render, screen } from '@testing-library/react'; +import React from 'react'; +import { ServiceProviderIcon, ServiceProviderName } from './service_provider'; +import { ServiceProviderKeys } from '../../../../../common/inference/constants'; + +jest.mock('../assets/images/elastic.svg', () => 'elasticIcon.svg'); +jest.mock('../assets/images/hugging_face.svg', () => 'huggingFaceIcon.svg'); +jest.mock('../assets/images/cohere.svg', () => 'cohereIcon.svg'); +jest.mock('../assets/images/open_ai.svg', () => 'openAIIcon.svg'); + +describe('ServiceProviderIcon component', () => { + it('renders Hugging Face icon and name when providerKey is hugging_face', () => { + render(); + const icon = screen.getByTestId('icon-service-provider-hugging_face'); + expect(icon).toBeInTheDocument(); + }); + + it('renders Open AI icon and name when providerKey is openai', () => { + render(); + const icon = screen.getByTestId('icon-service-provider-openai'); + expect(icon).toBeInTheDocument(); + }); +}); + +describe('ServiceProviderName component', () => { + it('renders Hugging Face icon and name when providerKey is hugging_face', () => { + render(); + expect(screen.getByText('Hugging Face')).toBeInTheDocument(); + }); + + it('renders Open AI icon and name when providerKey is openai', () => { + render(); + expect(screen.getByText('OpenAI')).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.tsx new file mode 100644 index 00000000000000..5d2c99ffd92ce0 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/render_service_provider/service_provider.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 { EuiHighlight, EuiIcon } from '@elastic/eui'; +import React from 'react'; +import { ServiceProviderKeys } from '../../../../../common/inference/constants'; +import elasticIcon from '../assets/images/elastic.svg'; +import huggingFaceIcon from '../assets/images/hugging_face.svg'; +import cohereIcon from '../assets/images/cohere.svg'; +import openAIIcon from '../assets/images/open_ai.svg'; +import azureAIStudioIcon from '../assets/images/azure_ai_studio.svg'; +import azureOpenAIIcon from '../assets/images/azure_open_ai.svg'; +import googleAIStudioIcon from '../assets/images/google_ai_studio.svg'; +import mistralIcon from '../assets/images/mistral.svg'; +import amazonBedrockIcon from '../assets/images/amazon_bedrock.svg'; +import anthropicIcon from '../assets/images/anthropic.svg'; +import alibabaCloudIcon from '../assets/images/alibaba_cloud.svg'; +import ibmWatsonxIcon from '../assets/images/ibm_watsonx.svg'; + +interface ServiceProviderProps { + providerKey: ServiceProviderKeys; + searchValue?: string; +} + +type ProviderSolution = 'Observability' | 'Security' | 'Search'; + +interface ServiceProviderRecord { + icon: string; + name: string; + solutions: ProviderSolution[]; +} + +export const SERVICE_PROVIDERS: Record = { + [ServiceProviderKeys.amazonbedrock]: { + icon: amazonBedrockIcon, + name: 'Amazon Bedrock', + solutions: ['Observability', 'Security', 'Search'], + }, + [ServiceProviderKeys.azureaistudio]: { + icon: azureAIStudioIcon, + name: 'Azure AI Studio', + solutions: ['Search'], + }, + [ServiceProviderKeys.azureopenai]: { + icon: azureOpenAIIcon, + name: 'Azure OpenAI', + solutions: ['Observability', 'Security', 'Search'], + }, + [ServiceProviderKeys.anthropic]: { + icon: anthropicIcon, + name: 'Anthropic', + solutions: ['Search'], + }, + [ServiceProviderKeys.cohere]: { + icon: cohereIcon, + name: 'Cohere', + solutions: ['Search'], + }, + [ServiceProviderKeys.elasticsearch]: { + icon: elasticIcon, + name: 'Elasticsearch', + solutions: ['Search'], + }, + [ServiceProviderKeys.googleaistudio]: { + icon: googleAIStudioIcon, + name: 'Google AI Studio', + solutions: ['Search'], + }, + [ServiceProviderKeys.googlevertexai]: { + icon: googleAIStudioIcon, + name: 'Google Vertex AI', + solutions: ['Observability', 'Security', 'Search'], + }, + [ServiceProviderKeys.hugging_face]: { + icon: huggingFaceIcon, + name: 'Hugging Face', + solutions: ['Search'], + }, + [ServiceProviderKeys.mistral]: { + icon: mistralIcon, + name: 'Mistral', + solutions: ['Search'], + }, + [ServiceProviderKeys.openai]: { + icon: openAIIcon, + name: 'OpenAI', + solutions: ['Observability', 'Security', 'Search'], + }, + [ServiceProviderKeys['alibabacloud-ai-search']]: { + icon: alibabaCloudIcon, + name: 'AlibabaCloud AI Search', + solutions: ['Search'], + }, + [ServiceProviderKeys.watsonxai]: { + icon: ibmWatsonxIcon, + name: 'IBM Watsonx', + solutions: ['Search'], + }, +}; + +export const ServiceProviderIcon: React.FC = ({ providerKey }) => { + const provider = SERVICE_PROVIDERS[providerKey]; + + return provider ? ( + + ) : ( + {providerKey} + ); +}; + +export const ServiceProviderName: React.FC = ({ + providerKey, + searchValue, +}) => { + const provider = SERVICE_PROVIDERS[providerKey]; + + return provider ? ( + {provider.name} + ) : ( + {providerKey} + ); +}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.test.tsx new file mode 100644 index 00000000000000..f83d4bcd9ea4c9 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.test.tsx @@ -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 type { EuiSelectableProps } from '@elastic/eui'; +import React from 'react'; +import type { ShallowWrapper } from 'enzyme'; +import { shallow } from 'enzyme'; + +import { SelectableProvider } from '.'; + +describe('SelectableProvider', () => { + const props = { + isLoading: false, + onClosePopover: jest.fn(), + onProviderChange: jest.fn(), + getSelectableOptions: jest.fn().mockReturnValue([]), + }; + + describe('should render', () => { + let wrapper: ShallowWrapper; + + describe('provider', () => { + beforeAll(() => { + wrapper = shallow(); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + test('render placeholder', () => { + const searchProps: EuiSelectableProps['searchProps'] = wrapper + .find('[data-test-subj="selectable-provider-input"]') + .prop('searchProps'); + expect(searchProps?.placeholder).toEqual('Search'); + }); + }); + + describe('template', () => { + beforeAll(() => { + wrapper = shallow(); + }); + + afterAll(() => { + jest.clearAllMocks(); + }); + + test('render placeholder', () => { + const searchProps: EuiSelectableProps['searchProps'] = wrapper + .find('[data-test-subj="selectable-provider-input"]') + .prop('searchProps'); + expect(searchProps?.placeholder).toEqual('Search'); + }); + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.tsx new file mode 100644 index 00000000000000..d4527e9c7b9a49 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/providers/selectable/index.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 type { EuiSelectableOption, EuiSelectableProps } from '@elastic/eui'; +import { EuiSelectable, EuiFlexGroup, EuiFlexItem, EuiBadge } from '@elastic/eui'; +import React, { memo, useCallback, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { ServiceProviderKeys } from '../../../../../common/inference/constants'; +import { + SERVICE_PROVIDERS, + ServiceProviderIcon, + ServiceProviderName, +} from '../render_service_provider/service_provider'; + +/** + * Modifies options by creating new property `providerTitle`(with value of `title`), and by setting `title` to undefined. + * Thus prevents appearing default browser tooltip on option hover (attribute `title` that gets rendered on li element) + * + * @param {EuiSelectableOption[]} options + * @returns {EuiSelectableOption[]} modified options + */ + +export interface SelectableProviderProps { + isLoading: boolean; + getSelectableOptions: (searchProviderValue?: string) => EuiSelectableOption[]; + onClosePopover: () => void; + onProviderChange: (provider?: string) => void; +} + +const SelectableProviderComponent: React.FC = ({ + isLoading, + getSelectableOptions, + onClosePopover, + onProviderChange, +}) => { + const [searchProviderValue, setSearchProviderValue] = useState(''); + const onSearchProvider = useCallback( + (val: string) => { + setSearchProviderValue(val); + }, + [setSearchProviderValue] + ); + + const renderProviderOption = useCallback>( + (option, searchValue) => { + const provider = SERVICE_PROVIDERS[option.label as ServiceProviderKeys]; + return ( + + + + + + + + + + + + + + {provider && + provider.solutions.map((solution) => ( + + {solution} + + ))} + + + + ); + }, + [] + ); + + const handleProviderChange = useCallback>( + (options) => { + const selectedProvider = options.filter((option) => option.checked === 'on'); + if (selectedProvider != null && selectedProvider.length > 0) { + onProviderChange(selectedProvider[0].label); + } + onClosePopover(); + }, + [onClosePopover, onProviderChange] + ); + + const EuiSelectableContent = useCallback>( + (list, search) => ( + <> + {search} + {list} + + ), + [] + ); + + const searchProps: EuiSelectableProps['searchProps'] = useMemo( + () => ({ + 'data-test-subj': 'provider-super-select-search-box', + placeholder: i18n.translate( + 'xpack.stackConnectors.components.inference.selectable.providerSearch', + { + defaultMessage: 'Search', + } + ), + onSearch: onSearchProvider, + incremental: false, + compressed: true, + fullWidth: true, + }), + [onSearchProvider] + ); + + return ( + + {EuiSelectableContent} + + ); +}; + +export const SelectableProvider = memo(SelectableProviderComponent); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/translations.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/translations.ts new file mode 100644 index 00000000000000..d73164ed2db9eb --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/translations.ts @@ -0,0 +1,107 @@ +/* + * 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 getRequiredMessage = (field: string) => { + return i18n.translate('xpack.stackConnectors.components.inference.requiredGenericTextField', { + defaultMessage: '{field} is required.', + values: { field }, + }); +}; + +export const INPUT_INVALID = i18n.translate( + 'xpack.stackConnectors.inference.params.error.invalidInputText', + { + defaultMessage: 'Input does not have a valid Array format.', + } +); + +export const INVALID_ACTION = i18n.translate( + 'xpack.stackConnectors.components.inference.invalidActionText', + { + defaultMessage: 'Invalid action name.', + } +); + +export const BODY = i18n.translate('xpack.stackConnectors.components.inference.bodyFieldLabel', { + defaultMessage: 'Body', +}); + +export const INPUT = i18n.translate( + 'xpack.stackConnectors.components.inference.completionInputLabel', + { + defaultMessage: 'Input', + } +); + +export const INPUT_TYPE = i18n.translate( + 'xpack.stackConnectors.components.inference.completionInputTypeLabel', + { + defaultMessage: 'Input type', + } +); + +export const QUERY = i18n.translate('xpack.stackConnectors.components.inference.rerankQueryLabel', { + defaultMessage: 'Query', +}); + +export const BODY_DESCRIPTION = i18n.translate( + 'xpack.stackConnectors.components.inference.bodyCodeEditorAriaLabel', + { + defaultMessage: 'Code editor', + } +); + +export const TASK_TYPE = i18n.translate( + 'xpack.stackConnectors.components.inference.taskTypeFieldLabel', + { + defaultMessage: 'Task type', + } +); + +export const PROVIDER = i18n.translate( + 'xpack.stackConnectors.components.inference.providerFieldLabel', + { + defaultMessage: 'Provider', + } +); + +export const PROVIDER_REQUIRED = i18n.translate( + 'xpack.stackConnectors.components.inference.error.requiredProviderText', + { + defaultMessage: 'Provider is required.', + } +); + +export const DOCUMENTATION = i18n.translate( + 'xpack.stackConnectors.components.inference.documentation', + { + defaultMessage: 'Inference API documentation', + } +); + +export const SELECT_PROVIDER = i18n.translate( + 'xpack.stackConnectors.components.inference.selectProvider', + { + defaultMessage: 'Select a service', + } +); + +export const COPY_TOOLTIP = i18n.translate( + 'xpack.stackConnectors.components.inference.copy.tooltip', + { + defaultMessage: 'Copy to clipboard', + } +); + +export const COPIED_TOOLTIP = i18n.translate( + 'xpack.stackConnectors.components.inference.copied.tooltip', + { + defaultMessage: 'Copied!', + } +); diff --git a/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/inference/types.ts new file mode 100644 index 00000000000000..150292894b6430 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/inference/types.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UserConfiguredActionConnector } from '@kbn/triggers-actions-ui-plugin/public/types'; +import { ActionTypeModel as ConnectorTypeModel } from '@kbn/triggers-actions-ui-plugin/public'; +import { SUB_ACTION } from '../../../common/inference/constants'; +import { + ChatCompleteParams, + RerankParams, + SparseEmbeddingParams, + TextEmbeddingParams, +} from '../../../common/inference/types'; +import { ConfigProperties } from '../lib/dynamic_config/types'; + +export type InferenceActionParams = + | { subAction: SUB_ACTION.COMPLETION; subActionParams: ChatCompleteParams } + | { subAction: SUB_ACTION.RERANK; subActionParams: RerankParams } + | { subAction: SUB_ACTION.SPARSE_EMBEDDING; subActionParams: SparseEmbeddingParams } + | { subAction: SUB_ACTION.TEXT_EMBEDDING; subActionParams: TextEmbeddingParams }; + +export type FieldsConfiguration = Record; + +export interface Config { + taskType: string; + taskTypeConfig?: Record; + inferenceId: string; + provider: string; + providerConfig?: Record; +} + +export interface Secrets { + providerSecrets?: Record; +} + +export type InferenceConnector = ConnectorTypeModel; + +export type InferenceActionConnector = UserConfiguredActionConnector; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx new file mode 100644 index 00000000000000..79ae552a9528af --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_field.tsx @@ -0,0 +1,360 @@ +/* + * 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, { useEffect, useState } from 'react'; + +import { + EuiAccordion, + EuiFieldText, + EuiFieldPassword, + EuiSwitch, + EuiTextArea, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiFieldNumber, + EuiCheckableCard, + useGeneratedHtmlId, + EuiSpacer, + EuiSuperSelect, + EuiText, +} from '@elastic/eui'; + +import { isEmpty } from 'lodash/fp'; +import { + ensureBooleanType, + ensureCorrectTyping, + ensureStringType, +} from './connector_configuration_utils'; +import { ConfigEntryView, DisplayType } from './types'; + +interface ConnectorConfigurationFieldProps { + configEntry: ConfigEntryView; + isLoading: boolean; + setConfigValue: (value: number | string | boolean | null) => void; +} + +interface ConfigInputFieldProps { + configEntry: ConfigEntryView; + isLoading: boolean; + validateAndSetConfigValue: (value: string | boolean) => void; +} +export const ConfigInputField: React.FC = ({ + configEntry, + isLoading, + validateAndSetConfigValue, +}) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { isValid, placeholder, value, default_value, key } = configEntry; + const [innerValue, setInnerValue] = useState( + !value || value.toString().length === 0 ? default_value : value + ); + + useEffect(() => { + setInnerValue(!value || value.toString().length === 0 ? default_value : value); + }, [default_value, value]); + return ( + { + setInnerValue(event.target.value); + validateAndSetConfigValue(event.target.value); + }} + placeholder={placeholder} + /> + ); +}; + +export const ConfigSwitchField: React.FC = ({ + configEntry, + isLoading, + validateAndSetConfigValue, +}) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { label, value, default_value, key } = configEntry; + const [innerValue, setInnerValue] = useState(value ?? default_value); + useEffect(() => { + setInnerValue(value ?? default_value); + }, [default_value, value]); + return ( + {label}

} + onChange={(event) => { + setInnerValue(event.target.checked); + validateAndSetConfigValue(event.target.checked); + }} + /> + ); +}; + +export const ConfigInputTextArea: React.FC = ({ + isLoading, + configEntry, + validateAndSetConfigValue, +}) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { isValid, placeholder, value, default_value, key } = configEntry; + const [innerValue, setInnerValue] = useState(value ?? default_value); + useEffect(() => { + setInnerValue(value ?? default_value); + }, [default_value, value]); + return ( + { + setInnerValue(event.target.value); + validateAndSetConfigValue(event.target.value); + }} + placeholder={placeholder} + /> + ); +}; + +export const ConfigNumberField: React.FC = ({ + configEntry, + isLoading, + validateAndSetConfigValue, +}) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { isValid, placeholder, value, default_value, key } = configEntry; + const [innerValue, setInnerValue] = useState(value ?? default_value); + useEffect(() => { + setInnerValue(!value || value.toString().length === 0 ? default_value : value); + }, [default_value, value]); + return ( + { + const newValue = isEmpty(event.target.value) ? '0' : event.target.value; + setInnerValue(newValue); + validateAndSetConfigValue(newValue); + }} + placeholder={placeholder} + /> + ); +}; + +export const ConfigCheckableField: React.FC = ({ + configEntry, + validateAndSetConfigValue, +}) => { + const radioCardId = useGeneratedHtmlId({ prefix: 'radioCard' }); + // eslint-disable-next-line @typescript-eslint/naming-convention + const { value, options, default_value } = configEntry; + const [innerValue, setInnerValue] = useState(value ?? default_value); + useEffect(() => { + setInnerValue(value ?? default_value); + }, [default_value, value]); + return ( + <> + {options?.map((o) => ( + <> + { + setInnerValue(o.value); + validateAndSetConfigValue(o.value); + }} + /> + + + ))} + + ); +}; + +export const ConfigSensitiveTextArea: React.FC = ({ + isLoading, + configEntry, + validateAndSetConfigValue, +}) => { + const { key, label } = configEntry; + return ( + {label}

}> + +
+ ); +}; + +export const ConfigInputPassword: React.FC = ({ + isLoading, + configEntry, + validateAndSetConfigValue, +}) => { + const { value, key } = configEntry; + const [innerValue, setInnerValue] = useState(value ?? null); + useEffect(() => { + setInnerValue(value ?? null); + }, [value]); + return ( + <> + { + setInnerValue(event.target.value); + validateAndSetConfigValue(event.target.value); + }} + /> + + ); +}; + +export const ConfigSelectField: React.FC = ({ + configEntry, + isLoading, + validateAndSetConfigValue, +}) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { isValid, options, value, default_value } = configEntry; + const [innerValue, setInnerValue] = useState(value ?? default_value); + const optionsRes = options?.map((o) => ({ + value: o.value, + inputDisplay: ( + + {o.icon ? ( + + + + ) : null} + + {o.label} + + + ), + })); + return ( + { + setInnerValue(newValue); + validateAndSetConfigValue(newValue); + }} + /> + ); +}; + +export const ConnectorConfigurationField: React.FC = ({ + configEntry, + isLoading, + setConfigValue, +}) => { + const validateAndSetConfigValue = (value: number | string | boolean) => { + setConfigValue(ensureCorrectTyping(configEntry.type, value)); + }; + + const { key, display, sensitive } = configEntry; + + switch (display) { + case DisplayType.DROPDOWN: + return ( + + ); + + case DisplayType.CHECKABLE: + return ( + + ); + + case DisplayType.NUMERIC: + return ( + + ); + + case DisplayType.TEXTAREA: + const textarea = ( + + ); + + return sensitive ? ( + <> + + + ) : ( + textarea + ); + + case DisplayType.TOGGLE: + return ( + + ); + + default: + return sensitive ? ( + + ) : ( + + ); + } +}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.tsx new file mode 100644 index 00000000000000..3190ac80275f1b --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_form_items.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 { + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiPanel, + EuiSpacer, + EuiText, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { ConfigEntryView, DisplayType } from './types'; +import { ConnectorConfigurationField } from './connector_configuration_field'; + +interface ConnectorConfigurationFormItemsProps { + isLoading: boolean; + items: ConfigEntryView[]; + setConfigEntry: (key: string, value: string | number | boolean | null) => void; + direction?: 'column' | 'row' | 'rowReverse' | 'columnReverse' | undefined; + itemsGrow?: boolean; +} + +export const ConnectorConfigurationFormItems: React.FC = ({ + isLoading, + items, + setConfigEntry, + direction, + itemsGrow, +}) => { + return ( + + {items.map((configEntry) => { + const { + depends_on: dependencies, + key, + display, + isValid, + label, + sensitive, + tooltip, + validationErrors, + required, + } = configEntry; + + const helpText = tooltip; + // toggle and sensitive textarea labels go next to the element, not in the row + const rowLabel = + display === DisplayType.TOGGLE || (display === DisplayType.TEXTAREA && sensitive) ? ( + <> + ) : tooltip ? ( + + +

{label}

+
+
+ ) : ( +

{label}

+ ); + + const optionalLabel = !required ? ( + + {i18n.translate('xpack.stackConnectors.components.inference.config.optionalValue', { + defaultMessage: 'Optional', + })} + + ) : undefined; + + if (dependencies?.length > 0) { + return ( + + + + { + setConfigEntry(configEntry.key, value); + }} + /> + + + + ); + } + return ( + + + { + setConfigEntry(configEntry.key, value); + }} + /> + + {configEntry.sensitive ? ( + <> + + + + ) : null} + + ); + })} +
+ ); +}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.ts new file mode 100644 index 00000000000000..182327a180a63a --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/connector_configuration_utils.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 { ConfigProperties, FieldType } from './types'; + +export type ConnectorConfigEntry = ConfigProperties & { key: string }; + +export const validIntInput = (value: string | number | boolean | null): boolean => { + // reject non integers (including x.0 floats), but don't validate if empty + return (value !== null || value !== '') && + (isNaN(Number(value)) || + !Number.isSafeInteger(Number(value)) || + ensureStringType(value).indexOf('.') >= 0) + ? false + : true; +}; + +export const ensureCorrectTyping = ( + type: FieldType, + value: string | number | boolean | null +): string | number | boolean | null => { + switch (type) { + case FieldType.INTEGER: + return validIntInput(value) ? ensureIntType(value) : value; + case FieldType.BOOLEAN: + return ensureBooleanType(value); + default: + return ensureStringType(value); + } +}; + +export const ensureStringType = (value: string | number | boolean | null): string => { + return value !== null ? String(value) : ''; +}; + +export const ensureIntType = (value: string | number | boolean | null): number | null => { + // int is null-safe to prevent empty values from becoming zeroes + if (value === null || value === '') { + return null; + } + + return parseInt(String(value), 10); +}; + +export const ensureBooleanType = (value: string | number | boolean | null): boolean => { + return Boolean(value); +}; + +export const hasUiRestrictions = (configEntry: Partial) => { + return (configEntry.ui_restrictions ?? []).length > 0; +}; diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/types.ts b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/types.ts new file mode 100644 index 00000000000000..40e17a19890757 --- /dev/null +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/dynamic_config/types.ts @@ -0,0 +1,71 @@ +/* + * 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 enum DisplayType { + TEXTBOX = 'textbox', + TEXTAREA = 'textarea', + NUMERIC = 'numeric', + TOGGLE = 'toggle', + DROPDOWN = 'dropdown', + CHECKABLE = 'checkable', +} + +export interface SelectOption { + label: string; + value: string; + icon?: string; +} + +export interface Dependency { + field: string; + value: string | number | boolean | null; +} + +export enum FieldType { + STRING = 'str', + INTEGER = 'int', + LIST = 'list', + BOOLEAN = 'bool', +} + +export interface ConfigCategoryProperties { + label: string; + order: number; + type: 'category'; +} + +export interface Validation { + constraint: string | number; + type: string; +} + +export interface ConfigProperties { + category?: string; + default_value: string | number | boolean | null; + depends_on: Dependency[]; + display: DisplayType; + label: string; + options?: SelectOption[]; + order?: number | null; + placeholder?: string; + required: boolean; + sensitive: boolean; + tooltip: string | null; + type: FieldType; + ui_restrictions: string[]; + validations: Validation[]; + value: string | number | boolean | null; +} + +interface ConfigEntry extends ConfigProperties { + key: string; +} + +export interface ConfigEntryView extends ConfigEntry { + isValid: boolean; + validationErrors: string[]; +} diff --git a/x-pack/plugins/stack_connectors/server/connector_types/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/index.ts index 2c905471761ed9..09d8a44c2a2875 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/index.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/index.ts @@ -20,6 +20,7 @@ import { getConnectorType as getIndexConnectorType } from './es_index'; import { getConnectorType as getOpenAIConnectorType } from './openai'; import { getConnectorType as getBedrockConnectorType } from './bedrock'; import { getConnectorType as getGeminiConnectorType } from './gemini'; +import { getConnectorType as getInferenceConnectorType } from './inference'; import { getConnectorType as getPagerDutyConnectorType } from './pagerduty'; import { getConnectorType as getSwimlaneConnectorType } from './swimlane'; import { getConnectorType as getServerLogConnectorType } from './server_log'; @@ -118,4 +119,7 @@ export function registerConnectorTypes({ if (experimentalFeatures.crowdstrikeConnectorOn) { actions.registerSubActionConnectorType(getCrowdstrikeConnectorType()); } + if (experimentalFeatures.inferenceConnectorOn) { + actions.registerSubActionConnectorType(getInferenceConnectorType()); + } } diff --git a/x-pack/plugins/stack_connectors/server/connector_types/inference/index.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/inference/index.test.ts new file mode 100644 index 00000000000000..b764a318df5dde --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/inference/index.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { ActionsConfigurationUtilities } from '@kbn/actions-plugin/server/actions_config'; +import { configValidator, getConnectorType } from '.'; +import { Config, Secrets } from '../../../common/inference/types'; +import { SubActionConnectorType } from '@kbn/actions-plugin/server/sub_action_framework/types'; +import { DEFAULT_PROVIDER, DEFAULT_TASK_TYPE } from '../../../common/inference/constants'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { InferencePutResponse } from '@elastic/elasticsearch/lib/api/types'; + +let connectorType: SubActionConnectorType; +let configurationUtilities: jest.Mocked; + +const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + +const mockResponse: Promise = Promise.resolve({ + inference_id: 'test', + service: 'openai', + service_settings: {}, + task_settings: {}, + task_type: 'completion', +}); + +describe('AI Connector', () => { + beforeEach(() => { + configurationUtilities = actionsConfigMock.create(); + connectorType = getConnectorType(); + }); + test('exposes the connector as `AI Connector` with id `.inference`', () => { + mockEsClient.inference.put.mockResolvedValue(mockResponse); + expect(connectorType.id).toEqual('.inference'); + expect(connectorType.name).toEqual('AI Connector'); + }); + describe('config validation', () => { + test('config validation passes when only required fields are provided', () => { + const config: Config = { + providerConfig: { + url: 'https://api.openai.com/v1/chat/completions', + }, + provider: DEFAULT_PROVIDER, + taskType: DEFAULT_TASK_TYPE, + inferenceId: 'test', + taskTypeConfig: {}, + }; + + expect(configValidator(config, { configurationUtilities })).toEqual(config); + }); + + test('config validation failed when the task type is empty', () => { + const config: Config = { + providerConfig: {}, + provider: 'openai', + taskType: '', + inferenceId: 'test', + taskTypeConfig: {}, + }; + expect(() => { + configValidator(config, { configurationUtilities }); + }).toThrowErrorMatchingInlineSnapshot( + `"Error configuring Inference API action: Error: Task type is not supported by Inference Endpoint."` + ); + }); + + test('config validation failed when the provider is empty', () => { + const config: Config = { + providerConfig: {}, + provider: '', + taskType: DEFAULT_TASK_TYPE, + inferenceId: 'test', + taskTypeConfig: {}, + }; + expect(() => { + configValidator(config, { configurationUtilities }); + }).toThrowErrorMatchingInlineSnapshot( + `"Error configuring Inference API action: Error: API Provider is not supported by Inference Endpoint."` + ); + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/inference/index.ts b/x-pack/plugins/stack_connectors/server/connector_types/inference/index.ts new file mode 100644 index 00000000000000..18af48bc18a510 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/inference/index.ts @@ -0,0 +1,182 @@ +/* + * 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 { + SubActionConnectorType, + ValidatorType, +} from '@kbn/actions-plugin/server/sub_action_framework/types'; +import { + GenerativeAIForSearchPlaygroundConnectorFeatureId, + GenerativeAIForSecurityConnectorFeatureId, +} from '@kbn/actions-plugin/common'; +import { ValidatorServices } from '@kbn/actions-plugin/server/types'; +import { GenerativeAIForObservabilityConnectorFeatureId } from '@kbn/actions-plugin/common/connector_feature_config'; +import { InferenceTaskType } from '@elastic/elasticsearch/lib/api/types'; +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { + INFERENCE_CONNECTOR_TITLE, + INFERENCE_CONNECTOR_ID, + ServiceProviderKeys, + SUB_ACTION, +} from '../../../common/inference/constants'; +import { ConfigSchema, SecretsSchema } from '../../../common/inference/schema'; +import { Config, Secrets } from '../../../common/inference/types'; +import { InferenceConnector } from './inference'; +import { unflattenObject } from '../lib/unflatten_object'; + +const deleteInferenceEndpoint = async ( + inferenceId: string, + taskType: InferenceTaskType, + logger: Logger, + esClient: ElasticsearchClient +) => { + try { + await esClient.inference.delete({ + task_type: taskType, + inference_id: inferenceId, + }); + logger.debug( + `Inference endpoint for task type "${taskType}" and inference id ${inferenceId} was successfuly deleted` + ); + } catch (e) { + logger.warn( + `Failed to delete inference endpoint for task type "${taskType}" and inference id ${inferenceId}. Error: ${e.message}` + ); + throw e; + } +}; + +export const getConnectorType = (): SubActionConnectorType => ({ + id: INFERENCE_CONNECTOR_ID, + name: INFERENCE_CONNECTOR_TITLE, + getService: (params) => new InferenceConnector(params), + schema: { + config: ConfigSchema, + secrets: SecretsSchema, + }, + validators: [{ type: ValidatorType.CONFIG, validator: configValidator }], + supportedFeatureIds: [ + GenerativeAIForSecurityConnectorFeatureId, + GenerativeAIForSearchPlaygroundConnectorFeatureId, + GenerativeAIForObservabilityConnectorFeatureId, + ], + minimumLicenseRequired: 'enterprise' as const, + preSaveHook: async ({ config, secrets, logger, services, isUpdate }) => { + const esClient = services.scopedClusterClient.asInternalUser; + try { + const taskSettings = config?.taskTypeConfig + ? { + ...unflattenObject(config?.taskTypeConfig), + } + : {}; + const serviceSettings = { + ...unflattenObject(config?.providerConfig ?? {}), + ...unflattenObject(secrets?.providerSecrets ?? {}), + }; + + let inferenceExists = false; + try { + await esClient?.inference.get({ + inference_id: config?.inferenceId, + task_type: config?.taskType as InferenceTaskType, + }); + inferenceExists = true; + } catch (e) { + /* throws error if inference endpoint by id does not exist */ + } + if (!isUpdate && inferenceExists) { + throw new Error( + `Inference with id ${config?.inferenceId} and task type ${config?.taskType} already exists.` + ); + } + + if (isUpdate && inferenceExists && config && config.provider) { + // TODO: replace, when update API for inference endpoint exists + await deleteInferenceEndpoint( + config.inferenceId, + config.taskType as InferenceTaskType, + logger, + esClient + ); + } + + await esClient?.inference.put({ + inference_id: config?.inferenceId ?? '', + task_type: config?.taskType as InferenceTaskType, + inference_config: { + service: config!.provider, + service_settings: serviceSettings, + task_settings: taskSettings, + }, + }); + logger.debug( + `Inference endpoint for task type "${config?.taskType}" and inference id ${ + config?.inferenceId + } was successfuly ${isUpdate ? 'updated' : 'created'}` + ); + } catch (e) { + logger.warn( + `Failed to ${isUpdate ? 'update' : 'create'} inference endpoint for task type "${ + config?.taskType + }" and inference id ${config?.inferenceId}. Error: ${e.message}` + ); + throw e; + } + }, + postSaveHook: async ({ config, logger, services, wasSuccessful, isUpdate }) => { + if (!wasSuccessful && !isUpdate) { + const esClient = services.scopedClusterClient.asInternalUser; + await deleteInferenceEndpoint( + config.inferenceId, + config.taskType as InferenceTaskType, + logger, + esClient + ); + } + }, + postDeleteHook: async ({ config, logger, services }) => { + const esClient = services.scopedClusterClient.asInternalUser; + await deleteInferenceEndpoint( + config.inferenceId, + config.taskType as InferenceTaskType, + logger, + esClient + ); + }, +}); + +export const configValidator = (configObject: Config, validatorServices: ValidatorServices) => { + try { + const { provider, taskType } = configObject; + if (!Object.keys(ServiceProviderKeys).includes(provider)) { + throw new Error( + `API Provider is not supported${ + provider && provider.length ? `: ${provider}` : `` + } by Inference Endpoint.` + ); + } + + if (!Object.keys(SUB_ACTION).includes(taskType.toUpperCase())) { + throw new Error( + `Task type is not supported${ + taskType && taskType.length ? `: ${taskType}` : `` + } by Inference Endpoint.` + ); + } + return configObject; + } catch (err) { + throw new Error( + i18n.translate('xpack.stackConnectors.inference.configurationErrorApiProvider', { + defaultMessage: 'Error configuring Inference API action: {err}', + values: { + err: err.toString(), + }, + }) + ); + } +}; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.test.ts new file mode 100644 index 00000000000000..a79bd0360598bd --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.test.ts @@ -0,0 +1,310 @@ +/* + * 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 { InferenceConnector } from './inference'; +import { actionsConfigMock } from '@kbn/actions-plugin/server/actions_config.mock'; +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { actionsMock } from '@kbn/actions-plugin/server/mocks'; +import { PassThrough, Transform } from 'stream'; +import {} from '@kbn/actions-plugin/server/types'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { InferenceInferenceResponse } from '@elastic/elasticsearch/lib/api/types'; + +const OPENAI_CONNECTOR_ID = '123'; +const DEFAULT_OPENAI_MODEL = 'gpt-4o'; + +describe('InferenceConnector', () => { + let mockError: jest.Mock; + const logger = loggingSystemMock.createLogger(); + const mockResponse: InferenceInferenceResponse = { + completion: [ + { + result: + 'Elastic is a company known for developing the Elasticsearch search and analytics engine, which allows for real-time data search, analysis, and visualization. Elasticsearch is part of the larger Elastic Stack (also known as the ELK Stack), which includes:\n\n1. **Elasticsearch**: A distributed, RESTful search and analytics engine capable of addressing a growing number of use cases. As the heart of the Elastic Stack, it centrally stores your data so you can discover the expected and uncover the unexpected.\n \n2. **Logstash**: A server-side data processing pipeline that ingests data from multiple sources simultaneously, transforms it, and sends it to your preferred "stash," such as Elasticsearch.\n \n3. **Kibana**: A data visualization dashboard for Elasticsearch. It allows you to search, view, and interact with data stored in Elasticsearch indices. You can perform advanced data analysis and visualize data in various charts, tables, and maps.\n\n4. **Beats**: Lightweight data shippers for different types of data. They send data from hundreds or thousands of machines and systems to Elasticsearch or Logstash.\n\nThe Elastic Stack is commonly used for various applications, such as log and event data analysis, full-text search, security analytics, business analytics, and more. It is employed across many industries to derive insights from large volumes of structured and unstructured data.\n\nElastic offers both open-source and paid versions of its software, providing a variety of features ranging from basic data ingestion and visualization to advanced machine learning and security capabilities.', + }, + ], + }; + + describe('performApiCompletion', () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + + beforeEach(() => { + mockEsClient.inference.inference.mockResolvedValue(mockResponse); + mockError = jest.fn().mockImplementation(() => { + throw new Error('API Error'); + }); + }); + + const services = actionsMock.createServices(); + services.scopedClusterClient = mockEsClient; + const connector = new InferenceConnector({ + configurationUtilities: actionsConfigMock.create(), + connector: { id: '1', type: OPENAI_CONNECTOR_ID }, + config: { + provider: 'openai', + providerConfig: { + url: 'https://api.openai.com/v1/chat/completions', + model_id: DEFAULT_OPENAI_MODEL, + }, + taskType: 'completion', + inferenceId: 'test', + taskTypeConfig: {}, + }, + secrets: { providerSecrets: { api_key: '123' } }, + logger, + services, + }); + + it('uses the completion task_type is supplied', async () => { + const response = await connector.performApiCompletion({ + input: 'What is Elastic?', + }); + expect(mockEsClient.inference.inference).toBeCalledTimes(1); + expect(mockEsClient.inference.inference).toHaveBeenCalledWith( + { + inference_id: 'test', + input: 'What is Elastic?', + task_type: 'completion', + }, + { asStream: false } + ); + expect(response).toEqual(mockResponse.completion); + }); + + it('errors during API calls are properly handled', async () => { + // @ts-ignore + mockEsClient.inference.inference = mockError; + + await expect(connector.performApiCompletion({ input: 'What is Elastic?' })).rejects.toThrow( + 'API Error' + ); + }); + }); + + describe('performApiRerank', () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + const mockResponseRerank = { + rerank: [ + { + index: 2, + score: 0.011597361, + text: 'leia', + }, + { + index: 0, + score: 0.006338922, + text: 'luke', + }, + ], + }; + + beforeEach(() => { + mockEsClient.inference.inference.mockResolvedValue(mockResponseRerank); + mockError = jest.fn().mockImplementation(() => { + throw new Error('API Error'); + }); + }); + const services = actionsMock.createServices(); + services.scopedClusterClient = mockEsClient; + it('the API call is successful with correct parameters', async () => { + const connectorRerank = new InferenceConnector({ + configurationUtilities: actionsConfigMock.create(), + connector: { id: '1', type: '123' }, + config: { + provider: 'googlevertexai', + providerConfig: { + model_id: DEFAULT_OPENAI_MODEL, + }, + taskType: 'rerank', + inferenceId: 'test-rerank', + taskTypeConfig: {}, + }, + secrets: { providerSecrets: { api_key: '123' } }, + logger, + services, + }); + const response = await connectorRerank.performApiRerank({ + input: ['apple', 'banana'], + query: 'test', + }); + expect(mockEsClient.inference.inference).toHaveBeenCalledWith( + { + inference_id: 'test-rerank', + input: ['apple', 'banana'], + query: 'test', + task_type: 'rerank', + }, + { asStream: false } + ); + expect(response).toEqual(mockResponseRerank.rerank); + }); + }); + + describe('performApiTextEmbedding', () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + + beforeEach(() => { + mockEsClient.inference.inference.mockResolvedValue(mockResponse); + mockError = jest.fn().mockImplementation(() => { + throw new Error('API Error'); + }); + }); + + const services = actionsMock.createServices(); + services.scopedClusterClient = mockEsClient; + const connectorTextEmbedding = new InferenceConnector({ + configurationUtilities: actionsConfigMock.create(), + connector: { id: '1', type: OPENAI_CONNECTOR_ID }, + config: { + providerConfig: { + url: 'https://My-test-resource-123.openai.azure.com/openai/deployments/NEW-DEPLOYMENT-321/chat/completions?api-version=2023-05-15', + }, + provider: 'elasticsearch', + taskType: '', + inferenceId: '', + taskTypeConfig: {}, + }, + secrets: { providerSecrets: {} }, + logger: loggingSystemMock.createLogger(), + services, + }); + + it('test the AzureAI API call is successful with correct parameters', async () => { + const response = await connectorTextEmbedding.performApiTextEmbedding({ + input: 'Hello world', + inputType: 'ingest', + }); + expect(mockEsClient.inference.inference).toHaveBeenCalledWith( + { + inference_id: '', + input: 'Hello world', + task_settings: { + input_type: 'ingest', + }, + task_type: 'text_embedding', + }, + { asStream: false } + ); + expect(response).toEqual(mockResponse.text_embedding); + }); + + it('errors during API calls are properly handled', async () => { + // @ts-ignore + mockEsClient.inference.inference = mockError; + + await expect( + connectorTextEmbedding.performApiTextEmbedding({ + input: 'Hello world', + inputType: 'ingest', + }) + ).rejects.toThrow('API Error'); + }); + }); + + describe('performApiCompletionStream', () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + + const mockStream = ( + dataToStream: string[] = [ + 'data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"My"}}]}\ndata: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" new"}}]}', + ] + ) => { + const streamMock = createStreamMock(); + dataToStream.forEach((chunk) => { + streamMock.write(chunk); + }); + streamMock.complete(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + mockEsClient.inference.inference.mockResolvedValue(streamMock.transform as any); + }; + + beforeEach(() => { + // @ts-ignore + mockStream(); + }); + + const services = actionsMock.createServices(); + services.scopedClusterClient = mockEsClient; + const connector = new InferenceConnector({ + configurationUtilities: actionsConfigMock.create(), + connector: { id: '1', type: OPENAI_CONNECTOR_ID }, + config: { + providerConfig: { + url: 'https://My-test-resource-123.openai.azure.com/openai/deployments/NEW-DEPLOYMENT-321/chat/completions?api-version=2023-05-15', + }, + provider: 'elasticsearch', + taskType: 'completion', + inferenceId: '', + taskTypeConfig: {}, + }, + secrets: { providerSecrets: {} }, + logger: loggingSystemMock.createLogger(), + services, + }); + + it('the API call is successful with correct request parameters', async () => { + await connector.performApiCompletionStream({ input: 'Hello world' }); + expect(mockEsClient.inference.inference).toBeCalledTimes(1); + expect(mockEsClient.inference.inference).toHaveBeenCalledWith( + { + inference_id: '', + input: 'Hello world', + task_type: 'completion', + }, + { asStream: true } + ); + }); + + it('signal is properly passed to streamApi', async () => { + const signal = jest.fn() as unknown as AbortSignal; + await connector.performApiCompletionStream({ input: 'Hello world', signal }); + + expect(mockEsClient.inference.inference).toHaveBeenCalledWith( + { + inference_id: '', + input: 'Hello world', + task_type: 'completion', + }, + { asStream: true, signal } + ); + }); + + it('errors during API calls are properly handled', async () => { + // @ts-ignore + mockEsClient.inference.inference = mockError; + + await expect( + connector.performApiCompletionStream({ input: 'What is Elastic?' }) + ).rejects.toThrow('API Error'); + }); + + it('responds with a readable stream', async () => { + const response = await connector.performApiCompletionStream({ + input: 'What is Elastic?', + }); + expect(response instanceof PassThrough).toEqual(true); + }); + }); +}); + +function createStreamMock() { + const transform: Transform = new Transform({}); + + return { + write: (data: string) => { + transform.push(data); + }, + fail: () => { + transform.emit('error', new Error('Stream failed')); + transform.end(); + }, + transform, + complete: () => { + transform.end(); + }, + }; +} diff --git a/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.ts b/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.ts new file mode 100644 index 00000000000000..d9aa4bf044e1dd --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/inference/inference.ts @@ -0,0 +1,232 @@ +/* + * 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 { ServiceParams, SubActionConnector } from '@kbn/actions-plugin/server'; + +import { PassThrough, Stream } from 'stream'; +import { IncomingMessage } from 'http'; + +import { AxiosError } from 'axios'; +import { + InferenceInferenceRequest, + InferenceInferenceResponse, + InferenceTaskType, +} from '@elastic/elasticsearch/lib/api/types'; +import { + ChatCompleteParamsSchema, + RerankParamsSchema, + SparseEmbeddingParamsSchema, + TextEmbeddingParamsSchema, +} from '../../../common/inference/schema'; +import { + Config, + Secrets, + ChatCompleteParams, + ChatCompleteResponse, + StreamingResponse, + RerankParams, + RerankResponse, + SparseEmbeddingParams, + SparseEmbeddingResponse, + TextEmbeddingParams, + TextEmbeddingResponse, +} from '../../../common/inference/types'; +import { SUB_ACTION } from '../../../common/inference/constants'; + +export class InferenceConnector extends SubActionConnector { + // Not using Axios + protected getResponseErrorMessage(error: AxiosError): string { + throw new Error('Method not implemented.'); + } + + private inferenceId; + private taskType; + + constructor(params: ServiceParams) { + super(params); + + this.provider = this.config.provider; + this.taskType = this.config.taskType; + this.inferenceId = this.config.inferenceId; + this.logger = this.logger; + this.connectorID = this.connector.id; + this.connectorTokenClient = params.services.connectorTokenClient; + + this.registerSubActions(); + } + + private registerSubActions() { + this.registerSubAction({ + name: SUB_ACTION.COMPLETION, + method: 'performApiCompletion', + schema: ChatCompleteParamsSchema, + }); + + this.registerSubAction({ + name: SUB_ACTION.RERANK, + method: 'performApiRerank', + schema: RerankParamsSchema, + }); + + this.registerSubAction({ + name: SUB_ACTION.SPARSE_EMBEDDING, + method: 'performApiSparseEmbedding', + schema: SparseEmbeddingParamsSchema, + }); + + this.registerSubAction({ + name: SUB_ACTION.TEXT_EMBEDDING, + method: 'performApiTextEmbedding', + schema: TextEmbeddingParamsSchema, + }); + + this.registerSubAction({ + name: SUB_ACTION.COMPLETION_STREAM, + method: 'performApiCompletionStream', + schema: ChatCompleteParamsSchema, + }); + } + + /** + * responsible for making a esClient inference method to perform chat completetion task endpoint and returning the service response data + * @param input the text on which you want to perform the inference task. + * @signal abort signal + */ + public async performApiCompletion({ + input, + signal, + }: ChatCompleteParams & { signal?: AbortSignal }): Promise { + const response = await this.performInferenceApi( + { inference_id: this.inferenceId, input, task_type: 'completion' }, + false, + signal + ); + return response.completion!; + } + + /** + * responsible for making a esClient inference method to rerank task endpoint and returning the response data + * @param input the text on which you want to perform the inference task. input can be a single string or an array. + * @query the search query text + * @signal abort signal + */ + public async performApiRerank({ + input, + query, + signal, + }: RerankParams & { signal?: AbortSignal }): Promise { + const response = await this.performInferenceApi( + { + query, + inference_id: this.inferenceId, + input, + task_type: 'rerank', + }, + false, + signal + ); + return response.rerank!; + } + + /** + * responsible for making a esClient inference method sparse embedding task endpoint and returning the response data + * @param input the text on which you want to perform the inference task. + * @signal abort signal + */ + public async performApiSparseEmbedding({ + input, + signal, + }: SparseEmbeddingParams & { signal?: AbortSignal }): Promise { + const response = await this.performInferenceApi( + { inference_id: this.inferenceId, input, task_type: 'sparse_embedding' }, + false, + signal + ); + return response.sparse_embedding!; + } + + /** + * responsible for making a esClient inference method text embedding task endpoint and returning the response data + * @param input the text on which you want to perform the inference task. + * @signal abort signal + */ + public async performApiTextEmbedding({ + input, + inputType, + signal, + }: TextEmbeddingParams & { signal?: AbortSignal }): Promise { + const response = await this.performInferenceApi( + { + inference_id: this.inferenceId, + input, + task_type: 'text_embedding', + task_settings: { + input_type: inputType, + }, + }, + false, + signal + ); + return response.text_embedding!; + } + + /** + * private generic method to avoid duplication esClient inference inference execute. + * @param params InferenceInferenceRequest params. + * @param asStream defines the type of the responce, regular or stream + * @signal abort signal + */ + private async performInferenceApi( + params: InferenceInferenceRequest, + asStream: boolean = false, + signal?: AbortSignal + ): Promise { + try { + const response = await this.esClient?.inference.inference(params, { asStream, signal }); + this.logger.info( + `Perform Inference endpoint for task type "${this.taskType}" and inference id ${this.inferenceId}` + ); + // TODO: const usageMetadata = response?.data?.usageMetadata; + return response; + } catch (err) { + this.logger.error(`error perform inference endpoint API: ${err}`); + throw err; + } + } + + private async streamAPI({ + input, + signal, + }: ChatCompleteParams & { signal?: AbortSignal }): Promise { + const response = await this.performInferenceApi( + { inference_id: this.inferenceId, input, task_type: this.taskType as InferenceTaskType }, + true, + signal + ); + + return (response as unknown as Stream).pipe(new PassThrough()); + } + + /** + * takes input. It calls the streamApi method to make a + * request to the Inference API with the message. It then returns a Transform stream + * that pipes the response from the API through the transformToString function, + * which parses the proprietary response into a string of the response text alone + * @param input A message to be sent to the API + * @signal abort signal + */ + public async performApiCompletionStream({ + input, + signal, + }: ChatCompleteParams & { signal?: AbortSignal }): Promise { + const res = (await this.streamAPI({ + input, + signal, + })) as unknown as IncomingMessage; + return res; + } +} diff --git a/x-pack/plugins/stack_connectors/server/connector_types/lib/gen_ai/create_gen_ai_dashboard.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/gen_ai/create_gen_ai_dashboard.ts index ed15c31dc29c7b..ee5a3471a2d343 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/lib/gen_ai/create_gen_ai_dashboard.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/lib/gen_ai/create_gen_ai_dashboard.ts @@ -24,7 +24,7 @@ export const initDashboard = async ({ logger: Logger; savedObjectsClient: SavedObjectsClientContract; dashboardId: string; - genAIProvider: 'OpenAI' | 'Bedrock' | 'Gemini'; + genAIProvider: 'OpenAI' | 'Bedrock' | 'Gemini' | 'Inference'; }): Promise<{ success: boolean; error?: OutputError; diff --git a/x-pack/plugins/stack_connectors/server/connector_types/lib/gen_ai/gen_ai_dashboard.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/gen_ai/gen_ai_dashboard.ts index 144704b8af6777..5805dd7728ccfc 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/lib/gen_ai/gen_ai_dashboard.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/lib/gen_ai/gen_ai_dashboard.ts @@ -11,11 +11,15 @@ import { SavedObject } from '@kbn/core-saved-objects-common/src/server_types'; import { OPENAI_TITLE, OPENAI_CONNECTOR_ID } from '../../../../common/openai/constants'; import { BEDROCK_TITLE, BEDROCK_CONNECTOR_ID } from '../../../../common/bedrock/constants'; import { GEMINI_TITLE, GEMINI_CONNECTOR_ID } from '../../../../common/gemini/constants'; +import { + INFERENCE_CONNECTOR_TITLE, + INFERENCE_CONNECTOR_ID, +} from '../../../../common/inference/constants'; export const getDashboardTitle = (title: string) => `${title} Token Usage`; export const getDashboard = ( - genAIProvider: 'OpenAI' | 'Bedrock' | 'Gemini', + genAIProvider: 'OpenAI' | 'Bedrock' | 'Gemini' | 'Inference', dashboardId: string ): SavedObject => { let attributes = { @@ -42,6 +46,12 @@ export const getDashboard = ( dashboardTitle: getDashboardTitle(GEMINI_TITLE), actionTypeId: GEMINI_CONNECTOR_ID, }; + } else if (genAIProvider === 'Inference') { + attributes = { + provider: INFERENCE_CONNECTOR_TITLE, + dashboardTitle: getDashboardTitle(INFERENCE_CONNECTOR_TITLE), + actionTypeId: INFERENCE_CONNECTOR_ID, + }; } const ids: Record = { diff --git a/x-pack/plugins/stack_connectors/server/connector_types/lib/unflatten_object.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/unflatten_object.test.ts new file mode 100644 index 00000000000000..ccd11f7e929475 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/lib/unflatten_object.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { unflattenObject } from './unflatten_object'; + +describe('unflattenObject', () => { + test('should unflatten an object', () => { + const obj = { + a: true, + 'b.baz[0].a': false, + 'b.baz[0].b': 'foo', + 'b.baz[1]': 'bar', + 'b.baz[2]': true, + 'b.foo': 'bar', + 'b.baz[3][0]': 1, + 'b.baz[3][1]': 2, + 'c.b.foo': 'cheese', + }; + + expect(unflattenObject(obj)).toEqual({ + a: true, + b: { + foo: 'bar', + baz: [ + { + a: false, + b: 'foo', + }, + 'bar', + true, + [1, 2], + ], + }, + c: { + b: { + foo: 'cheese', + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/stack_connectors/server/connector_types/lib/unflatten_object.ts b/x-pack/plugins/stack_connectors/server/connector_types/lib/unflatten_object.ts new file mode 100644 index 00000000000000..23f2553223a352 --- /dev/null +++ b/x-pack/plugins/stack_connectors/server/connector_types/lib/unflatten_object.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 { set } from '@kbn/safer-lodash-set'; + +interface GenericObject { + [key: string]: unknown; +} +export const unflattenObject = (object: object): T => + Object.entries(object).reduce((acc, [key, value]) => { + set(acc, key, value); + return acc; + }, {} as T); diff --git a/x-pack/plugins/stack_connectors/server/plugin.test.ts b/x-pack/plugins/stack_connectors/server/plugin.test.ts index 0c9551f787ca03..7d11d152f8316e 100644 --- a/x-pack/plugins/stack_connectors/server/plugin.test.ts +++ b/x-pack/plugins/stack_connectors/server/plugin.test.ts @@ -131,7 +131,7 @@ describe('Stack Connectors Plugin', () => { name: 'Torq', }) ); - expect(actionsSetup.registerSubActionConnectorType).toHaveBeenCalledTimes(10); + expect(actionsSetup.registerSubActionConnectorType).toHaveBeenCalledTimes(11); expect(actionsSetup.registerSubActionConnectorType).toHaveBeenNthCalledWith( 1, expect.objectContaining({ diff --git a/x-pack/plugins/stack_connectors/tsconfig.json b/x-pack/plugins/stack_connectors/tsconfig.json index 8a37f4edaa0b0e..66c84d17408bc2 100644 --- a/x-pack/plugins/stack_connectors/tsconfig.json +++ b/x-pack/plugins/stack_connectors/tsconfig.json @@ -42,6 +42,8 @@ "@kbn/utility-types", "@kbn/task-manager-plugin", "@kbn/alerting-types", + "@kbn/alerts-ui-shared", + "@kbn/core-notifications-browser", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap b/x-pack/plugins/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap index 754d9f0c66b4b4..96ac62b9f03df1 100644 --- a/x-pack/plugins/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap +++ b/x-pack/plugins/task_manager/server/integration_tests/__snapshots__/task_cost_check.test.ts.snap @@ -106,6 +106,10 @@ Array [ "cost": 1, "taskType": "actions:.crowdstrike", }, + Object { + "cost": 1, + "taskType": "actions:.inference", + }, Object { "cost": 1, "taskType": "actions:.cases", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/header.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/header.tsx index 0b252ca6660c60..e3fb79520b93b5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/header.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/create_connector_flyout/header.tsx @@ -51,13 +51,17 @@ const FlyoutHeaderComponent: React.FC = ({

- + {actionTypeName && actionTypeName.toLowerCase().includes('connector') ? ( + actionTypeName + ) : ( + + )}

diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx index 78039c753a2769..22eb2673fd3538 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.test.tsx @@ -15,17 +15,23 @@ const renderWithSecretFields = ({ isEdit, isMissingSecrets, numberOfSecretFields, + isSecretFieldsHidden = false, }: { isEdit: boolean; isMissingSecrets: boolean; numberOfSecretFields: number; + isSecretFieldsHidden?: boolean; }): RenderResult => { return render( {Array.from({ length: numberOfSecretFields }).map((_, index) => { return ( - + ); })} @@ -67,10 +73,16 @@ describe('EncryptedFieldsCallout', () => { ], ]; - const noSecretsTests: Array<[{ isEdit: boolean; isMissingSecrets: boolean }, string]> = [ + const noSecretsTests: Array< + [{ isEdit: boolean; isMissingSecrets: boolean; isSecretFieldsHidden?: boolean }, string] + > = [ [{ isEdit: false, isMissingSecrets: false }, 'create-connector-secrets-callout'], [{ isEdit: true, isMissingSecrets: false }, 'edit-connector-secrets-callout'], [{ isEdit: false, isMissingSecrets: true }, 'missing-secrets-callout'], + [ + { isEdit: true, isMissingSecrets: true, isSecretFieldsHidden: true }, + 'edit-connector-secrets-callout', + ], ]; it.each(isCreateTests)( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx index 35cbb473c6a3f6..161fed865a4e61 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/encrypted_fields_callout.tsx @@ -96,7 +96,7 @@ const EncryptedFieldsCalloutComponent: React.FC = ( ); } - if (!isEdit) { + if (!isEdit && secretFieldsLabel.length) { return ( = ( ); } - if (isEdit) { + if (isEdit && secretFieldsLabel.length) { return ( + ) { + if (this.returnError) { + return InferenceSimulator.sendErrorResponse(response); + } + + return InferenceSimulator.sendResponse(response); + } + + private static sendResponse(response: http.ServerResponse) { + response.statusCode = 202; + response.setHeader('Content-Type', 'application/json'); + response.end(JSON.stringify(inferenceSuccessResponse, null, 4)); + } + + private static sendErrorResponse(response: http.ServerResponse) { + response.statusCode = 422; + response.setHeader('Content-Type', 'application/json;charset=UTF-8'); + response.end(JSON.stringify(inferenceFailedResponse, null, 4)); + } +} + +export const inferenceSuccessResponse = { + refid: '80be4a0d-5f0e-4d6c-b00e-8cb918f7df1f', +}; +export const inferenceFailedResponse = { + error: { + statusMessage: 'Bad job', + }, +}; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/inference.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/inference.ts new file mode 100644 index 00000000000000..f3f6361e84db40 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/inference.ts @@ -0,0 +1,518 @@ +/* + * 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 { IValidatedEvent } from '@kbn/event-log-plugin/server'; + +import { + InferenceSimulator, + inferenceSuccessResponse, +} from '@kbn/actions-simulators-plugin/server/inference_simulation'; +import { TaskErrorSource } from '@kbn/task-manager-plugin/common'; +import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; +import { getUrlPrefix, ObjectRemover } from '../../../../../common/lib'; +import { getEventLog } from '../../../../../common/lib'; + +const connectorTypeId = '.inference'; +const name = 'AI connector action'; +const secrets = { + apiKey: 'genAiApiKey', +}; + +const defaultConfig = { provider: 'openai' }; + +// eslint-disable-next-line import/no-default-export +export default function InferenceConnectorTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const objectRemover = new ObjectRemover(supertest); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const configService = getService('config'); + const retry = getService('retry'); + const createConnector = async (apiUrl: string, spaceId?: string) => { + const { body } = await supertest + .post(`${getUrlPrefix(spaceId ?? 'default')}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config: { ...defaultConfig, apiUrl }, + secrets, + }) + .expect(200); + + objectRemover.add(spaceId ?? 'default', body.id, 'connector', 'actions'); + + return body.id; + }; + + describe('OpenAI', () => { + after(async () => { + await objectRemover.removeAll(); + }); + describe('action creation', () => { + const simulator = new InferenceSimulator({ + returnError: false, + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + const config = { ...defaultConfig, apiUrl: '' }; + + before(async () => { + config.apiUrl = await simulator.start(); + }); + + after(() => { + simulator.close(); + }); + + it('should return 200 when creating the connector without a default model', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config, + secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + is_preconfigured: false, + is_system_action: false, + is_deprecated: false, + name, + connector_type_id: connectorTypeId, + is_missing_secrets: false, + config: { + ...config, + defaultModel: 'gpt-4o', + }, + }); + }); + + it('should return 200 when creating the connector with a default model', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config: { + ...config, + defaultModel: 'gpt-3.5-turbo', + }, + secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + is_preconfigured: false, + is_system_action: false, + is_deprecated: false, + name, + connector_type_id: connectorTypeId, + is_missing_secrets: false, + config: { + ...config, + defaultModel: 'gpt-3.5-turbo', + }, + }); + }); + + it('should return 400 Bad Request when creating the connector without the apiProvider', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A GenAi action', + connector_type_id: '.gen-ai', + config: { + apiUrl: config.apiUrl, + }, + secrets: { + apiKey: '123', + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: types that failed validation:\n- [0.apiProvider]: expected at least one defined value but got [undefined]\n- [1.apiProvider]: expected at least one defined value but got [undefined]', + }); + }); + }); + + it('should return 400 Bad Request when creating the connector without the apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config: defaultConfig, + secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: types that failed validation:\n- [0.apiProvider]: expected value to equal [Azure OpenAI]\n- [1.apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should return 400 Bad Request when creating the connector with a apiUrl that is not allowed', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config: { + ...defaultConfig, + apiUrl: 'http://genAi.mynonexistent.com', + }, + secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: Error configuring OpenAI action: Error: error validating url: target url "http://genAi.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', + }); + }); + }); + + it('should return 400 Bad Request when creating the connector without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name, + connector_type_id: connectorTypeId, + config, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: [apiKey]: expected value of type [string] but got [undefined]', + }); + }); + }); + }); + + describe('executor', () => { + describe('validation', () => { + const simulator = new InferenceSimulator({ + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + let genAiActionId: string; + + before(async () => { + const apiUrl = await simulator.start(); + genAiActionId = await createConnector(apiUrl); + }); + + after(() => { + simulator.close(); + }); + + it('should fail when the params is empty', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${genAiActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }); + expect(200); + + expect(body).to.eql({ + status: 'error', + connector_id: genAiActionId, + message: + 'error validating action params: [subAction]: expected value of type [string] but got [undefined]', + retry: false, + errorSource: TaskErrorSource.FRAMEWORK, + }); + }); + + it('should fail when the subAction is invalid', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${genAiActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'invalidAction' }, + }) + .expect(200); + + expect(body).to.eql({ + connector_id: genAiActionId, + status: 'error', + retry: true, + message: 'an error occurred while running the action', + errorSource: TaskErrorSource.FRAMEWORK, + service_message: `Sub action "invalidAction" is not registered. Connector id: ${genAiActionId}. Connector name: OpenAI. Connector type: .gen-ai`, + }); + }); + }); + + describe('execution', () => { + describe('successful response simulator', () => { + const simulator = new InferenceSimulator({ + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + let apiUrl: string; + let genAiActionId: string; + + before(async () => { + apiUrl = await simulator.start(); + genAiActionId = await createConnector(apiUrl); + }); + + after(() => { + simulator.close(); + }); + + it('should send a stringified JSON object', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${genAiActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'test', + subActionParams: { + body: '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"Hello world"}]}', + }, + }, + }) + .expect(200); + + expect(simulator.requestData).to.eql({ + model: 'gpt-3.5-turbo', + messages: [{ role: 'user', content: 'Hello world' }], + }); + expect(body).to.eql({ + status: 'ok', + connector_id: genAiActionId, + data: inferenceSuccessResponse, + }); + + const events: IValidatedEvent[] = await retry.try(async () => { + return await getEventLog({ + getService, + spaceId: 'default', + type: 'action', + id: genAiActionId, + provider: 'actions', + actions: new Map([ + ['execute-start', { equal: 1 }], + ['execute', { equal: 1 }], + ]), + }); + }); + + const executeEvent = events[1]; + expect(executeEvent?.kibana?.action?.execution?.usage?.request_body_bytes).to.be(78); + }); + describe('Token tracking dashboard', () => { + const dashboardId = 'specific-dashboard-id-default'; + + it('should not create a dashboard when user does not have kibana event log permissions', async () => { + const { body } = await supertestWithoutAuth + .post(`/api/actions/connector/${genAiActionId}/_execute`) + .auth('global_read', 'global_read-password') + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getDashboard', + subActionParams: { + dashboardId, + }, + }, + }) + .expect(200); + + // check dashboard has not been created + await supertest + .get(`/api/saved_objects/dashboard/${dashboardId}`) + .set('kbn-xsrf', 'foo') + .expect(404); + + expect(body).to.eql({ + status: 'ok', + connector_id: genAiActionId, + data: { available: false }, + }); + }); + + it('should create a dashboard when user has correct permissions', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${genAiActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getDashboard', + subActionParams: { + dashboardId, + }, + }, + }) + .expect(200); + + // check dashboard has been created + await retry.try(async () => + supertest + .get(`/api/saved_objects/dashboard/${dashboardId}`) + .set('kbn-xsrf', 'foo') + .expect(200) + ); + + objectRemover.add('default', dashboardId, 'dashboard', 'saved_objects'); + + expect(body).to.eql({ + status: 'ok', + connector_id: genAiActionId, + data: { available: true }, + }); + }); + }); + }); + describe('non-default space simulator', () => { + const simulator = new InferenceSimulator({ + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + let apiUrl: string; + let genAiActionId: string; + + before(async () => { + apiUrl = await simulator.start(); + genAiActionId = await createConnector(apiUrl, 'space1'); + }); + after(() => { + simulator.close(); + }); + + const dashboardId = 'specific-dashboard-id-space1'; + + it('should create a dashboard in non-default space', async () => { + const { body } = await supertest + .post(`${getUrlPrefix('space1')}/api/actions/connector/${genAiActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getDashboard', + subActionParams: { + dashboardId, + }, + }, + }) + .expect(200); + + // check dashboard has been created + await retry.try( + async () => + await supertest + .get(`${getUrlPrefix('space1')}/api/saved_objects/dashboard/${dashboardId}`) + .set('kbn-xsrf', 'foo') + .expect(200) + ); + objectRemover.add('space1', dashboardId, 'dashboard', 'saved_objects'); + + expect(body).to.eql({ + status: 'ok', + connector_id: genAiActionId, + data: { available: true }, + }); + }); + }); + + describe('error response simulator', () => { + const simulator = new InferenceSimulator({ + returnError: true, + proxy: { + config: configService.get('kbnTestServer.serverArgs'), + }, + }); + + let genAiActionId: string; + + before(async () => { + const apiUrl = await simulator.start(); + genAiActionId = await createConnector(apiUrl); + }); + + after(() => { + simulator.close(); + }); + + it('should return a failure when error happens', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${genAiActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .expect(200); + + expect(body).to.eql({ + status: 'error', + connector_id: genAiActionId, + message: + 'error validating action params: [subAction]: expected value of type [string] but got [undefined]', + retry: false, + errorSource: TaskErrorSource.FRAMEWORK, + }); + }); + + it('should return a error when error happens', async () => { + const { body } = await supertest + .post(`/api/actions/connector/${genAiActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'test', + subActionParams: { + body: '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"Hello world"}]}', + }, + }, + }) + .expect(200); + + expect(body).to.eql({ + status: 'error', + connector_id: genAiActionId, + message: 'an error occurred while running the action', + retry: true, + errorSource: TaskErrorSource.FRAMEWORK, + service_message: + 'Status code: 422. Message: API Error: Unprocessable Entity - The model `bad model` does not exist', + }); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts index 4b7dd28d63b5ce..10449613f6ef6e 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/check_registered_connector_types.ts @@ -53,6 +53,7 @@ export default function createRegisteredConnectorTypeTests({ getService }: FtrPr '.gen-ai', '.bedrock', '.gemini', + '.inference', '.sentinelone', '.cases', '.crowdstrike', diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts index fcb782b069dbfa..f5ccfcc3ca56f0 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts @@ -61,6 +61,7 @@ export default function ({ getService }: FtrProviderContext) { 'actions:.gemini', 'actions:.gen-ai', 'actions:.index', + 'actions:.inference', 'actions:.jira', 'actions:.observability-ai-assistant', 'actions:.opsgenie', From 6c4ac90f72a6d44df2f4affd63505a0150b2891b Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Sun, 13 Oct 2024 21:37:48 -0700 Subject: [PATCH 03/38] [Data Usage] Added AutoOps API service (#195844) ## Summary Summarize your PR. If it involves visual changes include a screenshot or gif. ### 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#_add_your_labels) - [ ] This will appear in the **Release Notes** and follow the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/data_usage/server/app_context.ts | 74 ++++++++ x-pack/plugins/data_usage/server/config.ts | 17 ++ x-pack/plugins/data_usage/server/plugin.ts | 19 +- .../data_usage/server/services/autoops_api.ts | 178 ++++++++++++++++++ .../plugins/data_usage/server/types/types.ts | 21 +++ x-pack/plugins/data_usage/tsconfig.json | 3 + 6 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/data_usage/server/app_context.ts create mode 100644 x-pack/plugins/data_usage/server/services/autoops_api.ts diff --git a/x-pack/plugins/data_usage/server/app_context.ts b/x-pack/plugins/data_usage/server/app_context.ts new file mode 100644 index 00000000000000..e339403e3baf59 --- /dev/null +++ b/x-pack/plugins/data_usage/server/app_context.ts @@ -0,0 +1,74 @@ +/* + * 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 { Observable } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; +import { kibanaPackageJson } from '@kbn/repo-info'; +import type { LoggerFactory } from '@kbn/core/server'; + +import type { CloudSetup } from '@kbn/cloud-plugin/server'; +import { DataUsageConfigType } from './config'; +import type { DataUsageContext } from './types'; + +class AppContextService { + private config$?: Observable; + private configSubject$?: BehaviorSubject; + private kibanaVersion: DataUsageContext['kibanaVersion'] = kibanaPackageJson.version; + private kibanaBranch: DataUsageContext['kibanaBranch'] = kibanaPackageJson.branch; + private kibanaInstanceId: DataUsageContext['kibanaInstanceId'] = ''; + private cloud?: CloudSetup; + private logFactory?: LoggerFactory; + + public start(appContext: DataUsageContext) { + this.cloud = appContext.cloud; + this.logFactory = appContext.logFactory; + this.kibanaVersion = appContext.kibanaVersion; + this.kibanaBranch = appContext.kibanaBranch; + this.kibanaInstanceId = appContext.kibanaInstanceId; + + if (appContext.config$) { + this.config$ = appContext.config$; + const initialValue = appContext.configInitialValue; + this.configSubject$ = new BehaviorSubject(initialValue); + } + } + + public stop() {} + + public getCloud() { + return this.cloud; + } + + public getLogger() { + if (!this.logFactory) { + throw new Error('Logger not set.'); + } + return this.logFactory; + } + + public getConfig() { + return this.configSubject$?.value; + } + + public getConfig$() { + return this.config$; + } + + public getKibanaVersion() { + return this.kibanaVersion; + } + + public getKibanaBranch() { + return this.kibanaBranch; + } + + public getKibanaInstanceId() { + return this.kibanaInstanceId; + } +} + +export const appContextService = new AppContextService(); diff --git a/x-pack/plugins/data_usage/server/config.ts b/x-pack/plugins/data_usage/server/config.ts index bf89431f2abea4..7dd664f35288bf 100644 --- a/x-pack/plugins/data_usage/server/config.ts +++ b/x-pack/plugins/data_usage/server/config.ts @@ -10,6 +10,23 @@ import { PluginInitializerContext } from '@kbn/core/server'; export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: false }), + autoops: schema.maybe( + schema.object({ + enabled: schema.boolean({ defaultValue: false }), + api: schema.maybe( + schema.object({ + url: schema.maybe(schema.uri({ scheme: ['http', 'https'] })), + tls: schema.maybe( + schema.object({ + certificate: schema.maybe(schema.string()), + key: schema.maybe(schema.string()), + ca: schema.maybe(schema.string()), + }) + ), + }) + ), + }) + ), }); export type DataUsageConfigType = TypeOf; diff --git a/x-pack/plugins/data_usage/server/plugin.ts b/x-pack/plugins/data_usage/server/plugin.ts index 2beae9b22bba97..c282d12d767a1f 100644 --- a/x-pack/plugins/data_usage/server/plugin.ts +++ b/x-pack/plugins/data_usage/server/plugin.ts @@ -18,6 +18,7 @@ import type { } from './types'; import { registerDataUsageRoutes } from './routes'; import { PLUGIN_ID } from '../common'; +import { appContextService } from './app_context'; export class DataUsagePlugin implements @@ -37,11 +38,17 @@ export class DataUsagePlugin this.logger = context.logger.get(); this.logger.debug('data usage plugin initialized'); + this.dataUsageContext = { + config$: context.config.create(), + configInitialValue: context.config.get(), logFactory: context.logger, get serverConfig() { return serverConfig; }, + kibanaVersion: context.env.packageInfo.version, + kibanaBranch: context.env.packageInfo.branch, + kibanaInstanceId: context.env.instanceUuid, }; } setup(coreSetup: CoreSetup, pluginsSetup: DataUsageSetupDependencies): DataUsageServerSetup { @@ -64,7 +71,17 @@ export class DataUsagePlugin return {}; } - start(coreStart: CoreStart, pluginsStart: DataUsageStartDependencies): DataUsageServerStart { + start(_coreStart: CoreStart, _pluginsStart: DataUsageStartDependencies): DataUsageServerStart { + appContextService.start({ + logFactory: this.dataUsageContext.logFactory, + configInitialValue: this.dataUsageContext.configInitialValue, + serverConfig: this.dataUsageContext.serverConfig, + config$: this.dataUsageContext.config$, + kibanaVersion: this.dataUsageContext.kibanaVersion, + kibanaBranch: this.dataUsageContext.kibanaBranch, + kibanaInstanceId: this.dataUsageContext.kibanaInstanceId, + cloud: this.dataUsageContext.cloud, + }); return {}; } diff --git a/x-pack/plugins/data_usage/server/services/autoops_api.ts b/x-pack/plugins/data_usage/server/services/autoops_api.ts new file mode 100644 index 00000000000000..5f3a636301f874 --- /dev/null +++ b/x-pack/plugins/data_usage/server/services/autoops_api.ts @@ -0,0 +1,178 @@ +/* + * 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 https from 'https'; +import { SslConfig, sslSchema } from '@kbn/server-http-tools'; +import apm from 'elastic-apm-node'; + +import type { AxiosError, AxiosRequestConfig } from 'axios'; +import axios from 'axios'; +import { LogMeta } from '@kbn/core/server'; +import { + UsageMetricsRequestSchemaQueryParams, + UsageMetricsResponseSchemaBody, +} from '../../common/rest_types'; +import { appContextService } from '../app_context'; +import { AutoOpsConfig } from '../types'; + +class AutoOpsAPIService { + public async autoOpsUsageMetricsAPI(requestBody: UsageMetricsRequestSchemaQueryParams) { + const logger = appContextService.getLogger().get(); + const traceId = apm.currentTransaction?.traceparent; + const withRequestIdMessage = (message: string) => `${message} [Request Id: ${traceId}]`; + + const errorMetadata: LogMeta = { + trace: { + id: traceId, + }, + }; + + const autoopsConfig = appContextService.getConfig()?.autoops; + if (!autoopsConfig) { + logger.error('[AutoOps API] Missing autoops configuration', errorMetadata); + throw new Error('missing autoops configuration'); + } + + logger.debug( + `[AutoOps API] Creating autoops agent with TLS cert: ${ + autoopsConfig?.api?.tls?.certificate ? '[REDACTED]' : 'undefined' + } and TLS key: ${autoopsConfig?.api?.tls?.key ? '[REDACTED]' : 'undefined'} + and TLS ca: ${autoopsConfig?.api?.tls?.ca ? '[REDACTED]' : 'undefined'}` + ); + const tlsConfig = this.createTlsConfig(autoopsConfig); + + const requestConfig: AxiosRequestConfig = { + url: autoopsConfig.api?.url, + data: requestBody, + method: 'POST', + headers: { + 'Content-type': 'application/json', + 'X-Request-ID': traceId, + }, + httpsAgent: new https.Agent({ + rejectUnauthorized: tlsConfig.rejectUnauthorized, + cert: tlsConfig.certificate, + key: tlsConfig.key, + ca: tlsConfig.certificateAuthorities, + }), + }; + + const cloudSetup = appContextService.getCloud(); + if (!cloudSetup?.isServerlessEnabled) { + requestConfig.data.stack_version = appContextService.getKibanaVersion(); + } + + const requestConfigDebugStatus = this.createRequestConfigDebug(requestConfig); + + logger.debug( + `[AutoOps API] Creating autoops agent with request config ${requestConfigDebugStatus}` + ); + const errorMetadataWithRequestConfig: LogMeta = { + ...errorMetadata, + http: { + request: { + id: traceId, + body: requestConfig.data, + }, + }, + }; + + const response = await axios(requestConfig).catch( + (error: Error | AxiosError) => { + if (!axios.isAxiosError(error)) { + logger.error( + `[AutoOps API] Creating autoops failed with an error ${error} ${requestConfigDebugStatus}`, + errorMetadataWithRequestConfig + ); + throw new Error(withRequestIdMessage(error.message)); + } + + const errorLogCodeCause = `${error.code} ${this.convertCauseErrorsToString(error)}`; + + if (error.response) { + // The request was made and the server responded with a status code and error data + logger.error( + `[AutoOps API] Creating autoops failed because the AutoOps API responding with a status code that falls out of the range of 2xx: ${JSON.stringify( + error.response.status + )}} ${JSON.stringify(error.response.data)}} ${requestConfigDebugStatus}`, + { + ...errorMetadataWithRequestConfig, + http: { + ...errorMetadataWithRequestConfig.http, + response: { + status_code: error.response.status, + body: error.response.data, + }, + }, + } + ); + throw new Error( + withRequestIdMessage(`the AutoOps API could not create the autoops agent`) + ); + } else if (error.request) { + // The request was made but no response was received + logger.error( + `[AutoOps API] Creating autoops agent failed while sending the request to the AutoOps API: ${errorLogCodeCause} ${requestConfigDebugStatus}`, + errorMetadataWithRequestConfig + ); + throw new Error(withRequestIdMessage(`no response received from the AutoOps API`)); + } else { + // Something happened in setting up the request that triggered an Error + logger.error( + `[AutoOps API] Creating autoops agent failed to be created ${errorLogCodeCause} ${requestConfigDebugStatus}`, + errorMetadataWithRequestConfig + ); + throw new Error( + withRequestIdMessage('the AutoOps API could not create the autoops agent') + ); + } + } + ); + + logger.debug(`[AutoOps API] Created an autoops agent ${response}`); + return response; + } + + private createTlsConfig(autoopsConfig: AutoOpsConfig | undefined) { + return new SslConfig( + sslSchema.validate({ + enabled: true, + certificate: autoopsConfig?.api?.tls?.certificate, + key: autoopsConfig?.api?.tls?.key, + certificateAuthorities: autoopsConfig?.api?.tls?.ca, + }) + ); + } + + private createRequestConfigDebug(requestConfig: AxiosRequestConfig) { + return JSON.stringify({ + ...requestConfig, + data: { + ...requestConfig.data, + fleet_token: '[REDACTED]', + }, + httpsAgent: { + ...requestConfig.httpsAgent, + options: { + ...requestConfig.httpsAgent.options, + cert: requestConfig.httpsAgent.options.cert ? 'REDACTED' : undefined, + key: requestConfig.httpsAgent.options.key ? 'REDACTED' : undefined, + ca: requestConfig.httpsAgent.options.ca ? 'REDACTED' : undefined, + }, + }, + }); + } + + private convertCauseErrorsToString = (error: AxiosError) => { + if (error.cause instanceof AggregateError) { + return error.cause.errors.map((e: Error) => e.message); + } + return error.cause; + }; +} + +export const autoopsApiService = new AutoOpsAPIService(); diff --git a/x-pack/plugins/data_usage/server/types/types.ts b/x-pack/plugins/data_usage/server/types/types.ts index c90beb184f020f..0f4713832c8957 100644 --- a/x-pack/plugins/data_usage/server/types/types.ts +++ b/x-pack/plugins/data_usage/server/types/types.ts @@ -10,9 +10,12 @@ import type { CustomRequestHandlerContext, IRouter, LoggerFactory, + PluginInitializerContext, } from '@kbn/core/server'; import { DeepReadonly } from 'utility-types'; +import type { CloudSetup } from '@kbn/cloud-plugin/server'; import { FeaturesPluginSetup } from '@kbn/features-plugin/server'; +import { Observable } from 'rxjs'; import { DataUsageConfigType } from '../config'; export interface DataUsageSetupDependencies { @@ -36,7 +39,25 @@ export type DataUsageRequestHandlerContext = CustomRequestHandlerContext<{ export type DataUsageRouter = IRouter; +export interface AutoOpsConfig { + enabled?: boolean; + api?: { + url?: string; + tls?: { + certificate?: string; + key?: string; + ca?: string; + }; + }; +} + export interface DataUsageContext { logFactory: LoggerFactory; + config$?: Observable; + configInitialValue: DataUsageConfigType; serverConfig: DeepReadonly; + kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; + kibanaBranch: PluginInitializerContext['env']['packageInfo']['branch']; + kibanaInstanceId: PluginInitializerContext['env']['instanceUuid']; + cloud?: CloudSetup; } diff --git a/x-pack/plugins/data_usage/tsconfig.json b/x-pack/plugins/data_usage/tsconfig.json index d3754906475e92..7ca0276c1f4970 100644 --- a/x-pack/plugins/data_usage/tsconfig.json +++ b/x-pack/plugins/data_usage/tsconfig.json @@ -29,6 +29,9 @@ "@kbn/core-chrome-browser", "@kbn/features-plugin", "@kbn/index-management-shared-types", + "@kbn/repo-info", + "@kbn/cloud-plugin", + "@kbn/server-http-tools", ], "exclude": ["target/**/*"] } From 62ca320ea282890b4f56131d82ec200fbe7e985a Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:07:09 +1100 Subject: [PATCH 04/38] [api-docs] 2024-10-14 Daily api_docs build (#196048) Generated by https://buildkite.com/elastic/kibana-api-docs-daily/builds/860 --- 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/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_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_quality.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_usage.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/entities_data_access.mdx | 2 +- api_docs/entity_manager.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/esql.mdx | 2 +- api_docs/esql_data_grid.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/fields_metadata.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/inference.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/ingest_pipelines.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/integration_assistant.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/inventory.mdx | 2 +- api_docs/investigate.mdx | 2 +- api_docs/investigate_app.mdx | 2 +- api_docs/kbn_actions_types.mdx | 2 +- api_docs/kbn_ai_assistant.mdx | 2 +- api_docs/kbn_ai_assistant_common.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_comparators.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_grouping.mdx | 2 +- api_docs/kbn_alerts_ui_shared.devdocs.json | 320 +----------------- api_docs/kbn_alerts_ui_shared.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_collection_utils.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 +- api_docs/kbn_apm_synthtrace_client.mdx | 2 +- api_docs/kbn_apm_types.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_avc_banner.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_cbor.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_cloud_security_posture.mdx | 2 +- .../kbn_cloud_security_posture_common.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 +- ...ent_management_content_insights_public.mdx | 2 +- ...ent_management_content_insights_server.mdx | 2 +- ...bn_content_management_favorites_public.mdx | 2 +- ...bn_content_management_favorites_server.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 +- .../kbn_content_management_user_profiles.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_feature_flags_browser.mdx | 2 +- ...bn_core_feature_flags_browser_internal.mdx | 2 +- .../kbn_core_feature_flags_browser_mocks.mdx | 2 +- api_docs/kbn_core_feature_flags_server.mdx | 2 +- ...kbn_core_feature_flags_server_internal.mdx | 2 +- .../kbn_core_feature_flags_server_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...re_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_resources_server.mdx | 2 +- ...bn_core_http_resources_server_internal.mdx | 2 +- .../kbn_core_http_resources_server_mocks.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_server.mdx | 2 +- api_docs/kbn_core_lifecycle_server_mocks.mdx | 2 +- api_docs/kbn_core_logging_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_common_internal.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- .../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_entities_schema.mdx | 2 +- api_docs/kbn_es.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.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_editor.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_grid_layout.mdx | 2 +- api_docs/kbn_grouping.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 +- .../kbn_index_management_shared_types.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_investigation_shared.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_json_schemas.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_language_documentation.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 +- api_docs/kbn_management_settings_ids.mdx | 2 +- ...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_field_stats_flyout.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_parse_interval.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_ml_validators.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_object_versioning_utils.mdx | 2 +- api_docs/kbn_observability_alert_details.mdx | 2 +- .../kbn_observability_alerting_rule_utils.mdx | 2 +- .../kbn_observability_alerting_test_data.mdx | 2 +- ...ility_get_padded_alert_time_range_util.mdx | 2 +- api_docs/kbn_observability_logs_overview.mdx | 2 +- ...kbn_observability_synthetics_test_data.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_product_doc_artifact_builder.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_recently_accessed.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 +- .../kbn_response_ops_feature_flag_service.mdx | 2 +- api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_rollup.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_screenshotting_server.mdx | 2 +- api_docs/kbn_search_api_keys_components.mdx | 2 +- api_docs/kbn_search_api_keys_server.mdx | 2 +- api_docs/kbn_search_api_panels.mdx | 2 +- api_docs/kbn_search_connectors.mdx | 2 +- 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_shared_ui.mdx | 2 +- api_docs/kbn_search_types.mdx | 2 +- api_docs/kbn_security_api_key_management.mdx | 2 +- api_docs/kbn_security_authorization_core.mdx | 2 +- api_docs/kbn_security_form_components.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 +- .../kbn_security_role_management_model.mdx | 2 +- api_docs/kbn_security_solution_common.mdx | 2 +- ...kbn_security_solution_distribution_bar.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 +- api_docs/kbn_security_ui_components.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_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 +- .../kbn_server_route_repository_client.mdx | 2 +- .../kbn_server_route_repository_utils.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_table_persist.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_predicates.mdx | 2 +- api_docs/kbn_sse_utils.mdx | 2 +- api_docs/kbn_sse_utils_client.mdx | 2 +- api_docs/kbn_sse_utils_server.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_synthetics_e2e.mdx | 2 +- api_docs/kbn_synthetics_private_location.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_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_unsaved_changes_prompt.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.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.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.mdx | 2 +- 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 | 2 +- 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_assistant.mdx | 2 +- api_docs/search_connectors.mdx | 2 +- api_docs/search_homepage.mdx | 2 +- api_docs/search_indices.mdx | 2 +- api_docs/search_inference_endpoints.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.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.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 +- 752 files changed, 761 insertions(+), 1061 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 450b6bcf5bd5ca..698e60edb913f6 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-10-13 +date: 2024-10-14 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 b800781aa5e0dd..e9ca79c99735c8 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-10-13 +date: 2024-10-14 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 906d7fc032db23..6c9184109b31d8 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-10-13 +date: 2024-10-14 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 98c9d8b8d4249b..aeb09c707831d3 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-10-13 +date: 2024-10-14 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 f9a76556142027..5cc58b974b8056 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-10-13 +date: 2024-10-14 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 5de42f6038954e..d153cab8edf199 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-10-13 +date: 2024-10-14 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 a063e43e67e009..b9c4fa84ad2b8e 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 1f3cd8b949cfeb..5e986e354e880a 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-10-13 +date: 2024-10-14 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 d594be24b8531f..80a1d827bfb691 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-10-13 +date: 2024-10-14 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 e11dc3afa41ab7..62c2ce6a22b3a6 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-10-13 +date: 2024-10-14 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 7e375fcc6186b0..ce240016d99cb6 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-10-13 +date: 2024-10-14 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 0deb91001caa5a..1678b5e099623a 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-10-13 +date: 2024-10-14 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 7de48d5ed40c7c..8b7e2f4ffefdff 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-10-13 +date: 2024-10-14 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 bead0867815ebf..ee878d9bea7656 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-10-13 +date: 2024-10-14 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 ed5742a8d4363f..f16e4e251c4820 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 7fe9ffc1d082b5..a6b9a527bfdb77 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-10-13 +date: 2024-10-14 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 49121db6d8fb29..e472e3a2ac10ad 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-10-13 +date: 2024-10-14 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 d48190c65380ac..68e9874c53cf44 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-10-13 +date: 2024-10-14 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 29b282e65e0e2b..8a30fced2c9f12 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-10-13 +date: 2024-10-14 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 5e342fd0d22bd7..1d8967e8624ef3 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-10-13 +date: 2024-10-14 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 28c856e2302498..0642555c64e168 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-10-13 +date: 2024-10-14 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 d8b41fd6b73845..2ce536d74646d9 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-10-13 +date: 2024-10-14 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 adb4c1e58c63e5..dc25b4e1b16153 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_quality.mdx b/api_docs/data_quality.mdx index db72d0cc35bc19..08e4ad18227ef9 100644 --- a/api_docs/data_quality.mdx +++ b/api_docs/data_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataQuality title: "dataQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the dataQuality plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataQuality'] --- import dataQualityObj from './data_quality.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 7724d42c7a9ee0..7fceffbb633e5b 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-10-13 +date: 2024-10-14 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 5cd5e2ba8e7146..eb2e4f8cdd2b98 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_usage.mdx b/api_docs/data_usage.mdx index 3b55de066ad619..5831aaed4c5e79 100644 --- a/api_docs/data_usage.mdx +++ b/api_docs/data_usage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataUsage title: "dataUsage" image: https://source.unsplash.com/400x175/?github description: API docs for the dataUsage plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataUsage'] --- import dataUsageObj from './data_usage.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 5d366e3f7c9614..df5d285afae72d 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-10-13 +date: 2024-10-14 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 2c913802039583..ef86aa99559c26 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-10-13 +date: 2024-10-14 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 50b2715150f681..f0cc8a599520d5 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-10-13 +date: 2024-10-14 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 aeea06d655d8b0..80420e17e0b1c6 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-10-13 +date: 2024-10-14 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 659cc91f8ae7bd..d5fd2abe222ae8 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-10-13 +date: 2024-10-14 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 be64e6a74aa8dd..9bce3d34e6973f 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-10-13 +date: 2024-10-14 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 08909ee1cb4d81..3aa09ede1036d0 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 71fd83c7bf30d5..10c7e8429433eb 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 1aab7083bab1f9..2e436b6068df7e 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 932384f108612a..fbff6ea30cbf43 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-10-13 +date: 2024-10-14 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 8c221734ab7c65..9e3a2103c284bf 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-10-13 +date: 2024-10-14 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 f825cf10d66377..ef4ce3fcac6deb 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-10-13 +date: 2024-10-14 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 71a9c798721611..de2e95fefe1af7 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-10-13 +date: 2024-10-14 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 bd48290b81a3f8..0e7a91fd2fa700 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-10-13 +date: 2024-10-14 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 77b55ab7dc224a..a93f3d77c50bb6 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-10-13 +date: 2024-10-14 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 676f4fd988f798..af504fe0c60dc1 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-10-13 +date: 2024-10-14 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 3820752d9e170d..1878e0cd89b669 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-10-13 +date: 2024-10-14 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 84d2bf6671adbf..4ef53e361fcf1f 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-10-13 +date: 2024-10-14 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 b2b59eb55f80e3..78ae48a4ef7ef8 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/entities_data_access.mdx b/api_docs/entities_data_access.mdx index dc7256b69bed27..28b7d13d04ca19 100644 --- a/api_docs/entities_data_access.mdx +++ b/api_docs/entities_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entitiesDataAccess title: "entitiesDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the entitiesDataAccess plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entitiesDataAccess'] --- import entitiesDataAccessObj from './entities_data_access.devdocs.json'; diff --git a/api_docs/entity_manager.mdx b/api_docs/entity_manager.mdx index f9e7e5549f75e6..b82a89e5537f61 100644 --- a/api_docs/entity_manager.mdx +++ b/api_docs/entity_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/entityManager title: "entityManager" image: https://source.unsplash.com/400x175/?github description: API docs for the entityManager plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'entityManager'] --- import entityManagerObj from './entity_manager.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 4929f35a36efa4..32e3f0cc11ffd9 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/esql.mdx b/api_docs/esql.mdx index 80615ad4176847..d8a9f839198144 100644 --- a/api_docs/esql.mdx +++ b/api_docs/esql.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esql title: "esql" image: https://source.unsplash.com/400x175/?github description: API docs for the esql plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esql'] --- import esqlObj from './esql.devdocs.json'; diff --git a/api_docs/esql_data_grid.mdx b/api_docs/esql_data_grid.mdx index 9fb3eab9c7134a..3849cfc4289ec3 100644 --- a/api_docs/esql_data_grid.mdx +++ b/api_docs/esql_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esqlDataGrid title: "esqlDataGrid" image: https://source.unsplash.com/400x175/?github description: API docs for the esqlDataGrid plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esqlDataGrid'] --- import esqlDataGridObj from './esql_data_grid.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 47a57825ba4344..91bcd2a82f34f7 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-10-13 +date: 2024-10-14 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 6c889ae04c33e2..357bdb7a2714b9 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-10-13 +date: 2024-10-14 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 393f42e1697b22..dbac9da4ae08ee 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-10-13 +date: 2024-10-14 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 6f3f492ca91524..af8d2a04913440 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-10-13 +date: 2024-10-14 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 37b12c419b045a..b871cefac84b4c 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-10-13 +date: 2024-10-14 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 82df050ef8cd3d..65e7031aeaadea 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-10-13 +date: 2024-10-14 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 b2f0e4d7dbf26e..7f2078f3a32c4b 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-10-13 +date: 2024-10-14 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 47f23b57b745ca..2a5e0a4784429f 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-10-13 +date: 2024-10-14 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 bf8781bf23cf29..fa93ca568eedc3 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-10-13 +date: 2024-10-14 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 235461e995f77a..097e896f026f93 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-10-13 +date: 2024-10-14 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 879b86ea3510ff..f68e67a9375f53 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-10-13 +date: 2024-10-14 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 b6892ac5d03f91..dc617e067039dc 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-10-13 +date: 2024-10-14 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 993b3fbda5f5dc..96e1c23478ed58 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-10-13 +date: 2024-10-14 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 3e440e10c879aa..368b15186f10de 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-10-13 +date: 2024-10-14 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 d9928b907acae0..adaac6483e7cd2 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-10-13 +date: 2024-10-14 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 21de3ad7cda4cc..ff76dc878746b3 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-10-13 +date: 2024-10-14 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 567b0ab72d91e3..19278feebba74c 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-10-13 +date: 2024-10-14 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 5a9891d31a5f0b..5d0168ecb80aaf 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-10-13 +date: 2024-10-14 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 980e070a47dd2b..9465529cbe29da 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-10-13 +date: 2024-10-14 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 63d18962fa3036..7268f3f9bc2dc7 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/fields_metadata.mdx b/api_docs/fields_metadata.mdx index 8cb01163611e8f..3acca9f43f1183 100644 --- a/api_docs/fields_metadata.mdx +++ b/api_docs/fields_metadata.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldsMetadata title: "fieldsMetadata" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldsMetadata plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldsMetadata'] --- import fieldsMetadataObj from './fields_metadata.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 70adc05922314d..306eb9c39fc760 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-10-13 +date: 2024-10-14 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 82ee38f657c129..1a932999dc80ba 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-10-13 +date: 2024-10-14 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 aaab73cbdb1c38..b84c324e002427 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-10-13 +date: 2024-10-14 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 db9e07ecd07177..53c5cfd820a7cd 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-10-13 +date: 2024-10-14 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 e5780de6e09937..639df0a49c9ebc 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-10-13 +date: 2024-10-14 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 f341b7b06abaef..06ade039f3eb83 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-10-13 +date: 2024-10-14 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 c06bd8f154b0cd..2e59d5e7c6b3bc 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-10-13 +date: 2024-10-14 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 c1975191c3bdb5..d3838aeaa9c115 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-10-13 +date: 2024-10-14 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 793c80a4783e1d..ae3cd7cee99a0c 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-10-13 +date: 2024-10-14 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 42e23c8516ff2f..ae645465e9149b 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/inference.mdx b/api_docs/inference.mdx index 684de135f72231..9bdfca2841ceef 100644 --- a/api_docs/inference.mdx +++ b/api_docs/inference.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inference title: "inference" image: https://source.unsplash.com/400x175/?github description: API docs for the inference plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inference'] --- import inferenceObj from './inference.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 392914b3e46dbb..f4703c03a904a6 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-10-13 +date: 2024-10-14 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 eb082db7390fab..5f3e4cf5b5b679 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-10-13 +date: 2024-10-14 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 3dacbf8b1a49c1..917d8707041900 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/integration_assistant.mdx b/api_docs/integration_assistant.mdx index f4231f7d6e9ab7..f2dcd0a62e9a23 100644 --- a/api_docs/integration_assistant.mdx +++ b/api_docs/integration_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/integrationAssistant title: "integrationAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the integrationAssistant plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'integrationAssistant'] --- import integrationAssistantObj from './integration_assistant.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 8a0a2dcdb43c8f..cd463cb84b99e9 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/inventory.mdx b/api_docs/inventory.mdx index 5417c1a9460119..5c0cdddb7f2511 100644 --- a/api_docs/inventory.mdx +++ b/api_docs/inventory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inventory title: "inventory" image: https://source.unsplash.com/400x175/?github description: API docs for the inventory plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inventory'] --- import inventoryObj from './inventory.devdocs.json'; diff --git a/api_docs/investigate.mdx b/api_docs/investigate.mdx index c99ae99505f22e..874df9987f7be9 100644 --- a/api_docs/investigate.mdx +++ b/api_docs/investigate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigate title: "investigate" image: https://source.unsplash.com/400x175/?github description: API docs for the investigate plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigate'] --- import investigateObj from './investigate.devdocs.json'; diff --git a/api_docs/investigate_app.mdx b/api_docs/investigate_app.mdx index 9f07fe67adb052..89d3916fe7dcc6 100644 --- a/api_docs/investigate_app.mdx +++ b/api_docs/investigate_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/investigateApp title: "investigateApp" image: https://source.unsplash.com/400x175/?github description: API docs for the investigateApp plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'investigateApp'] --- import investigateAppObj from './investigate_app.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index 652225319c4870..869e749461c805 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_ai_assistant.mdx b/api_docs/kbn_ai_assistant.mdx index c0132f01d1397b..ce43df4cc4bbc4 100644 --- a/api_docs/kbn_ai_assistant.mdx +++ b/api_docs/kbn_ai_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ai-assistant title: "@kbn/ai-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ai-assistant plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ai-assistant'] --- import kbnAiAssistantObj from './kbn_ai_assistant.devdocs.json'; diff --git a/api_docs/kbn_ai_assistant_common.mdx b/api_docs/kbn_ai_assistant_common.mdx index f3ed1886851e02..47ec9af07d960e 100644 --- a/api_docs/kbn_ai_assistant_common.mdx +++ b/api_docs/kbn_ai_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ai-assistant-common title: "@kbn/ai-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ai-assistant-common plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ai-assistant-common'] --- import kbnAiAssistantCommonObj from './kbn_ai_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index dbe48d640bbf8e..e3b4a8f20185be 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-10-13 +date: 2024-10-14 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 2f9c943202520a..6ef56e0d141f8c 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-10-13 +date: 2024-10-14 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 c715fd386e8460..3433d8306c13d1 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-10-13 +date: 2024-10-14 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 cf59d8cdd704d4..f120de86593fa1 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-10-13 +date: 2024-10-14 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_comparators.mdx b/api_docs/kbn_alerting_comparators.mdx index 13b14871866864..26bbfc526868df 100644 --- a/api_docs/kbn_alerting_comparators.mdx +++ b/api_docs/kbn_alerting_comparators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-comparators title: "@kbn/alerting-comparators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-comparators plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-comparators'] --- import kbnAlertingComparatorsObj from './kbn_alerting_comparators.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 24fc656634a319..e2de5587529220 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-10-13 +date: 2024-10-14 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 8a10e10d62b57d..edcf8ff7f8596d 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-10-13 +date: 2024-10-14 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 c8e526566f6a2d..0294ba0d59ad56 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-10-13 +date: 2024-10-14 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_grouping.mdx b/api_docs/kbn_alerts_grouping.mdx index 88ebe8692883e6..e41d25ab9a4a4d 100644 --- a/api_docs/kbn_alerts_grouping.mdx +++ b/api_docs/kbn_alerts_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-grouping title: "@kbn/alerts-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-grouping plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-grouping'] --- import kbnAlertsGroupingObj from './kbn_alerts_grouping.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.devdocs.json b/api_docs/kbn_alerts_ui_shared.devdocs.json index b3314d1d85582d..bab1864fcef71d 100644 --- a/api_docs/kbn_alerts_ui_shared.devdocs.json +++ b/api_docs/kbn_alerts_ui_shared.devdocs.json @@ -142,7 +142,7 @@ "label": "AlertsSearchBar", "description": [], "signature": [ - "({ appName, disableQueryLanguageSwitcher, featureIds, ruleTypeId, query, filters, onQueryChange, onQuerySubmit, onFiltersUpdated, rangeFrom, rangeTo, showFilterBar, showDatePicker, showSubmitButton, placeholder, submitOnBlur, http, toasts, unifiedSearchBar, dataViewsService, }: ", + "({ appName, disableQueryLanguageSwitcher, featureIds, ruleTypeId, query, filters, onQueryChange, onQuerySubmit, onFiltersUpdated, rangeFrom, rangeTo, showFilterBar, showDatePicker, showSubmitButton, placeholder, submitOnBlur, http, toasts, unifiedSearchBar, dataService, }: ", { "pluginId": "@kbn/alerts-ui-shared", "scope": "public", @@ -161,7 +161,7 @@ "id": "def-public.AlertsSearchBar.$1", "type": "Object", "tags": [], - "label": "{\n appName,\n disableQueryLanguageSwitcher = false,\n featureIds = EMPTY_FEATURE_IDS,\n ruleTypeId,\n query,\n filters,\n onQueryChange,\n onQuerySubmit,\n onFiltersUpdated,\n rangeFrom,\n rangeTo,\n showFilterBar = false,\n showDatePicker = true,\n showSubmitButton = true,\n placeholder = SEARCH_BAR_PLACEHOLDER,\n submitOnBlur = false,\n http,\n toasts,\n unifiedSearchBar,\n dataViewsService,\n}", + "label": "{\n appName,\n disableQueryLanguageSwitcher = false,\n featureIds = EMPTY_FEATURE_IDS,\n ruleTypeId,\n query,\n filters,\n onQueryChange,\n onQuerySubmit,\n onFiltersUpdated,\n rangeFrom,\n rangeTo,\n showFilterBar = false,\n showDatePicker = true,\n showSubmitButton = true,\n placeholder = SEARCH_BAR_PLACEHOLDER,\n submitOnBlur = false,\n http,\n toasts,\n unifiedSearchBar,\n dataService,\n}", "description": [], "signature": [ { @@ -2591,319 +2591,19 @@ }, { "parentPluginId": "@kbn/alerts-ui-shared", - "id": "def-public.AlertsSearchBarProps.dataViewsService", + "id": "def-public.AlertsSearchBarProps.dataService", "type": "Object", "tags": [], - "label": "dataViewsService", + "label": "dataService", "description": [], "signature": [ - "{ create: (spec: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewSpec", - "text": "DataViewSpec" - }, - ", skipFetchFields?: boolean, displayErrors?: boolean) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - ">; get: (id: string, displayErrors?: boolean, refreshFields?: boolean) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - ">; delete: (indexPatternId: string) => Promise; find: (search: string, size?: number) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - "[]>; getCanSave: () => Promise; getIds: (refresh?: boolean) => Promise; getTitles: (refresh?: boolean) => Promise; findLazy: (search: string, size?: number) => Promise<", { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - "[]>; getIdsWithTitle: (refresh?: boolean) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewListItem", - "text": "DataViewListItem" - }, - "[]>; getAllDataViewLazy: (refresh?: boolean) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - "[]>; clearCache: () => void; clearInstanceCache: (id?: string | undefined) => void; getCache: () => Promise<", - { - "pluginId": "@kbn/core-saved-objects-common", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" - }, - "<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewSavedObjectAttrs", - "text": "DataViewSavedObjectAttrs" - }, - ">[] | null | undefined>; getDefault: (displayErrors?: boolean) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - " | null>; getDefaultId: () => Promise; setDefault: (id: string | null, force?: boolean) => Promise; hasUserDataView: () => Promise; getMetaFields: () => Promise; getShortDotsEnable: () => Promise; getFieldsForWildcard: (options: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.GetFieldsOptions", - "text": "GetFieldsOptions" - }, - ") => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.FieldSpec", - "text": "FieldSpec" - }, - "[]>; getFieldsForIndexPattern: (indexPattern: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - " | ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewSpec", - "text": "DataViewSpec" - }, - ", options?: Omit<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.GetFieldsOptions", - "text": "GetFieldsOptions" - }, - ", \"allowNoIndex\"> | undefined) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.FieldSpec", - "text": "FieldSpec" - }, - "[]>; refreshFields: (dataView: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - ", displayErrors?: boolean, forceRefresh?: boolean) => Promise; fieldArrayToMap: (fields: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.FieldSpec", - "text": "FieldSpec" - }, - "[], fieldAttrs?: ", - "FieldAttrsAsObject", - " | undefined) => ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewFieldMap", - "text": "DataViewFieldMap" - }, - "; savedObjectToSpec: (savedObject: ", - { - "pluginId": "@kbn/core-saved-objects-common", - "scope": "common", - "docId": "kibKbnCoreSavedObjectsCommonPluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" - }, - "<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewAttributes", - "text": "DataViewAttributes" - }, - ">) => ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewSpec", - "text": "DataViewSpec" - }, - "; getDataViewLazy: (id: string) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - ">; getDataViewLazyFromCache: (id: string) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - " | undefined>; createDataViewLazy: (spec: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewSpec", - "text": "DataViewSpec" - }, - ") => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - ">; createAndSaveDataViewLazy: (spec: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewSpec", - "text": "DataViewSpec" - }, - ", overwrite?: boolean) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - ">; createAndSave: (spec: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewSpec", - "text": "DataViewSpec" - }, - ", overwrite?: boolean, skipFetchFields?: boolean, displayErrors?: boolean) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - ">; createSavedObject: (dataView: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.AbstractDataView", - "text": "AbstractDataView" - }, - ", overwrite?: boolean) => Promise; updateSavedObject: (indexPattern: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.AbstractDataView", - "text": "AbstractDataView" - }, - ", saveAttempts?: number, ignoreErrors?: boolean, displayErrors?: boolean) => Promise; defaultDataViewExists: () => Promise; getDefaultDataViewLazy: () => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - " | null>; getDefaultDataView: (options?: { displayErrors?: boolean | undefined; refreshFields?: boolean | undefined; }) => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - " | null>; toDataView: (dataViewLazy: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - ") => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - ">; toDataViewLazy: (dataView: ", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataView", - "text": "DataView" - }, - ") => Promise<", - { - "pluginId": "dataViews", - "scope": "common", - "docId": "kibDataViewsPluginApi", - "section": "def-common.DataViewLazy", - "text": "DataViewLazy" - }, - ">; }" + "pluginId": "data", + "scope": "public", + "docId": "kibDataPluginApi", + "section": "def-public.DataPublicPluginStart", + "text": "DataPublicPluginStart" + } ], "path": "packages/kbn-alerts-ui-shared/src/alerts_search_bar/types.ts", "deprecated": false, diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 8994d3e77c82c6..42908fa61c79bf 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-10-13 +date: 2024-10-14 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 c0c71b919c1fa9..0f712f2d31dea9 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index dec2015154b146..7108fe83f9299d 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 427c0280e610a0..675d7ec6443e3b 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-10-13 +date: 2024-10-14 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 3350f79968b526..685df0e98d62d4 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-10-13 +date: 2024-10-14 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 bbbb345574a2be..add4887215bae3 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-10-13 +date: 2024-10-14 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.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index c7e84139f369b4..d14e619cdd6f4f 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_types.mdx b/api_docs/kbn_apm_types.mdx index ef1ee49a24cc56..08bfc37eb6303e 100644 --- a/api_docs/kbn_apm_types.mdx +++ b/api_docs/kbn_apm_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-types title: "@kbn/apm-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-types plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-types'] --- import kbnApmTypesObj from './kbn_apm_types.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 2cb52d7b5665ab..722c1c309b9858 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_avc_banner.mdx b/api_docs/kbn_avc_banner.mdx index c2aa493081d452..50ce155ded90c7 100644 --- a/api_docs/kbn_avc_banner.mdx +++ b/api_docs/kbn_avc_banner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-avc-banner title: "@kbn/avc-banner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/avc-banner plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/avc-banner'] --- import kbnAvcBannerObj from './kbn_avc_banner.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 65b5c8df685a9d..b9e18c776e3766 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-10-13 +date: 2024-10-14 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 0c40b9a4b33fa6..60b95bb658abc3 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-10-13 +date: 2024-10-14 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 00f533d57eba2c..755afbc8c528c2 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-10-13 +date: 2024-10-14 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 4c19c95f150364..ecd3c779fe6bb4 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-10-13 +date: 2024-10-14 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 7b1ce0314f1fe5..0f77c0ab99a88c 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cbor.mdx b/api_docs/kbn_cbor.mdx index e6675c3603ba04..148363f86297ea 100644 --- a/api_docs/kbn_cbor.mdx +++ b/api_docs/kbn_cbor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cbor title: "@kbn/cbor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cbor plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cbor'] --- import kbnCborObj from './kbn_cbor.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index 0dc88f77c37db4..cd3d53813b7871 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-10-13 +date: 2024-10-14 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 36023e622b60f8..1eaaba5afa8934 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-10-13 +date: 2024-10-14 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 3ab9a161aa9e1c..99f4a45155ed3e 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-10-13 +date: 2024-10-14 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 5e6f84d24be812..f59aec229d23f9 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-10-13 +date: 2024-10-14 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 c3a1891fbce924..db815b7232968d 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-10-13 +date: 2024-10-14 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 fdbc2c3a5473b0..340f2eaab560b5 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-10-13 +date: 2024-10-14 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 c22bf10c6cae30..da4ae61b8c448e 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture.mdx b/api_docs/kbn_cloud_security_posture.mdx index 8042aad7877808..3a2f81a7f6f605 100644 --- a/api_docs/kbn_cloud_security_posture.mdx +++ b/api_docs/kbn_cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture title: "@kbn/cloud-security-posture" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture'] --- import kbnCloudSecurityPostureObj from './kbn_cloud_security_posture.devdocs.json'; diff --git a/api_docs/kbn_cloud_security_posture_common.mdx b/api_docs/kbn_cloud_security_posture_common.mdx index 8f5352ef3e0207..4d2fc344b7d7c6 100644 --- a/api_docs/kbn_cloud_security_posture_common.mdx +++ b/api_docs/kbn_cloud_security_posture_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cloud-security-posture-common title: "@kbn/cloud-security-posture-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cloud-security-posture-common plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cloud-security-posture-common'] --- import kbnCloudSecurityPostureCommonObj from './kbn_cloud_security_posture_common.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 6ff3d93e5abe13..4a0ead885a596a 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-10-13 +date: 2024-10-14 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 16b753ced85ab3..17c175194bd60b 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-10-13 +date: 2024-10-14 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 225777913d612f..464c6f2687014b 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-10-13 +date: 2024-10-14 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 9bf41ed9ea5c50..3b9b1267dd01c2 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-10-13 +date: 2024-10-14 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 65d3b32bfe37d7..283bbbf0314972 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-10-13 +date: 2024-10-14 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 79cb2c6b24c928..b9d32621c5d460 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-10-13 +date: 2024-10-14 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 98b88581c2c31e..8f6d3605ee26cb 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-10-13 +date: 2024-10-14 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 afc3ac5d2a3371..f2eb54dd4a3dc4 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-10-13 +date: 2024-10-14 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_content_insights_public.mdx b/api_docs/kbn_content_management_content_insights_public.mdx index ef1abe0f8ed70c..f9bed25d80276c 100644 --- a/api_docs/kbn_content_management_content_insights_public.mdx +++ b/api_docs/kbn_content_management_content_insights_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-insights-public title: "@kbn/content-management-content-insights-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-insights-public plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-insights-public'] --- import kbnContentManagementContentInsightsPublicObj from './kbn_content_management_content_insights_public.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_insights_server.mdx b/api_docs/kbn_content_management_content_insights_server.mdx index 38c73b82eecf45..065993a210f58e 100644 --- a/api_docs/kbn_content_management_content_insights_server.mdx +++ b/api_docs/kbn_content_management_content_insights_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-insights-server title: "@kbn/content-management-content-insights-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-insights-server plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-insights-server'] --- import kbnContentManagementContentInsightsServerObj from './kbn_content_management_content_insights_server.devdocs.json'; diff --git a/api_docs/kbn_content_management_favorites_public.mdx b/api_docs/kbn_content_management_favorites_public.mdx index f1d6b42a2f9fe6..8982b1a0bffdbf 100644 --- a/api_docs/kbn_content_management_favorites_public.mdx +++ b/api_docs/kbn_content_management_favorites_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-favorites-public title: "@kbn/content-management-favorites-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-favorites-public plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-favorites-public'] --- import kbnContentManagementFavoritesPublicObj from './kbn_content_management_favorites_public.devdocs.json'; diff --git a/api_docs/kbn_content_management_favorites_server.mdx b/api_docs/kbn_content_management_favorites_server.mdx index 4c6a779e35e539..773ea03fec1ac0 100644 --- a/api_docs/kbn_content_management_favorites_server.mdx +++ b/api_docs/kbn_content_management_favorites_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-favorites-server title: "@kbn/content-management-favorites-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-favorites-server plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-favorites-server'] --- import kbnContentManagementFavoritesServerObj from './kbn_content_management_favorites_server.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 2784d66f3bfeb8..e97a5ac8f84e5b 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-10-13 +date: 2024-10-14 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 77a14a82c37006..b9ccd0cb6d1b4d 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-10-13 +date: 2024-10-14 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 1a5c6e9d019c41..855fc70cfa7f41 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-10-13 +date: 2024-10-14 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 f018da1bf1ef13..2694c544f291c2 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-10-13 +date: 2024-10-14 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_user_profiles.mdx b/api_docs/kbn_content_management_user_profiles.mdx index bee63e34cf817b..d9c1b710664841 100644 --- a/api_docs/kbn_content_management_user_profiles.mdx +++ b/api_docs/kbn_content_management_user_profiles.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-user-profiles title: "@kbn/content-management-user-profiles" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-user-profiles plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-user-profiles'] --- import kbnContentManagementUserProfilesObj from './kbn_content_management_user_profiles.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 95daf0d3ffa807..f853fae07542be 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-10-13 +date: 2024-10-14 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 f9c12f3adba692..03bf0313ce550e 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-10-13 +date: 2024-10-14 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 48563955cd1152..2ad0fadbe8a920 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-10-13 +date: 2024-10-14 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 6b43c8b14a1d32..1ccf01e1cedf1a 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-10-13 +date: 2024-10-14 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 6726e27359fd88..d9c82f59c1684d 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-10-13 +date: 2024-10-14 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 aa2a5ad55c8d55..858cb8520abeed 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-10-13 +date: 2024-10-14 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 9f374e7425585d..51beb4e9656566 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-10-13 +date: 2024-10-14 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 2cf45aa33e0fd5..fdf72fa69f893a 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-10-13 +date: 2024-10-14 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 dfead87ea71b57..f62ffb0c6f5707 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-10-13 +date: 2024-10-14 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 8f4cb2ce27877c..f49b5c324af840 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-10-13 +date: 2024-10-14 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 79bfbdadcfdba2..98c06f8fc3979e 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-10-13 +date: 2024-10-14 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 e0b517ce55b9e7..f9c68cbfae9525 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-10-13 +date: 2024-10-14 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 d3470a7fbfc676..550d6c534e40b5 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-10-13 +date: 2024-10-14 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 ef77898f5a2c6b..7eb3a9f89c30a6 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-10-13 +date: 2024-10-14 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 65df302b7b7bb9..10fba67717c33b 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-10-13 +date: 2024-10-14 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 14a08b724114cf..87420e926e14be 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-10-13 +date: 2024-10-14 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 6a7cb8eaa3af69..7700f345b2ead3 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-10-13 +date: 2024-10-14 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 b6a6c8383e2d5e..31bb200a57cef9 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-10-13 +date: 2024-10-14 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 0e8799aade0a3f..2b962547ccb307 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-10-13 +date: 2024-10-14 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 9715711c556e54..da073c6ec6cdd3 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-10-13 +date: 2024-10-14 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 466e7baaab1a9d..14dd96c3c7a8dc 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-10-13 +date: 2024-10-14 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 ddfd33a0181d02..d07a15c1db1a75 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-10-13 +date: 2024-10-14 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 e89852e9054d76..43228f2040659e 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-10-13 +date: 2024-10-14 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 af4c144154caab..04da05f13686c7 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-10-13 +date: 2024-10-14 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 f64423148f8cd0..802b7bd3cab4ff 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-10-13 +date: 2024-10-14 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 7e94b893bbde8b..ebcdfd7780a199 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-10-13 +date: 2024-10-14 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 544f36702c84be..da5ba6c1b7b07e 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-10-13 +date: 2024-10-14 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 e6e52e167b3b8b..5eefd9681de20a 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-10-13 +date: 2024-10-14 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 57d4c3dfcfff75..bc1b5119027497 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-10-13 +date: 2024-10-14 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 48e27c1f4264b0..6ccab132aec705 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-10-13 +date: 2024-10-14 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 23aece02e96e77..1c5da227c793b5 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-10-13 +date: 2024-10-14 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 ea4d1d1a5d2f38..b7a318ddd9794f 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-10-13 +date: 2024-10-14 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 49f972347a1d00..a68e756ff5fa29 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-10-13 +date: 2024-10-14 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 39893f356df553..dade90d1f58236 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-10-13 +date: 2024-10-14 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 681b3966fcc385..10f5335204034f 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-10-13 +date: 2024-10-14 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 1da1d18a3d4237..54535011827e70 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-10-13 +date: 2024-10-14 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 cbb033855026ca..dee1aff648f7d0 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-10-13 +date: 2024-10-14 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 86b6902b0e2440..12cc560573bb9b 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-10-13 +date: 2024-10-14 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 46ca595838c71d..745f089ac92c88 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-10-13 +date: 2024-10-14 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 43e19b06b36d5c..55f5cd9b76df49 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-10-13 +date: 2024-10-14 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 96cfa3be0e9a56..191a40daa2e971 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-10-13 +date: 2024-10-14 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 bf1ea37788ca8f..62e72a0c99956c 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-10-13 +date: 2024-10-14 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 7cf7198cc853a1..5b5180e7eb3692 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-10-13 +date: 2024-10-14 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 721168ee78eafd..d01b9e167820a1 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-10-13 +date: 2024-10-14 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 39131d4f248256..6be3b89ea4263b 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-10-13 +date: 2024-10-14 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 688c72b241cef1..ccf38731eeea64 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-10-13 +date: 2024-10-14 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 f1e51de48f694a..d9b3b543d67ae0 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-10-13 +date: 2024-10-14 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 08c8d2585af068..c9d913ce2d6744 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-10-13 +date: 2024-10-14 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 2f629bbbb5c50a..dd75701b212065 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-10-13 +date: 2024-10-14 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 c86af441307e32..6ecf8708cf6241 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-10-13 +date: 2024-10-14 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 d1185bd4bc3629..abb1641cf80fc2 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-10-13 +date: 2024-10-14 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 07c9556c9527cf..4d720b6f3b3696 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-10-13 +date: 2024-10-14 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 0c44a29627085f..1d9bd857ee9360 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-10-13 +date: 2024-10-14 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 ce59a75883f2cc..442b99515a6276 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-10-13 +date: 2024-10-14 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 ae8fc3f5b32ee9..5dd1b93e76d913 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-10-13 +date: 2024-10-14 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 5c7a1bb0285bcc..722483ffe6b9cb 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-10-13 +date: 2024-10-14 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 c0f74d2943b2b8..03fd05649991b3 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-10-13 +date: 2024-10-14 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 41805ddf0124b9..0a6719c8574a59 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-10-13 +date: 2024-10-14 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 67a4a5ff5509de..59e6a989d2ee72 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-10-13 +date: 2024-10-14 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_feature_flags_browser.mdx b/api_docs/kbn_core_feature_flags_browser.mdx index 30feb75bbf80cc..60508586b6c2d2 100644 --- a/api_docs/kbn_core_feature_flags_browser.mdx +++ b/api_docs/kbn_core_feature_flags_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser title: "@kbn/core-feature-flags-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser'] --- import kbnCoreFeatureFlagsBrowserObj from './kbn_core_feature_flags_browser.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser_internal.mdx b/api_docs/kbn_core_feature_flags_browser_internal.mdx index 0d66d3e9b32830..dedd15f1a63ad3 100644 --- a/api_docs/kbn_core_feature_flags_browser_internal.mdx +++ b/api_docs/kbn_core_feature_flags_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser-internal title: "@kbn/core-feature-flags-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser-internal plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser-internal'] --- import kbnCoreFeatureFlagsBrowserInternalObj from './kbn_core_feature_flags_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_browser_mocks.mdx b/api_docs/kbn_core_feature_flags_browser_mocks.mdx index 4c9e79ec53e38b..31cb95535092c0 100644 --- a/api_docs/kbn_core_feature_flags_browser_mocks.mdx +++ b/api_docs/kbn_core_feature_flags_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-browser-mocks title: "@kbn/core-feature-flags-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-browser-mocks plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-browser-mocks'] --- import kbnCoreFeatureFlagsBrowserMocksObj from './kbn_core_feature_flags_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server.mdx b/api_docs/kbn_core_feature_flags_server.mdx index 076df36b754751..0ac4684c2af4f5 100644 --- a/api_docs/kbn_core_feature_flags_server.mdx +++ b/api_docs/kbn_core_feature_flags_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server title: "@kbn/core-feature-flags-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server'] --- import kbnCoreFeatureFlagsServerObj from './kbn_core_feature_flags_server.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server_internal.mdx b/api_docs/kbn_core_feature_flags_server_internal.mdx index d58da7cc71a109..5eab9cf75afcb5 100644 --- a/api_docs/kbn_core_feature_flags_server_internal.mdx +++ b/api_docs/kbn_core_feature_flags_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server-internal title: "@kbn/core-feature-flags-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server-internal plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server-internal'] --- import kbnCoreFeatureFlagsServerInternalObj from './kbn_core_feature_flags_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_feature_flags_server_mocks.mdx b/api_docs/kbn_core_feature_flags_server_mocks.mdx index 069f51e272bc3a..a659fdb3b281c1 100644 --- a/api_docs/kbn_core_feature_flags_server_mocks.mdx +++ b/api_docs/kbn_core_feature_flags_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-feature-flags-server-mocks title: "@kbn/core-feature-flags-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-feature-flags-server-mocks plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-feature-flags-server-mocks'] --- import kbnCoreFeatureFlagsServerMocksObj from './kbn_core_feature_flags_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 936807560438fe..a1cbdfd9525015 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-10-13 +date: 2024-10-14 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 35842a0e2476dc..ebe2e8773c74eb 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-10-13 +date: 2024-10-14 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 b8aae8ec93a81e..09f06b35914ce9 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-10-13 +date: 2024-10-14 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 e8127a10d826a4..2fe5775d09a6e6 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-10-13 +date: 2024-10-14 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 12b30969968785..be840d960a23c1 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-10-13 +date: 2024-10-14 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 78560f3a432d77..3e4a6f1f0133cd 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-10-13 +date: 2024-10-14 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 d98ced7c2d7da7..f7641b092fb5c5 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-10-13 +date: 2024-10-14 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 b5bae9d3a0f530..2666665034b2ea 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-10-13 +date: 2024-10-14 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 b507bf8216abda..1a72bc0bc66838 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-10-13 +date: 2024-10-14 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 540f94cf54ce85..acced40a406255 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-10-13 +date: 2024-10-14 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 e8d93e8e3b11ff..bb43a8a6586771 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 892a37f19a377b..5427d4c868adf5 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-10-13 +date: 2024-10-14 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 e61c1efc6c1bbc..1b9f87043beff1 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-10-13 +date: 2024-10-14 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 39d3217623de5e..707592102cd97f 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-10-13 +date: 2024-10-14 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 a1de7811f07459..c45b919fd22867 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-10-13 +date: 2024-10-14 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 3722811f8e2ae7..1837837bd098f3 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-10-13 +date: 2024-10-14 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 e12025871b6fbe..5f4424b556b2ce 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-10-13 +date: 2024-10-14 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 2c26e0167e5632..606a91d70813d2 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-10-13 +date: 2024-10-14 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 2f8d093dbe58e7..945579b6d470a3 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-10-13 +date: 2024-10-14 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 eb1083852a7183..0492d1e55b0cd7 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-10-13 +date: 2024-10-14 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 f895df7f98da5b..104810e5d32e58 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-10-13 +date: 2024-10-14 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 7761ca33199b4b..1ab0cb7c9a318d 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-10-13 +date: 2024-10-14 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 2a5b32ec15cae3..62f149be55f567 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-10-13 +date: 2024-10-14 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 b1f1b4a0255fe7..da9197d5c61a0a 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-10-13 +date: 2024-10-14 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 8a5b80c5bbe89c..e748ed85ac681c 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-10-13 +date: 2024-10-14 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 6ee568ce10924a..b79804d75f3c0a 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-10-13 +date: 2024-10-14 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 ca19b4ccb290b1..69d709329b7317 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-10-13 +date: 2024-10-14 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 8e6d46e75a4fe3..f886aa2b78af08 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-10-13 +date: 2024-10-14 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 97200618bab825..c048a427037e45 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-10-13 +date: 2024-10-14 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 5958372f0e9dd7..6618746bac4553 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-10-13 +date: 2024-10-14 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 38362ce365a1d9..66e54429e667f1 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-10-13 +date: 2024-10-14 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 216eee31711e55..2f37169fe37bbb 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-10-13 +date: 2024-10-14 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 a7beafa665c8d7..db4c5cee7fb39c 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-10-13 +date: 2024-10-14 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 b1e02550817805..5b2aff30bb6c01 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-10-13 +date: 2024-10-14 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 762d5eb6529ab1..880cdc606b95bc 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-10-13 +date: 2024-10-14 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 2d9ac1f6f0c2de..9ecce4829d3fb3 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-10-13 +date: 2024-10-14 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 471b6abc2ca174..9722c15e2dae23 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-10-13 +date: 2024-10-14 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 bbb65735322c91..d1e7fa8e3de3e3 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-10-13 +date: 2024-10-14 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 daa391da7daba0..3f08395489bc10 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-10-13 +date: 2024-10-14 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 c5029d6e28a654..978460390d1095 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-10-13 +date: 2024-10-14 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 4d238ec82efcf3..015f254fb195d4 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-10-13 +date: 2024-10-14 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 626c0fad6efe6a..c6028253aa38e7 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-10-13 +date: 2024-10-14 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 5b5aed00506c69..cf571b576171db 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-10-13 +date: 2024-10-14 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 d4cb541104c375..78fd7b466a5da2 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-10-13 +date: 2024-10-14 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 ebca9e197838e6..83aa2670bd57a7 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-10-13 +date: 2024-10-14 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 8cb028848ff4e4..01cb7ad5af7689 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-10-13 +date: 2024-10-14 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 9f28b4e360c321..0bf9f3e425bf17 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-10-13 +date: 2024-10-14 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 8b7a22a07c2daa..aea8b859a85d2e 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-10-13 +date: 2024-10-14 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 1ad48a6f697d48..2ecbbe01633de5 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-10-13 +date: 2024-10-14 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 9513dd796aff95..32d85fd0b704d2 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-10-13 +date: 2024-10-14 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 b72b601b29a093..0110a19256ecba 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-10-13 +date: 2024-10-14 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 367e30141ddd7d..b767150c305849 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-10-13 +date: 2024-10-14 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 53aa6c12ba866f..24c07869b13e97 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-10-13 +date: 2024-10-14 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 f797c35324d8f1..561bf4c2e59966 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-10-13 +date: 2024-10-14 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 72ca9f9b544f12..c4719b4f3c2e17 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-10-13 +date: 2024-10-14 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 d36e046b2cd6c6..0417dc858d400a 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-10-13 +date: 2024-10-14 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 2af1d741e0a773..a332bbc6f7e50a 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-10-13 +date: 2024-10-14 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 b8984ce983a9fe..011e992c274588 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-10-13 +date: 2024-10-14 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 ea5cceabb21e7f..ca249b731ff005 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-10-13 +date: 2024-10-14 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 492292df8146eb..a6b1bd12cbd114 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-10-13 +date: 2024-10-14 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 ba841fbca6f2db..ad3fb86aff9070 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-10-13 +date: 2024-10-14 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 3c6f1ecd70be9b..1b2ad11d82d478 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-10-13 +date: 2024-10-14 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 d6748ea720a505..b384a0d907cead 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-10-13 +date: 2024-10-14 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 5a0901605aaedc..dec85d42dd7b50 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-10-13 +date: 2024-10-14 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 a916b958c5df19..5b677b952e3bf2 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-10-13 +date: 2024-10-14 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 70333496a574c7..7b74316915ac98 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-10-13 +date: 2024-10-14 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 aa067db3238bd3..eeca404e633081 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-10-13 +date: 2024-10-14 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 5e39c6b230b609..a15836b9316402 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-10-13 +date: 2024-10-14 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 c921d97c6037dc..c148a4c55b853e 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-10-13 +date: 2024-10-14 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 ed76f21bf2130b..2f5220e54d76db 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-10-13 +date: 2024-10-14 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 383f64705f280f..d316a7b8ea6c65 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-10-13 +date: 2024-10-14 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 97f66de5ebe0a8..7145a2ea1490c6 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-10-13 +date: 2024-10-14 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 bce826864f5c6c..a0f581a1afd3e0 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-10-13 +date: 2024-10-14 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 82d6755374ecf3..56622e08bc4728 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-10-13 +date: 2024-10-14 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 0759de8032caf4..992977d8578039 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-10-13 +date: 2024-10-14 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 bbc98fb79818f2..26dfdd6e42c4d4 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-10-13 +date: 2024-10-14 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 c88cc7c47ca78e..80c44eda65d8c1 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-10-13 +date: 2024-10-14 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 ad6877799cfaec..fcc908c6348199 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-10-13 +date: 2024-10-14 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 2eb2ac6e8c535e..da5a0a42f12b29 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-10-13 +date: 2024-10-14 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 2ae4ca8405db60..309eac85aed2d0 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-10-13 +date: 2024-10-14 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 8e52e920244dbf..4922a3c706d662 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-10-13 +date: 2024-10-14 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 8fd70d0a12c98a..19aa327ebd8c19 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-10-13 +date: 2024-10-14 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 a73157d131c370..5e69e5b65bd44e 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-10-13 +date: 2024-10-14 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 5d76cfb5f8e073..6d48f8803eacee 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-10-13 +date: 2024-10-14 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 6d1532e6b180c6..f8b031b75a1e62 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-10-13 +date: 2024-10-14 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 62896776af882d..177c687da8cb2f 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-10-13 +date: 2024-10-14 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 941c6188b35555..eabbd12312cf96 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-10-13 +date: 2024-10-14 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 3920a2f8754c22..a8d495b567a355 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-10-13 +date: 2024-10-14 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 a1af437b630bdc..718f4987fa86d3 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-10-13 +date: 2024-10-14 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 c5c80bbcf3f469..cff501e9a68e62 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-10-13 +date: 2024-10-14 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 8bc07f4aff48d4..0f946ce5857c14 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-10-13 +date: 2024-10-14 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 5b6de74c8b274c..06471086c80592 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-10-13 +date: 2024-10-14 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 a0aba610f72ea0..820af40a5fd2c2 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-10-13 +date: 2024-10-14 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 ca64ad62a20ac6..8f65deeef79089 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-10-13 +date: 2024-10-14 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 6f1713d60bdd61..01780459381cd3 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-10-13 +date: 2024-10-14 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 98fbeb66f20cc1..e25a2a3ec1c56d 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-10-13 +date: 2024-10-14 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 f74c3902408737..1299065a58ae34 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-10-13 +date: 2024-10-14 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 849fc1b58e7de0..033749b2f180d5 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-10-13 +date: 2024-10-14 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 fed51012716ac8..5a12aa70a27162 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-10-13 +date: 2024-10-14 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 48be888a36f30e..92d20b7d388fb3 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-10-13 +date: 2024-10-14 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 3acda5267e7805..d8d98ceb015979 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-10-13 +date: 2024-10-14 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 0a1341d224f8b3..55f308ad477e0a 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-10-13 +date: 2024-10-14 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 d786d3eba68be8..9bc3de5ad71316 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-10-13 +date: 2024-10-14 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 3c0c5aac4f84df..c6e81504b004b8 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-10-13 +date: 2024-10-14 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 34eb2fc303a68f..f3431fbdd49e7f 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-10-13 +date: 2024-10-14 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 6d817100bb7260..cb99772dc2415f 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-10-13 +date: 2024-10-14 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 d1e20dc10599c3..231bcfe69d5828 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-10-13 +date: 2024-10-14 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 e7177dd3d854a8..a4d373cd334ce9 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-10-13 +date: 2024-10-14 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 8bcd6808c29bda..3b08ee90b8fc6f 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-10-13 +date: 2024-10-14 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 994d36e4002c0a..d3a8fee0a77197 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-10-13 +date: 2024-10-14 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 2015d2aad13ef1..89b51dbb98ae80 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-10-13 +date: 2024-10-14 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 16fad500de389b..4b8e259b50fb16 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-10-13 +date: 2024-10-14 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 db866a2f564509..056a889ce5ebb8 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-10-13 +date: 2024-10-14 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 990adff1cfc9ac..f47a01f56ef224 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-10-13 +date: 2024-10-14 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 15adde5accc7fe..23fe73102e547f 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-10-13 +date: 2024-10-14 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 a477ef2c3fc9f1..f505477902cabd 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-10-13 +date: 2024-10-14 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 6d982eaa84841f..8bb822b23b7c7d 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-10-13 +date: 2024-10-14 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 c35b1667ff838d..2b9d04fd02189c 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-10-13 +date: 2024-10-14 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 75948931f704aa..f2975d2a5befdb 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-10-13 +date: 2024-10-14 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 0be7bc72f2184c..fb39c7f160b1e9 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-10-13 +date: 2024-10-14 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 ef55b48cd3c41f..83cdc6ef4a33a9 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-10-13 +date: 2024-10-14 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 555b0cf5393e6a..60727320be4c14 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-10-13 +date: 2024-10-14 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 5905642874b577..fc84a78fc16a3f 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-10-13 +date: 2024-10-14 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 2d4532ecb0bc8d..68bd0880888756 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-10-13 +date: 2024-10-14 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 76035fcb2dac49..1a5cc514a326bb 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-10-13 +date: 2024-10-14 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 0d973e7032efb8..6f0c90afa6285f 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-10-13 +date: 2024-10-14 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 ad1253b7409db4..1b2e1747b73746 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-10-13 +date: 2024-10-14 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 5eb5edca37a8b3..e4a206a0a54b02 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-10-13 +date: 2024-10-14 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 4f1510d06f1290..8372d3d0e60c26 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-10-13 +date: 2024-10-14 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 8bff752a017175..d5ebde5a3abfe9 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-10-13 +date: 2024-10-14 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 25b2a661aada7d..ddeabb77bf8322 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-10-13 +date: 2024-10-14 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 54e372d88e1ede..e0434620ba1df6 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-10-13 +date: 2024-10-14 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 88a2d8dc07ecd0..a6b4051795eccd 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-10-13 +date: 2024-10-14 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 6d8c40cdd9205f..abdb9c5dc74526 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-10-13 +date: 2024-10-14 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 1195682342d31c..122bb670c20136 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-10-13 +date: 2024-10-14 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 ff34539e8c3aa5..7dcde89f441c37 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-10-13 +date: 2024-10-14 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 630acdcafb0e16..064a038e9ff1b5 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-10-13 +date: 2024-10-14 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 ececeb489fe4c4..9200278a97e8d8 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-10-13 +date: 2024-10-14 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 3a2894ec799081..6d71f7b68ec982 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-10-13 +date: 2024-10-14 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 77a43749f29abc..1c3e0a6ea1744d 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-10-13 +date: 2024-10-14 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 5ecb00de737d93..1ae919b61c49e4 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-10-13 +date: 2024-10-14 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 1e4b56a88beced..dd069e7f90e7af 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-10-13 +date: 2024-10-14 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 b6deb4bc231eb4..18d2d662b90171 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-10-13 +date: 2024-10-14 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 69914136ceec14..d57e41bbbf82da 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-10-13 +date: 2024-10-14 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 a0ac336e980691..b8804fad20748d 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-10-13 +date: 2024-10-14 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 401e7ff6b0c118..55cb8fc47a66a7 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-10-13 +date: 2024-10-14 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 a2c9d8974e42e3..ad9db29de2d3bc 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-10-13 +date: 2024-10-14 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 93984241fbe95d..43a7377aef2886 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-10-13 +date: 2024-10-14 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 32afa3ff6fa7db..6de70532e0e676 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-10-13 +date: 2024-10-14 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 150af5ccd7a358..ca9740e84e2686 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index a7adf671e625d0..066aaa8207c97b 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 5875de530541b4..e0c851e3b86a80 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-10-13 +date: 2024-10-14 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 bbd8198038e78c..1424d7bdbaad28 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-10-13 +date: 2024-10-14 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 8e480e98b75ba2..2b579a5827e7bf 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-10-13 +date: 2024-10-14 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 ec07dc7326ec5b..e564eeaf39b03a 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-10-13 +date: 2024-10-14 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 416f305c2b1119..fda61abd0c28a4 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-10-13 +date: 2024-10-14 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 3c94e29863697c..eda032d5971b48 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-10-13 +date: 2024-10-14 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 167dfc4c022997..d2a95d129eca52 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; diff --git a/api_docs/kbn_esql_editor.mdx b/api_docs/kbn_esql_editor.mdx index ab8f23cc8997f6..9ea87b228b4174 100644 --- a/api_docs/kbn_esql_editor.mdx +++ b/api_docs/kbn_esql_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-editor title: "@kbn/esql-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-editor plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-editor'] --- import kbnEsqlEditorObj from './kbn_esql_editor.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 09380a8317cc50..a2796497e54dec 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-10-13 +date: 2024-10-14 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 00df885454b0a4..1241008e0b921e 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-10-13 +date: 2024-10-14 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 9de4c58f22de3e..2f7db6e62f392c 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-10-13 +date: 2024-10-14 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 55566470219214..1ecbd84acf6b9b 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-10-13 +date: 2024-10-14 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 0a53fe1c5fc289..05e4eb6e04a008 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-10-13 +date: 2024-10-14 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 b0e4058510aa19..09b629899b4b7e 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-10-13 +date: 2024-10-14 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 f15f292009ce45..6059cc6854baaf 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-10-13 +date: 2024-10-14 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 6127718a68ba39..e860bf6ceb8388 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-10-13 +date: 2024-10-14 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 03f2c3fa7e8606..215d36d141de09 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-10-13 +date: 2024-10-14 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 c1486ff95ecaec..64a9c46dc6e290 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-10-13 +date: 2024-10-14 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 bdfe5f2deebb3b..3d9024cf99fcb8 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-10-13 +date: 2024-10-14 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 6191f8fbfc457d..da9c73e722b83a 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-10-13 +date: 2024-10-14 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 2d6023b7e24662..83a5f51e4d91f4 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-10-13 +date: 2024-10-14 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 20e55301998417..246f16e53a0bf9 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_grid_layout.mdx b/api_docs/kbn_grid_layout.mdx index a23505f95d6e0f..29314fe28dd3f9 100644 --- a/api_docs/kbn_grid_layout.mdx +++ b/api_docs/kbn_grid_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grid-layout title: "@kbn/grid-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grid-layout plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grid-layout'] --- import kbnGridLayoutObj from './kbn_grid_layout.devdocs.json'; diff --git a/api_docs/kbn_grouping.mdx b/api_docs/kbn_grouping.mdx index 9b40ec1acac125..1e6fde108df517 100644 --- a/api_docs/kbn_grouping.mdx +++ b/api_docs/kbn_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-grouping title: "@kbn/grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/grouping plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/grouping'] --- import kbnGroupingObj from './kbn_grouping.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 8f6d1c5741fdd7..76ed17aaad140a 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-10-13 +date: 2024-10-14 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 f26fd317b14c99..c72b0ec0bcbd10 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-10-13 +date: 2024-10-14 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 e7f6e0f1c2bd24..7caf2d32cf3aae 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-10-13 +date: 2024-10-14 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 c064b969719633..b95d0f48f47bf0 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-10-13 +date: 2024-10-14 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 42c35e8c1dfdb7..569eb2ab134778 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-10-13 +date: 2024-10-14 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 54f100bea72ae0..cdcc9d76aaf13d 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-10-13 +date: 2024-10-14 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 c67afd1b6e4b9c..41cc44c4089a24 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-10-13 +date: 2024-10-14 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 2fd64bad80c082..3434ca22ed42ab 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-10-13 +date: 2024-10-14 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 3fcd1205e5ee40..156f8df69be2b6 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management_shared_types.mdx b/api_docs/kbn_index_management_shared_types.mdx index efb55b61399cd3..27a292052c3a86 100644 --- a/api_docs/kbn_index_management_shared_types.mdx +++ b/api_docs/kbn_index_management_shared_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management-shared-types title: "@kbn/index-management-shared-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management-shared-types plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management-shared-types'] --- import kbnIndexManagementSharedTypesObj from './kbn_index_management_shared_types.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index 7743734a043489..c1ec42b1f327f1 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-10-13 +date: 2024-10-14 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 957e6efd090f0a..0a6f7d82cb9a58 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-10-13 +date: 2024-10-14 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 25180cbd210659..14fd213149119e 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_investigation_shared.mdx b/api_docs/kbn_investigation_shared.mdx index 2d6c3cd4772d17..c20a50a5518657 100644 --- a/api_docs/kbn_investigation_shared.mdx +++ b/api_docs/kbn_investigation_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-investigation-shared title: "@kbn/investigation-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/investigation-shared plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/investigation-shared'] --- import kbnInvestigationSharedObj from './kbn_investigation_shared.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index db0e916ac20dcd..2d3690bde8dc98 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-10-13 +date: 2024-10-14 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 a19c3957fbe2fd..0c200de12ed446 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-10-13 +date: 2024-10-14 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 cb53b28d2c151e..1e3aa733759b1b 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-10-13 +date: 2024-10-14 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 67d32db22c2eb8..c83b4bca553105 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-10-13 +date: 2024-10-14 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 8d4e95a16eea34..84b4766b38c55a 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_json_schemas.mdx b/api_docs/kbn_json_schemas.mdx index 8c067bd4830c7a..c5d2499e38f436 100644 --- a/api_docs/kbn_json_schemas.mdx +++ b/api_docs/kbn_json_schemas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-schemas title: "@kbn/json-schemas" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-schemas plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-schemas'] --- import kbnJsonSchemasObj from './kbn_json_schemas.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 57028cadd856a4..1aeea06f5de506 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-10-13 +date: 2024-10-14 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.mdx b/api_docs/kbn_language_documentation.mdx index 6e83b28b8aeb62..0eb74e9cbba9bb 100644 --- a/api_docs/kbn_language_documentation.mdx +++ b/api_docs/kbn_language_documentation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation title: "@kbn/language-documentation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation'] --- import kbnLanguageDocumentationObj from './kbn_language_documentation.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index 05f5866d6d424b..f13903fc3b17c4 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-10-13 +date: 2024-10-14 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 40dd232f87a529..7a2185b35e2305 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-10-13 +date: 2024-10-14 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 9eb95aee6d34f5..3b9c96e8d48d75 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-10-13 +date: 2024-10-14 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 ed8d50ccc5ddea..36ab94064c9d11 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-10-13 +date: 2024-10-14 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 254684cef4cfaa..5582b8e6c5ff15 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-10-13 +date: 2024-10-14 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 595f971b92438a..ef68359f63801b 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-10-13 +date: 2024-10-14 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 5170e0fb1f4716..ed735d713f3388 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-10-13 +date: 2024-10-14 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 890dc5aa3a8e31..809f3c9cbcadf6 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-10-13 +date: 2024-10-14 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 20309f7e8a86a8..de6fb0372dded5 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-10-13 +date: 2024-10-14 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 ee83f7d236806f..e13cd0ad8ca230 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-10-13 +date: 2024-10-14 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 f7ca1f2a347548..aca8ed1de18819 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-10-13 +date: 2024-10-14 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 7e2058f853f104..e4e3c4f5088931 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-10-13 +date: 2024-10-14 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 db2a4e8d2b0190..fbdbde622a439a 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-10-13 +date: 2024-10-14 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.mdx b/api_docs/kbn_management_settings_ids.mdx index 2c7177794fae0f..9f8726354c662c 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 3831cbb0b4abdc..3468b494cb2b1a 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-10-13 +date: 2024-10-14 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 497fb9b36e5f5b..0e12968b816afb 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-10-13 +date: 2024-10-14 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 eccf6015ae30f2..cf6b3cc0d4a888 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-10-13 +date: 2024-10-14 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 8dcab6fa443b66..028e536d48d925 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-10-13 +date: 2024-10-14 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 d2b975a9d03d87..47ba706a0282e2 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-10-13 +date: 2024-10-14 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 7325abfadfcafe..61f04dfa3865dc 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-10-13 +date: 2024-10-14 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 d7731d73f25ca1..96a0c2395a1c50 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-10-13 +date: 2024-10-14 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 d963d21c87f40c..e5e66957d18619 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-10-13 +date: 2024-10-14 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 aea34718ae9f88..136947f9607f1f 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-10-13 +date: 2024-10-14 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 d14025d5edb76d..3d6663626e1775 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-10-13 +date: 2024-10-14 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 c5f9f02858295d..583bafb528cddf 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-10-13 +date: 2024-10-14 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 81bb7520356d05..d29f4150df6c96 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-10-13 +date: 2024-10-14 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 9d0e8a908c7045..1ae92f5cb72b00 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-10-13 +date: 2024-10-14 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 708467ece1ad9e..950aeab2059920 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-10-13 +date: 2024-10-14 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 3ea4b4dab71af8..8cf786ca256b88 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-10-13 +date: 2024-10-14 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 5ec4cb6c303b69..0da4ced2aafdd0 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-10-13 +date: 2024-10-14 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_field_stats_flyout.mdx b/api_docs/kbn_ml_field_stats_flyout.mdx index ee0be8654d3420..ac39e1ff34b770 100644 --- a/api_docs/kbn_ml_field_stats_flyout.mdx +++ b/api_docs/kbn_ml_field_stats_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-field-stats-flyout title: "@kbn/ml-field-stats-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-field-stats-flyout plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-field-stats-flyout'] --- import kbnMlFieldStatsFlyoutObj from './kbn_ml_field_stats_flyout.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 44c6d24e09b51b..24606191b2380e 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-10-13 +date: 2024-10-14 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 981d55d9adbb59..5e7ad9716ea647 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-10-13 +date: 2024-10-14 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 852e43f8cd16ab..27b4091b3700f4 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-10-13 +date: 2024-10-14 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 6b4248b307b137..25d09004581b3e 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-10-13 +date: 2024-10-14 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 ab3c8952460b00..bef9319af4a3ee 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-10-13 +date: 2024-10-14 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 1bf82430b353ce..88dab017349c8b 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-10-13 +date: 2024-10-14 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 9e4c6b0a6ed2e3..70472e29dfd8e8 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-10-13 +date: 2024-10-14 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_parse_interval.mdx b/api_docs/kbn_ml_parse_interval.mdx index a1c14a6c77c088..993904b8ff06c3 100644 --- a/api_docs/kbn_ml_parse_interval.mdx +++ b/api_docs/kbn_ml_parse_interval.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-parse-interval title: "@kbn/ml-parse-interval" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-parse-interval plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-parse-interval'] --- import kbnMlParseIntervalObj from './kbn_ml_parse_interval.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 358b88e2eb68e2..677698429bfd63 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-10-13 +date: 2024-10-14 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 4f9e2aca656eed..1d1d58e9ccb884 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-10-13 +date: 2024-10-14 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 15a88c9b7fe913..99ba9827aa2b72 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-10-13 +date: 2024-10-14 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 eed3c3cd155bc2..4b20cb12bb3f9e 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-10-13 +date: 2024-10-14 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 a02d762dbab6e0..42244b2831fcf8 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-10-13 +date: 2024-10-14 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 7db2ce14fb5e68..c10d94ad6ddd98 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-10-13 +date: 2024-10-14 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 35e862a812002e..0d154784afe031 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-10-13 +date: 2024-10-14 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 b55683853d78d9..4ff745426e3a26 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-10-13 +date: 2024-10-14 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 4a8923b3fef417..1572f973a68c1f 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_ml_validators.mdx b/api_docs/kbn_ml_validators.mdx index b91cf0216ec9ba..b5e406ea97406c 100644 --- a/api_docs/kbn_ml_validators.mdx +++ b/api_docs/kbn_ml_validators.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-validators title: "@kbn/ml-validators" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-validators plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-validators'] --- import kbnMlValidatorsObj from './kbn_ml_validators.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index e21dc1b5a18bae..2bfbdf4d4f973e 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-10-13 +date: 2024-10-14 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 c5803e94e63ae2..bf4125cd3f546b 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-10-13 +date: 2024-10-14 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 2f33b68aeb4aa9..44f84eb152ac8b 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_object_versioning_utils.mdx b/api_docs/kbn_object_versioning_utils.mdx index 96541de37f5021..c4f59ff844c032 100644 --- a/api_docs/kbn_object_versioning_utils.mdx +++ b/api_docs/kbn_object_versioning_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning-utils title: "@kbn/object-versioning-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning-utils plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning-utils'] --- import kbnObjectVersioningUtilsObj from './kbn_object_versioning_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index 3175600490caf8..8921ecd1e074c5 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-10-13 +date: 2024-10-14 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_rule_utils.mdx b/api_docs/kbn_observability_alerting_rule_utils.mdx index c084bae392aa88..bb5c550ba219ec 100644 --- a/api_docs/kbn_observability_alerting_rule_utils.mdx +++ b/api_docs/kbn_observability_alerting_rule_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-rule-utils title: "@kbn/observability-alerting-rule-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-rule-utils plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-rule-utils'] --- import kbnObservabilityAlertingRuleUtilsObj from './kbn_observability_alerting_rule_utils.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 6762b837da6162..ed8001ed0c3fa5 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-10-13 +date: 2024-10-14 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 39011aab9de065..9ca1b09c4a3213 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-10-13 +date: 2024-10-14 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_observability_logs_overview.mdx b/api_docs/kbn_observability_logs_overview.mdx index 79142f5d130eb1..06907235c19181 100644 --- a/api_docs/kbn_observability_logs_overview.mdx +++ b/api_docs/kbn_observability_logs_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-logs-overview title: "@kbn/observability-logs-overview" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-logs-overview plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-logs-overview'] --- import kbnObservabilityLogsOverviewObj from './kbn_observability_logs_overview.devdocs.json'; diff --git a/api_docs/kbn_observability_synthetics_test_data.mdx b/api_docs/kbn_observability_synthetics_test_data.mdx index 16151f0af2c154..08ee964c442965 100644 --- a/api_docs/kbn_observability_synthetics_test_data.mdx +++ b/api_docs/kbn_observability_synthetics_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-synthetics-test-data title: "@kbn/observability-synthetics-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-synthetics-test-data plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-synthetics-test-data'] --- import kbnObservabilitySyntheticsTestDataObj from './kbn_observability_synthetics_test_data.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index 1ec6c089f4b87d..b47ae5a791f19a 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-10-13 +date: 2024-10-14 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 7c0d2312eebc04..d9d29f66aff78e 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-10-13 +date: 2024-10-14 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 ae9da6ad8270a3..cc9ef2a0bf332b 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-10-13 +date: 2024-10-14 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 2af80ccf4d97ce..0d2ac497602015 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-10-13 +date: 2024-10-14 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 349e7de505463d..72f74b12ac3613 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-10-13 +date: 2024-10-14 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 3610988fc72f70..e7e0d9fa9442d0 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-10-13 +date: 2024-10-14 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 f7a1589f496993..11839d5e98f102 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-10-13 +date: 2024-10-14 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 87fe0d7934b897..8a312c6712ccf1 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-10-13 +date: 2024-10-14 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 b113c188da8947..e1908987cc2ac8 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-10-13 +date: 2024-10-14 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 bb24f9314f7085..b39d5236abb26b 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-10-13 +date: 2024-10-14 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 0363442c00f435..4ccc45a3f80871 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-10-13 +date: 2024-10-14 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 4158f2ff2ebae6..02f381e984b000 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_product_doc_artifact_builder.mdx b/api_docs/kbn_product_doc_artifact_builder.mdx index 888994006a450d..1c904617a42f9b 100644 --- a/api_docs/kbn_product_doc_artifact_builder.mdx +++ b/api_docs/kbn_product_doc_artifact_builder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-product-doc-artifact-builder title: "@kbn/product-doc-artifact-builder" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/product-doc-artifact-builder plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/product-doc-artifact-builder'] --- import kbnProductDocArtifactBuilderObj from './kbn_product_doc_artifact_builder.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index a440002f12ac40..ef91e8957a7d3c 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-10-13 +date: 2024-10-14 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 1bc3c9c31766fc..d7b759abf9a622 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-10-13 +date: 2024-10-14 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 19f91ce5605a38..5f5748488ca586 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-10-13 +date: 2024-10-14 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 eb9bbc373cb6b1..ce4ffa94463d0f 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-10-13 +date: 2024-10-14 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 b528df9b5a1f3a..ee2ace35127425 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-10-13 +date: 2024-10-14 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 7d603abccc5780..3b7e2d76d8f27a 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-10-13 +date: 2024-10-14 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 958f66df0ac315..c754de221223de 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-10-13 +date: 2024-10-14 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 508f04c684f815..2642a3a92addcf 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-10-13 +date: 2024-10-14 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 9beefe0743784e..85e11ab253c8e6 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-10-13 +date: 2024-10-14 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 73e50931506a44..131929894b7d2c 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_recently_accessed.mdx b/api_docs/kbn_recently_accessed.mdx index 2694fd8693bd35..dca7732b91d6fd 100644 --- a/api_docs/kbn_recently_accessed.mdx +++ b/api_docs/kbn_recently_accessed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-recently-accessed title: "@kbn/recently-accessed" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/recently-accessed plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/recently-accessed'] --- import kbnRecentlyAccessedObj from './kbn_recently_accessed.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 8654419a3b156d..6a09b4e47851ce 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-10-13 +date: 2024-10-14 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 1c4b837b03e3bd..ec88f0dde404ca 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-10-13 +date: 2024-10-14 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 827f6587e4f412..35d5f63036cdec 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-10-13 +date: 2024-10-14 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 c5b7f2dd318a5a..95250078fa0ac5 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-10-13 +date: 2024-10-14 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 859bd2d1e0f35a..87e22b22548df2 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-10-13 +date: 2024-10-14 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 fa0d8b358175e2..89eea7be0238e3 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-10-13 +date: 2024-10-14 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 8351eb78208c87..524cf2d5ca245e 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-10-13 +date: 2024-10-14 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 a54104bd332d4e..8d46123ebab545 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-10-13 +date: 2024-10-14 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 b9dbc1d2b5f098..cd7d787058a826 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-10-13 +date: 2024-10-14 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 56c79922c81186..5da0165a1f1ea4 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-10-13 +date: 2024-10-14 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 85ca790f6a0dea..f2b39dbec4d8a7 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-10-13 +date: 2024-10-14 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 75d112e6b88fd5..79264d1808aaa8 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-10-13 +date: 2024-10-14 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 6d39e2a6450286..237592cc68ba13 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-10-13 +date: 2024-10-14 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 61aeb770683126..d90581f2cb4f59 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-10-13 +date: 2024-10-14 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 9db7777d00e959..1bc9d5a3b58219 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-10-13 +date: 2024-10-14 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 f07fab5ea3e13c..4b93851792863c 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_response_ops_feature_flag_service.mdx b/api_docs/kbn_response_ops_feature_flag_service.mdx index ccff9053a317f6..f3e58e3dd8628d 100644 --- a/api_docs/kbn_response_ops_feature_flag_service.mdx +++ b/api_docs/kbn_response_ops_feature_flag_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-response-ops-feature-flag-service title: "@kbn/response-ops-feature-flag-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/response-ops-feature-flag-service plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/response-ops-feature-flag-service'] --- import kbnResponseOpsFeatureFlagServiceObj from './kbn_response_ops_feature_flag_service.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index a0a8793224da31..4808bb447ffb4a 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rollup.mdx b/api_docs/kbn_rollup.mdx index f20b61180aeafe..6354fad20b5ae5 100644 --- a/api_docs/kbn_rollup.mdx +++ b/api_docs/kbn_rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rollup title: "@kbn/rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rollup plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rollup'] --- import kbnRollupObj from './kbn_rollup.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index 689128c3c679bf..c2ec1b174aead7 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-10-13 +date: 2024-10-14 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 88bebbd3ea8ede..27b3b9f0d8b1c0 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-10-13 +date: 2024-10-14 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 9a57ab42bbd24f..46bbc7339d5d73 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-10-13 +date: 2024-10-14 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 c64d714c84481a..1b675340a5d9c3 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-10-13 +date: 2024-10-14 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 0489493fd2afd4..3b5f63877942a5 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_screenshotting_server.mdx b/api_docs/kbn_screenshotting_server.mdx index 26b1ba6c6b7850..a7a171fed40611 100644 --- a/api_docs/kbn_screenshotting_server.mdx +++ b/api_docs/kbn_screenshotting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-screenshotting-server title: "@kbn/screenshotting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/screenshotting-server plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/screenshotting-server'] --- import kbnScreenshottingServerObj from './kbn_screenshotting_server.devdocs.json'; diff --git a/api_docs/kbn_search_api_keys_components.mdx b/api_docs/kbn_search_api_keys_components.mdx index a76c3d9d1ed5b2..ab33db485db8d8 100644 --- a/api_docs/kbn_search_api_keys_components.mdx +++ b/api_docs/kbn_search_api_keys_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-keys-components title: "@kbn/search-api-keys-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-keys-components plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-keys-components'] --- import kbnSearchApiKeysComponentsObj from './kbn_search_api_keys_components.devdocs.json'; diff --git a/api_docs/kbn_search_api_keys_server.mdx b/api_docs/kbn_search_api_keys_server.mdx index fe3abd2c287065..7289b6e8e51cd0 100644 --- a/api_docs/kbn_search_api_keys_server.mdx +++ b/api_docs/kbn_search_api_keys_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-keys-server title: "@kbn/search-api-keys-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-keys-server plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-keys-server'] --- import kbnSearchApiKeysServerObj from './kbn_search_api_keys_server.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 36ce885544100f..0c7934b257c9e5 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index be22ec1eb52491..b218eabe6412bb 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index 3177ed07e129b8..4382e59d70e1dc 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-10-13 +date: 2024-10-14 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 c9beaddc4f3a5a..b1ee5c83b727c6 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-10-13 +date: 2024-10-14 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 307bab69b8e311..672bd0ae4457a0 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-10-13 +date: 2024-10-14 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_shared_ui.mdx b/api_docs/kbn_search_shared_ui.mdx index 380a33a8ceb56c..e1cc2d3a2284dd 100644 --- a/api_docs/kbn_search_shared_ui.mdx +++ b/api_docs/kbn_search_shared_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-shared-ui title: "@kbn/search-shared-ui" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-shared-ui plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-shared-ui'] --- import kbnSearchSharedUiObj from './kbn_search_shared_ui.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 1e28f0bb449889..01775170ce67fd 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_api_key_management.mdx b/api_docs/kbn_security_api_key_management.mdx index b26d5fd2db9618..e0ee7086d67256 100644 --- a/api_docs/kbn_security_api_key_management.mdx +++ b/api_docs/kbn_security_api_key_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-api-key-management title: "@kbn/security-api-key-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-api-key-management plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-api-key-management'] --- import kbnSecurityApiKeyManagementObj from './kbn_security_api_key_management.devdocs.json'; diff --git a/api_docs/kbn_security_authorization_core.mdx b/api_docs/kbn_security_authorization_core.mdx index 86427ea8b0ea28..3208341b8209fd 100644 --- a/api_docs/kbn_security_authorization_core.mdx +++ b/api_docs/kbn_security_authorization_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-authorization-core title: "@kbn/security-authorization-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-authorization-core plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-authorization-core'] --- import kbnSecurityAuthorizationCoreObj from './kbn_security_authorization_core.devdocs.json'; diff --git a/api_docs/kbn_security_form_components.mdx b/api_docs/kbn_security_form_components.mdx index 8b4df3bbe8e6d0..3ffc0096fb42c1 100644 --- a/api_docs/kbn_security_form_components.mdx +++ b/api_docs/kbn_security_form_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-form-components title: "@kbn/security-form-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-form-components plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-form-components'] --- import kbnSecurityFormComponentsObj from './kbn_security_form_components.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index 2aa5a01af778bd..dce6f670acbc82 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-10-13 +date: 2024-10-14 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 63dfea3c8de05d..0f356132471cfd 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-10-13 +date: 2024-10-14 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 085b84b98bdaab..5b11555b32f87c 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-10-13 +date: 2024-10-14 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 f5d37f3690253e..e3473a933471ab 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-10-13 +date: 2024-10-14 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_role_management_model.mdx b/api_docs/kbn_security_role_management_model.mdx index 48aa0b00796f58..89a3e513267d46 100644 --- a/api_docs/kbn_security_role_management_model.mdx +++ b/api_docs/kbn_security_role_management_model.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-role-management-model title: "@kbn/security-role-management-model" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-role-management-model plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-role-management-model'] --- import kbnSecurityRoleManagementModelObj from './kbn_security_role_management_model.devdocs.json'; diff --git a/api_docs/kbn_security_solution_common.mdx b/api_docs/kbn_security_solution_common.mdx index 63c832f66acc40..71236ad1482a78 100644 --- a/api_docs/kbn_security_solution_common.mdx +++ b/api_docs/kbn_security_solution_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-common title: "@kbn/security-solution-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-common plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-common'] --- import kbnSecuritySolutionCommonObj from './kbn_security_solution_common.devdocs.json'; diff --git a/api_docs/kbn_security_solution_distribution_bar.mdx b/api_docs/kbn_security_solution_distribution_bar.mdx index d3c8dd4cbe9a87..a0c098bb05725c 100644 --- a/api_docs/kbn_security_solution_distribution_bar.mdx +++ b/api_docs/kbn_security_solution_distribution_bar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-distribution-bar title: "@kbn/security-solution-distribution-bar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-distribution-bar plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-distribution-bar'] --- import kbnSecuritySolutionDistributionBarObj from './kbn_security_solution_distribution_bar.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index eb8067f429cc10..6f5ce668a10750 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-10-13 +date: 2024-10-14 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 4287377dd88551..7ecd9cadb08d39 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-10-13 +date: 2024-10-14 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 7f55dfe02fb588..051b615f554dd6 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-10-13 +date: 2024-10-14 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 12cce5725467bc..3c074f4719df46 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-10-13 +date: 2024-10-14 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_security_ui_components.mdx b/api_docs/kbn_security_ui_components.mdx index 07e21571be7dc0..171bee086bf787 100644 --- a/api_docs/kbn_security_ui_components.mdx +++ b/api_docs/kbn_security_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-ui-components title: "@kbn/security-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-ui-components plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-ui-components'] --- import kbnSecurityUiComponentsObj from './kbn_security_ui_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 99852c3a986bc1..13c5061bd4b5eb 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-10-13 +date: 2024-10-14 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 ea6b4ec3efcbf2..162f62f98d3c5e 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-10-13 +date: 2024-10-14 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 87e6be85f76e80..1e95742baef799 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-10-13 +date: 2024-10-14 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 750a70d51523de..7f6583c58309aa 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-10-13 +date: 2024-10-14 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 e99d026537c6eb..fbb423ff05373c 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-10-13 +date: 2024-10-14 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_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 24905a2758153c..cb71c047847886 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-10-13 +date: 2024-10-14 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 fe0a8ca7dc251b..7d3fe53a908e60 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-10-13 +date: 2024-10-14 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 f588cd354e03e2..23fcf4086ceed1 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-10-13 +date: 2024-10-14 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 7b69a5dfbbf8c5..0ec92251df0e0a 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-10-13 +date: 2024-10-14 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 072a533a8a11ff..b4a41faf35ead3 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-10-13 +date: 2024-10-14 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 25580271860238..39ec3bcd742bcb 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-10-13 +date: 2024-10-14 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 fb5d963e0ed420..13bce27075e116 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-10-13 +date: 2024-10-14 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 1d860f946516db..a757cb72bb2cac 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-10-13 +date: 2024-10-14 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 e1b55c25c4a16e..0aac9c52b3c667 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-10-13 +date: 2024-10-14 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 60e243e3740354..a5ee9fe9ce5333 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-10-13 +date: 2024-10-14 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 f0df1f86bda46f..65c5bb6d48fd8c 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-10-13 +date: 2024-10-14 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 2166870cae44e1..e20a6e9f63af5d 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-10-13 +date: 2024-10-14 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 6cb33c3802928a..860fcf80fd07cb 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-10-13 +date: 2024-10-14 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 7525504e05d70b..44da10e73f53a7 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_client.mdx b/api_docs/kbn_server_route_repository_client.mdx index 9168168bd31670..58c435297ced94 100644 --- a/api_docs/kbn_server_route_repository_client.mdx +++ b/api_docs/kbn_server_route_repository_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-client title: "@kbn/server-route-repository-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-client plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-client'] --- import kbnServerRouteRepositoryClientObj from './kbn_server_route_repository_client.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository_utils.mdx b/api_docs/kbn_server_route_repository_utils.mdx index b74a8f9b1bdf6a..0746547fd38203 100644 --- a/api_docs/kbn_server_route_repository_utils.mdx +++ b/api_docs/kbn_server_route_repository_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository-utils title: "@kbn/server-route-repository-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository-utils plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository-utils'] --- import kbnServerRouteRepositoryUtilsObj from './kbn_server_route_repository_utils.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 5181357e4d4262..b573bb0949eb4d 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-10-13 +date: 2024-10-14 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 01938c6d054ece..ac57a947a839a8 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-10-13 +date: 2024-10-14 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 4d16f7f70912ee..3219e52a687765 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-10-13 +date: 2024-10-14 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 869693c31de911..29f5fb43d18e99 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-10-13 +date: 2024-10-14 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 eecb9be438d4e8..7e0790d38cebf8 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-10-13 +date: 2024-10-14 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 85fa97af1a39af..467021b720da5a 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-10-13 +date: 2024-10-14 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 3c8f208e1d29b9..6331535f3be6a5 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-10-13 +date: 2024-10-14 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 ce975d10543122..bd74cbe8981958 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-10-13 +date: 2024-10-14 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 6e325f9783c9aa..a4036f35b1b79a 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-10-13 +date: 2024-10-14 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 fc650e7d766021..85b64372835856 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-10-13 +date: 2024-10-14 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 ab1a39721de1ab..0ad3db353c2f93 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-10-13 +date: 2024-10-14 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 93dbf8b7293b8c..c0ea17327c2211 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-10-13 +date: 2024-10-14 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 edf8bcda23c7f8..abfb5e9402acce 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-10-13 +date: 2024-10-14 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 596f5f2fd37cdf..a5d089bb56db1b 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-10-13 +date: 2024-10-14 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 77ad3301128fc6..58008bc1f30939 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-10-13 +date: 2024-10-14 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 6ceae8184c3ee3..92a070929806ad 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-10-13 +date: 2024-10-14 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 a56ef3dd775f7b..f1382c99b473cc 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-10-13 +date: 2024-10-14 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 cc1c2203252bcd..c426cb27639d26 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-10-13 +date: 2024-10-14 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 fa32fadb8c5c63..0cbfe60a05684a 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-10-13 +date: 2024-10-14 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 1358d5e66f50af..55079532bcf661 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-10-13 +date: 2024-10-14 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 a03cf305f48ed2..e9abf51e906c7c 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-10-13 +date: 2024-10-14 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 2f3253598c9ae2..723a1f9eceb710 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-10-13 +date: 2024-10-14 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 25920f8a150d85..c4f7bd8754ab1b 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-10-13 +date: 2024-10-14 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 197e148691d321..905327ca83de90 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-10-13 +date: 2024-10-14 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 a944babc74a08c..d6d62560a3b9b7 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-10-13 +date: 2024-10-14 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 2baac72fcd410b..dc549660dd6f2f 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-10-13 +date: 2024-10-14 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 6684c2faeb8620..18622f06bee0af 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-10-13 +date: 2024-10-14 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 1767e8bb520588..f4f61f07f16ea4 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-10-13 +date: 2024-10-14 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 659c921c2a3424..71f2b7168a67a2 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-10-13 +date: 2024-10-14 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 9f11b53c702165..51d198389c6362 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-10-13 +date: 2024-10-14 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 5a7506921f3d75..50b89a0338bb94 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-10-13 +date: 2024-10-14 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 13bc963634588e..ea3259b742f648 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-10-13 +date: 2024-10-14 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 6715ba2ffed0f2..09e746b3c68a17 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-10-13 +date: 2024-10-14 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 11d8de702f8573..bcbe09cc6741fb 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-10-13 +date: 2024-10-14 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 dd19af4c2c46ad..f7e335bc21ad14 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-10-13 +date: 2024-10-14 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 7ce772f0307c7d..0e83154bf02fe2 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-10-13 +date: 2024-10-14 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 608ce750b0aa04..2905b2c3419775 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-10-13 +date: 2024-10-14 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 f3a07ae7771739..a32cefc162279c 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-10-13 +date: 2024-10-14 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 fa470bff048dcc..5295121232c7b7 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-10-13 +date: 2024-10-14 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 22baeef7efc25b..ce0875d7e156dc 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-10-13 +date: 2024-10-14 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 dfcf5018d132c8..af6b312301f244 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-10-13 +date: 2024-10-14 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 e35f03e42a89b1..09199e595814c0 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-10-13 +date: 2024-10-14 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 05e872fcbf8339..9bae18e6c47b6e 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-10-13 +date: 2024-10-14 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 9117d88c995226..a389d0c6f8121b 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-10-13 +date: 2024-10-14 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 ebd2ae7890bb09..c0eb2eaacef69e 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-10-13 +date: 2024-10-14 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_table_persist.mdx b/api_docs/kbn_shared_ux_table_persist.mdx index 3dbdab981b0597..78c87c1597f0e7 100644 --- a/api_docs/kbn_shared_ux_table_persist.mdx +++ b/api_docs/kbn_shared_ux_table_persist.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-table-persist title: "@kbn/shared-ux-table-persist" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-table-persist plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-table-persist'] --- import kbnSharedUxTablePersistObj from './kbn_shared_ux_table_persist.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 60d98040ed0271..98456d3d5f803e 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index a84393315d45b7..3514ced2f8f2e1 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index a622b97df31837..cf08dcb52303d2 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-10-13 +date: 2024-10-14 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 4ae557ae0bb6f2..5e91d78994b270 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_sse_utils.mdx b/api_docs/kbn_sse_utils.mdx index 6252b4f876568d..6d324fa0fdef67 100644 --- a/api_docs/kbn_sse_utils.mdx +++ b/api_docs/kbn_sse_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils title: "@kbn/sse-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils'] --- import kbnSseUtilsObj from './kbn_sse_utils.devdocs.json'; diff --git a/api_docs/kbn_sse_utils_client.mdx b/api_docs/kbn_sse_utils_client.mdx index a6e3749d7b5a8f..2172e546def8b9 100644 --- a/api_docs/kbn_sse_utils_client.mdx +++ b/api_docs/kbn_sse_utils_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils-client title: "@kbn/sse-utils-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils-client plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils-client'] --- import kbnSseUtilsClientObj from './kbn_sse_utils_client.devdocs.json'; diff --git a/api_docs/kbn_sse_utils_server.mdx b/api_docs/kbn_sse_utils_server.mdx index cbe44ad107f484..0f8892632a2238 100644 --- a/api_docs/kbn_sse_utils_server.mdx +++ b/api_docs/kbn_sse_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sse-utils-server title: "@kbn/sse-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sse-utils-server plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sse-utils-server'] --- import kbnSseUtilsServerObj from './kbn_sse_utils_server.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 64d4db6b094262..63ac79dc00b61f 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-10-13 +date: 2024-10-14 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 46e625814c2b49..1d502350ebb9f4 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-10-13 +date: 2024-10-14 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 3094b603bb349f..ac669752614aa0 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_synthetics_e2e.mdx b/api_docs/kbn_synthetics_e2e.mdx index 69a895f2c52f38..a423503874b58e 100644 --- a/api_docs/kbn_synthetics_e2e.mdx +++ b/api_docs/kbn_synthetics_e2e.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-e2e title: "@kbn/synthetics-e2e" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-e2e plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-e2e'] --- import kbnSyntheticsE2eObj from './kbn_synthetics_e2e.devdocs.json'; diff --git a/api_docs/kbn_synthetics_private_location.mdx b/api_docs/kbn_synthetics_private_location.mdx index 1151a824c37d29..bf3751817e891c 100644 --- a/api_docs/kbn_synthetics_private_location.mdx +++ b/api_docs/kbn_synthetics_private_location.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-synthetics-private-location title: "@kbn/synthetics-private-location" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/synthetics-private-location plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/synthetics-private-location'] --- import kbnSyntheticsPrivateLocationObj from './kbn_synthetics_private_location.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index b66a7d22d194b2..02f7f2ce7fe265 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-10-13 +date: 2024-10-14 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 a8eb59ba220cc6..1d9af2fd6a030c 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-10-13 +date: 2024-10-14 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 f93dde16f01d07..0a5f47f7b1a048 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-10-13 +date: 2024-10-14 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 ad83e83e425f3e..ab4f36b1f3645e 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-10-13 +date: 2024-10-14 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 546b9ebfce8f7d..17fb37bc6d2313 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index 0a2999fd92a908..adf825978f56da 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-10-13 +date: 2024-10-14 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 78756b99900a91..9d2f8d78fe70f6 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-10-13 +date: 2024-10-14 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 dad8cd9dc2b6be..50ae29b2b1dbc3 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-10-13 +date: 2024-10-14 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 c78445cc7ef5d3..c7db72532629c8 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-10-13 +date: 2024-10-14 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 fbd0bf7895d8b9..9f017f9eec7f0f 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-10-13 +date: 2024-10-14 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 d970a609e8d9d6..d5e08b3f5b84ca 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-10-13 +date: 2024-10-14 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 13069b73e031e5..af97e389baf3ec 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-10-13 +date: 2024-10-14 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 6ae279b0be6485..34b470f0d1bf0b 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-10-13 +date: 2024-10-14 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 02c19f68e0e5fe..3bd067b1ea18fa 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-10-13 +date: 2024-10-14 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 24c1baf10d5798..115d169236f82c 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-10-13 +date: 2024-10-14 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 000ab1e368cd1b..5e819f9ed66a0e 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-10-13 +date: 2024-10-14 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 f41f1a1591b8e4..c4208b5f4bfe2b 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-10-13 +date: 2024-10-14 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 1e5e76caa9567d..21306dda5b1c00 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_prompt.mdx b/api_docs/kbn_unsaved_changes_prompt.mdx index fd621228a6d3fd..572f0430d7cf45 100644 --- a/api_docs/kbn_unsaved_changes_prompt.mdx +++ b/api_docs/kbn_unsaved_changes_prompt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-prompt title: "@kbn/unsaved-changes-prompt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-prompt plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-prompt'] --- import kbnUnsavedChangesPromptObj from './kbn_unsaved_changes_prompt.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index b48e6259ceec1b..1361bcab4542c3 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-10-13 +date: 2024-10-14 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 619e545848c487..8ac7ec846a9f10 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-10-13 +date: 2024-10-14 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 cec49b753bbaf0..6a0d108af94349 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-10-13 +date: 2024-10-14 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 5ff117d0d76e52..eef976d8f0390d 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-10-13 +date: 2024-10-14 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 8909c2c403e8a6..1cf34f878982f1 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-10-13 +date: 2024-10-14 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 649755315a373d..0417d4835c63f2 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-10-13 +date: 2024-10-14 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 213bd1fcf8e214..b5afc9cc3ea808 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-10-13 +date: 2024-10-14 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 37442f79dc8377..70e8d880e7f205 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-10-13 +date: 2024-10-14 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 03b6d76c8ddcd0..c0c89c0e636b4c 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-10-13 +date: 2024-10-14 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.mdx b/api_docs/kbn_zod.mdx index d7b983f6289636..15c16ffdd214ae 100644 --- a/api_docs/kbn_zod.mdx +++ b/api_docs/kbn_zod.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod title: "@kbn/zod" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod'] --- import kbnZodObj from './kbn_zod.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index b969ee44300969..74aab9a39bea61 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-10-13 +date: 2024-10-14 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 79b93b00f91552..76810444ced683 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-10-13 +date: 2024-10-14 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 591eba4a17bc1b..e5692b6705153e 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-10-13 +date: 2024-10-14 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 08be92ad0b4663..5bfd5d03fc4573 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-10-13 +date: 2024-10-14 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 f4f68dc3725ccf..79dd93ea320d9e 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-10-13 +date: 2024-10-14 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 f3b96e2c10a504..d377297d1d2494 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-10-13 +date: 2024-10-14 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 b934d6f91e5b02..dbdc832eb9cd42 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-10-13 +date: 2024-10-14 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 4899bae28641e2..5e4f1aec04984c 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-10-13 +date: 2024-10-14 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 cb1fa10fe887c5..f06d2a8c7f3e43 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-10-13 +date: 2024-10-14 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 2f9500db951b49..998a1d3036a349 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-10-13 +date: 2024-10-14 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 d5c68d9d71f643..f010aa029ab216 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-10-13 +date: 2024-10-14 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 34918a3a8b11f3..8085237b50fddf 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-10-13 +date: 2024-10-14 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 8f7ff42b99b545..01db1ff4c65ca3 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-10-13 +date: 2024-10-14 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 152479f59a6e03..5ea630a517dce7 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-10-13 +date: 2024-10-14 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 ae1a3dad4fa94c..4d0042c1c03fb1 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-10-13 +date: 2024-10-14 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 9d8dab671340b5..4a5d91d7144146 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-10-13 +date: 2024-10-14 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 e15063d651fb62..3b7ae55dbe25eb 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index c337b9b05bd740..2dd6839974fa63 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-10-13 +date: 2024-10-14 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 b13652de0f46e7..0e334abd6f55d7 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-10-13 +date: 2024-10-14 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 dd8b73467d5f40..e1470912a3b2c1 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-10-13 +date: 2024-10-14 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 50d37e42e8b662..33f74a56f87538 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-10-13 +date: 2024-10-14 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 35961eee6e6098..e347ee7f202fa8 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-10-13 +date: 2024-10-14 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 706979efc826ec..21e0105471c3ab 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-10-13 +date: 2024-10-14 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 7b54bc870656a2..35eaebd34816a6 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-10-13 +date: 2024-10-14 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 a1ddce7bbaa1a3..7ed45e6df751fc 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-10-13 +date: 2024-10-14 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 a91e419dab30f7..cf9ac844fcb32a 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 3f0c7445f1112d..b6beba7ef7f681 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index 82ebbe37a21f64..10e160b4115406 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-10-13 +date: 2024-10-14 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 cf688ee11ecf35..e10b500a0bf66c 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-10-13 +date: 2024-10-14 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 3dbd433c814a6c..04e57ef5ef7b2d 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-10-13 +date: 2024-10-14 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 3b9d50e6e29b22..7c9910a5f8172b 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-10-13 +date: 2024-10-14 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 eed4790fc49f60..bba9f268236a33 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-10-13 +date: 2024-10-14 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 2f1358bb226959..718f1d84ad9662 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-10-13 +date: 2024-10-14 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 38f82ab7e3dd90..d3a337dd28237d 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-10-13 +date: 2024-10-14 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 9ebf6c3802cb94..046046d1e76443 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-10-13 +date: 2024-10-14 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 1f4b20ec8de483..f33ac9f1a55239 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index ee104802b7925c..4626fb512ba459 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-10-13 +date: 2024-10-14 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 eaee6f18bfa48b..bd9556769c16f4 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-10-13 +date: 2024-10-14 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 799d1874caf9f8..3d306012e506e2 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-10-13 +date: 2024-10-14 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 88724c80e80485..ea3c08726ecf40 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-10-13 +date: 2024-10-14 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 be5e426ecf1f8c..9e4b66707c5c4c 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-10-13 +date: 2024-10-14 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 7df6af2d526088..d12eb1345f02f2 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-10-13 +date: 2024-10-14 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 1171c022c9c676..605eda6ef59523 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-10-13 +date: 2024-10-14 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 fabfceea6c221f..8ce2bcd041498e 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-10-13 +date: 2024-10-14 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 2b8e122da805a1..8dfa47a2e38f7f 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-10-13 +date: 2024-10-14 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 44fc9c4c8b850c..017998a8f0fbbe 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-10-13 +date: 2024-10-14 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 7591ac037f135e..e6c9c646e2e27c 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-10-13 +date: 2024-10-14 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 2e45cf5debe75d..5f93ea0ea2750d 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-10-13 +date: 2024-10-14 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 e1dc60e44fb403..de83287a7502e1 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-10-13 +date: 2024-10-14 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 896c9ffd1f7726..a9c41adf296787 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-10-13 +date: 2024-10-14 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 329e75cd149d57..5e54cb029ce5fe 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-10-13 +date: 2024-10-14 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 a0fe8d70b896bb..f2c504284bc2aa 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-10-13 +date: 2024-10-14 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 53d2d54e72cc45..705cfc02555de4 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_assistant.mdx b/api_docs/search_assistant.mdx index 967726594a3e2c..9b1a6df2b86a92 100644 --- a/api_docs/search_assistant.mdx +++ b/api_docs/search_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchAssistant title: "searchAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the searchAssistant plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchAssistant'] --- import searchAssistantObj from './search_assistant.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index 33fa4d993605f5..a7823b43ca849a 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_homepage.mdx b/api_docs/search_homepage.mdx index 2d091c23cfa253..d8a62950f78fbb 100644 --- a/api_docs/search_homepage.mdx +++ b/api_docs/search_homepage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchHomepage title: "searchHomepage" image: https://source.unsplash.com/400x175/?github description: API docs for the searchHomepage plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchHomepage'] --- import searchHomepageObj from './search_homepage.devdocs.json'; diff --git a/api_docs/search_indices.mdx b/api_docs/search_indices.mdx index bce85ab7f27311..88d1e0879bc1ff 100644 --- a/api_docs/search_indices.mdx +++ b/api_docs/search_indices.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchIndices title: "searchIndices" image: https://source.unsplash.com/400x175/?github description: API docs for the searchIndices plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchIndices'] --- import searchIndicesObj from './search_indices.devdocs.json'; diff --git a/api_docs/search_inference_endpoints.mdx b/api_docs/search_inference_endpoints.mdx index 2b9f467a6f1427..d9381c30d864c0 100644 --- a/api_docs/search_inference_endpoints.mdx +++ b/api_docs/search_inference_endpoints.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchInferenceEndpoints title: "searchInferenceEndpoints" image: https://source.unsplash.com/400x175/?github description: API docs for the searchInferenceEndpoints plugin -date: 2024-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchInferenceEndpoints'] --- import searchInferenceEndpointsObj from './search_inference_endpoints.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index f929611d5e6967..b62aa6b4318815 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-10-13 +date: 2024-10-14 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 1e4a5c5b8c7093..17c6d7fb44ed14 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-10-13 +date: 2024-10-14 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 adcb50893f6500..b711ba194dc0b0 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-10-13 +date: 2024-10-14 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 6bafd11ec79c2e..ea493b53974db0 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-10-13 +date: 2024-10-14 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 8ac415850e2916..9af7304049f97b 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-10-13 +date: 2024-10-14 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 d34a59eeafd586..46562052c63e59 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-10-13 +date: 2024-10-14 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 c38229d368544a..2fecd2cb97590c 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-10-13 +date: 2024-10-14 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 470b4acec7de4d..70b75a4d326822 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-10-13 +date: 2024-10-14 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 5ebf3c3d69e525..7494dde033eb4d 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-10-13 +date: 2024-10-14 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 dd34e8a7a08eca..a1529062840978 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-10-13 +date: 2024-10-14 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 3fbd24ee2c9c63..d87027abd17344 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index d9a1c339742ec0..2e8f88ac353ffe 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-10-13 +date: 2024-10-14 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 f719b7fd36b820..2f815e9c446af9 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-10-13 +date: 2024-10-14 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 2545bdc66cde72..bf378fcc9b872d 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-10-13 +date: 2024-10-14 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 bc191e5206623f..df1df5e84d77d7 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-10-13 +date: 2024-10-14 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 1a8ad58efdce8b..0adcf78fd31732 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-10-13 +date: 2024-10-14 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 b127f5f8a8dff9..d658beae4fd67b 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-10-13 +date: 2024-10-14 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 77f6a270d266c7..5af587f0d87dc6 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-10-13 +date: 2024-10-14 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 c655dde6c4ad3f..8834defd10bd4e 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-10-13 +date: 2024-10-14 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 295ce4b20fcf10..c195522ef5509d 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-10-13 +date: 2024-10-14 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 f3342516462e2e..4f4eb1d6f12369 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 9cc5b7900bdfb7..ad197b89cb9079 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-10-13 +date: 2024-10-14 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 184c05c5521645..f0919e232b898d 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-10-13 +date: 2024-10-14 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 73b10ef72a85d0..b81e66c5e84369 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-10-13 +date: 2024-10-14 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 18241bcf931103..74f74fd8c59242 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-10-13 +date: 2024-10-14 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 62e54a7c195007..420e0fd40da3f0 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-10-13 +date: 2024-10-14 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 7a5d4b4aed201f..d83c6d02385a82 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-10-13 +date: 2024-10-14 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 318e98f960013c..d3168621516468 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-10-13 +date: 2024-10-14 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 3ba787b300bb03..971bd5aad04477 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-10-13 +date: 2024-10-14 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 f9bfbbe5d5a260..47f736ce4ab13f 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-10-13 +date: 2024-10-14 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 99b0397b01203e..d1b22e0c0cfb70 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-10-13 +date: 2024-10-14 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 b2620f759b4055..487f455a383203 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-10-13 +date: 2024-10-14 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 9966f4cadbe9e9..34406fd1f090d5 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-10-13 +date: 2024-10-14 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 2f1b5ad4215ae5..42a931c30d2ef9 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-10-13 +date: 2024-10-14 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 806f85a02bf1b6..c777d5810f3005 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-10-13 +date: 2024-10-14 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 7aae87d2fcac55..5f05407ee2d7ea 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-10-13 +date: 2024-10-14 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 6c760db7f2658b..39a1b394e1f37c 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-10-13 +date: 2024-10-14 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 eefc34983bd38f..c8d879ca7e35e7 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-10-13 +date: 2024-10-14 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 e6b912401033a6..b6362de4451b74 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-10-13 +date: 2024-10-14 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 32ab0c36eff83f..752f7c45dea4b6 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-10-13 +date: 2024-10-14 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 21166db4e0ab83..aba8173a523429 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-10-13 +date: 2024-10-14 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 b267289e6242c1..b4c07f3eab9fc0 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-10-13 +date: 2024-10-14 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 f5b883fd70bc38..57366cfa904274 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-10-13 +date: 2024-10-14 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 24fc8bde02770a..20f57e5d383a4c 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-10-13 +date: 2024-10-14 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 908d80bcd7bc25..2620f508cfd84d 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-10-13 +date: 2024-10-14 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 1d9d8440e59579..4b11744cc9dd9b 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-10-13 +date: 2024-10-14 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From 4c4cb1e1678130c300c93e00061f0478047aff28 Mon Sep 17 00:00:00 2001 From: Linghao Su Date: Mon, 14 Oct 2024 15:54:11 +0800 Subject: [PATCH 05/38] [Lens] Disable config axis side and color picker when groupid is breakdown and collaseFn enable (#195845) ## Summary Fixes https://github.com/elastic/kibana/issues/195481 For XY charts only, when opening the breakdown dimension editor, we disable the color picker and axis side configuration. ## Before ![image](https://github.com/user-attachments/assets/62f03481-45aa-402c-8d53-d30a8b02d11c) ## After ![image](https://github.com/user-attachments/assets/f48abdca-ab3f-40bb-b9fa-8266416b915c) ![image](https://github.com/user-attachments/assets/c0f62b62-b9ef-4fe4-917f-77246fd0066b) https://github.com/user-attachments/assets/05ee0e8e-713b-4eb3-a1ef-bf7418226409 --------- Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Co-authored-by: Nick Partridge --- .../xy_config_panel/dimension_editor.test.tsx | 60 +++++++++++++++++++ .../xy/xy_config_panel/dimension_editor.tsx | 6 +- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.test.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.test.tsx index 3d675883ec6bf9..08a83ef7e01768 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { mountWithIntl as mount } from '@kbn/test-jest-helpers'; +import { render, screen } from '@testing-library/react'; import { EuiButtonGroupProps, EuiButtonGroup } from '@elastic/eui'; import { DataDimensionEditor } from './dimension_editor'; import { FramePublicAPI, DatasourcePublicAPI } from '../../../types'; @@ -195,6 +196,65 @@ describe('XY Config panels', () => { expect(component.find(EuiColorPicker).prop('color')).toEqual('red'); }); + test.each<{ collapseFn?: string; shouldDisplay?: boolean }>([ + // should display color picker + { shouldDisplay: true }, + // should not display color picker + { collapseFn: 'sum', shouldDisplay: false }, + ])( + 'should only show color picker when collapseFn is defined for breakdown group', + ({ collapseFn = undefined, shouldDisplay = true }) => { + const state = { + ...testState(), + layers: [ + { + collapseFn, + seriesType: 'bar', + layerType: LayerTypes.DATA, + layerId: 'first', + splitAccessor: 'breakdownAccessor', + xAccessor: 'foo', + accessors: ['bar'], + yConfig: [{ forAccessor: 'bar', color: 'red' }], + }, + ], + } as XYState; + + render( + + ); + const colorPickerUi = screen.queryByLabelText('Edit colors'); + + if (shouldDisplay) { + expect(colorPickerUi).toBeInTheDocument(); + } else { + expect(colorPickerUi).not.toBeInTheDocument(); + } + } + ); test('does not apply incorrect color', () => { jest.useFakeTimers(); const setState = jest.fn(); diff --git a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index c0916b823466eb..f06f2d861d8659 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -134,8 +134,8 @@ export function DataDimensionEditor( const { splitAccessor } = layer; const splitCategories = getColorCategories(table?.rows ?? [], splitAccessor); - if (props.groupId === 'breakdown' && !layer.collapseFn) { - return ( + if (props.groupId === 'breakdown') { + return !layer.collapseFn ? ( - ); + ) : null; } const isHorizontal = isHorizontalChart(state.layers); From 08715c679ae7b573a8ba6631e40eb95e85fba114 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 14 Oct 2024 19:00:47 +1100 Subject: [PATCH 06/38] [ES|QL] Update function metadata (#196047) This PR updates the function definitions and inline docs based on the latest metadata from Elasticsearch. Co-authored-by: Stratoula Kalafateli --- .../definitions/generated/scalar_functions.ts | 258 ++++++++++++++++++ .../sections/generated/scalar_functions.tsx | 34 +++ 2 files changed, 292 insertions(+) diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts index e860ff932f3dc4..b25d3ad8b6563d 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts @@ -2015,6 +2015,263 @@ const greatestDefinition: FunctionDefinition = { examples: ['ROW a = 10, b = 20\n| EVAL g = GREATEST(a, b)'], }; +// Do not edit this manually... generated by scripts/generate_function_definitions.ts +const hypotDefinition: FunctionDefinition = { + type: 'eval', + name: 'hypot', + description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.hypot', { + defaultMessage: + 'Returns the hypotenuse of two numbers. The input can be any numeric values, the return value is always a double.\nHypotenuses of infinities are null.', + }), + alias: undefined, + signatures: [ + { + params: [ + { + name: 'number1', + type: 'double', + optional: false, + }, + { + name: 'number2', + type: 'double', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'double', + optional: false, + }, + { + name: 'number2', + type: 'integer', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'double', + optional: false, + }, + { + name: 'number2', + type: 'long', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'double', + optional: false, + }, + { + name: 'number2', + type: 'unsigned_long', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'integer', + optional: false, + }, + { + name: 'number2', + type: 'double', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'integer', + optional: false, + }, + { + name: 'number2', + type: 'integer', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'integer', + optional: false, + }, + { + name: 'number2', + type: 'long', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'integer', + optional: false, + }, + { + name: 'number2', + type: 'unsigned_long', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'long', + optional: false, + }, + { + name: 'number2', + type: 'double', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'long', + optional: false, + }, + { + name: 'number2', + type: 'integer', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'long', + optional: false, + }, + { + name: 'number2', + type: 'long', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'long', + optional: false, + }, + { + name: 'number2', + type: 'unsigned_long', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'unsigned_long', + optional: false, + }, + { + name: 'number2', + type: 'double', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'unsigned_long', + optional: false, + }, + { + name: 'number2', + type: 'integer', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'unsigned_long', + optional: false, + }, + { + name: 'number2', + type: 'long', + optional: false, + }, + ], + returnType: 'double', + }, + { + params: [ + { + name: 'number1', + type: 'unsigned_long', + optional: false, + }, + { + name: 'number2', + type: 'unsigned_long', + optional: false, + }, + ], + returnType: 'double', + }, + ], + supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], + supportedOptions: ['by'], + validate: undefined, + examples: ['ROW a = 3.0, b = 4.0\n| EVAL c = HYPOT(a, b)'], +}; + // Do not edit this manually... generated by scripts/generate_function_definitions.ts const ipPrefixDefinition: FunctionDefinition = { type: 'eval', @@ -8811,6 +9068,7 @@ export const scalarFunctionDefinitions = [ floorDefinition, fromBase64Definition, greatestDefinition, + hypotDefinition, ipPrefixDefinition, leastDefinition, leftDefinition, diff --git a/packages/kbn-language-documentation/src/sections/generated/scalar_functions.tsx b/packages/kbn-language-documentation/src/sections/generated/scalar_functions.tsx index e9d6edcbcb544e..3b74c73cd2a9aa 100644 --- a/packages/kbn-language-documentation/src/sections/generated/scalar_functions.tsx +++ b/packages/kbn-language-documentation/src/sections/generated/scalar_functions.tsx @@ -834,6 +834,40 @@ export const functions = { | EVAL g = GREATEST(a, b) \`\`\` Note: When run on \`keyword\` or \`text\` fields, this returns the last string in alphabetical order. When run on \`boolean\` columns this will return \`true\` if any values are \`true\`. + `, + description: + 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', + ignoreTag: true, + } + )} + /> + ), + }, + // Do not edit manually... automatically generated by scripts/generate_esql_docs.ts + { + label: i18n.translate('languageDocumentation.documentationESQL.hypot', { + defaultMessage: 'HYPOT', + }), + description: ( + + + ### HYPOT + Returns the hypotenuse of two numbers. The input can be any numeric values, the return value is always a double. + Hypotenuses of infinities are null. + + \`\`\` + ROW a = 3.0, b = 4.0 + | EVAL c = HYPOT(a, b) + \`\`\` `, description: 'Text is in markdown. Do not translate function names, special characters, or field names like sum(bytes)', From 94ee8d598fe9fdf388a49e48580c2db58e85df2b Mon Sep 17 00:00:00 2001 From: kosabogi <105062005+kosabogi@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:48:36 +0200 Subject: [PATCH 07/38] Updates file upload formats in Kibana guide (#195880) ### Overview This PR updates the **Upload a file** section in the **Add data** page to reflect the changes to the supported file formats in the Data Visualizer. ### Related issue: https://github.com/elastic/search-docs-team/issues/189#issuecomment-2399079270 ### Preview --- docs/setup/connect-to-elasticsearch.asciidoc | 21 +++++++++++++++---- docs/setup/images/add-data-fv.png | Bin 231075 -> 248950 bytes 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/setup/connect-to-elasticsearch.asciidoc b/docs/setup/connect-to-elasticsearch.asciidoc index 16e8b8a22053ce..f5c8ce3e732f2b 100644 --- a/docs/setup/connect-to-elasticsearch.asciidoc +++ b/docs/setup/connect-to-elasticsearch.asciidoc @@ -73,12 +73,25 @@ image::images/add-sample-data.png[eCommerce, flights, and web logs sample data s [[upload-data-kibana]] === Upload a data file -If you have a log file or delimited CSV, TSV, or JSON file, you can upload it, -view its fields and metrics, and optionally import it into {es}. +You can upload files, view their fields and metrics, and optionally import them to {es} with the Data Visualizer. In the *Integrations* view, search for *Upload a file*, and then drop your file on the target. -By default, you can upload a file up to 100 MB. This value is configurable up to 1 GB in -<>. +You can upload different file formats for analysis with the Data Visualizer: + +File formats supported up to 500 MB: + +* CSV +* TSV +* NDJSON +* Log files + +File formats supported up to 60 MB: + +* PDF +* Microsoft Office files (Word, Excel, PowerPoint) +* Plain Text (TXT) +* Rich Text (RTF) +* Open Document Format (ODF) NOTE: The upload feature is not intended for use as part of a repeated production process, but rather for the initial exploration of your data. diff --git a/docs/setup/images/add-data-fv.png b/docs/setup/images/add-data-fv.png index 45edbbc0fdad7e5825fb37e677af53f1f0ee7526..e65868078d8f633633e341e2afc556b686434d19 100644 GIT binary patch literal 248950 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

r7`V}orXK$-fIy>?|`h+I@Fl6?fc(i!k*-oXkL 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%0Dppw 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 CtRVvc From c53b2a8bb056af90fb100c839d0e626bf4797760 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:04:01 +0200 Subject: [PATCH 08/38] [Fleet] delete unenrolled agents task (#195544) ## Summary Closes https://github.com/elastic/kibana/issues/189506 Testing steps: - enable deleting unenrolled agents by adding `xpack.fleet.enableDeleteUnenrolledAgents: true` to `kibana.dev.yml` or turn it on on the UI - add some unenroll agents with the helper script ``` cd x-pack/plugins/fleet node scripts/create_agents/index.js --status unenrolled --count 10 info Creating 10 agents with statuses: info unenrolled: 10 info Batch complete, created 10 agent docs, took 0, errors: false info All batches complete. Created 10 agents in total. Goodbye! ``` - restart kibana or wait for the task to run and verify that the unenrolled agents were deleted ``` [2024-10-08T16:14:45.152+02:00][DEBUG][plugins.fleet.fleet:delete-unenrolled-agents-task:0.0.5] [DeleteUnenrolledAgentsTask] Executed deletion of 10 unenrolled agents [2024-10-08T16:14:45.153+02:00][INFO ][plugins.fleet.fleet:delete-unenrolled-agents-task:0.0.5] [DeleteUnenrolledAgentsTask] runTask ended: success ``` Added to UI settings: image If the flag is preconfigured, disabled update on the UI with a tooltip: image The update is also prevented from the API: image Once the preconfiguration is removed, the UI update is allowed again. ### 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: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- oas_docs/bundle.json | 52 ++++- oas_docs/bundle.serverless.json | 52 ++++- .../output/kibana.serverless.staging.yaml | 37 +++- oas_docs/output/kibana.serverless.yaml | 37 +++- oas_docs/output/kibana.staging.yaml | 37 +++- oas_docs/output/kibana.yaml | 37 +++- .../current_fields.json | 7 +- .../current_mappings.json | 12 ++ .../check_incompatible_mappings.ts | 2 + .../check_registered_types.test.ts | 2 +- .../fleet/common/types/models/settings.ts | 6 +- .../settings_page/advanced_section.tsx | 143 ++++++++++++++ .../components/settings_page/index.tsx | 3 + x-pack/plugins/fleet/server/config.ts | 1 + x-pack/plugins/fleet/server/errors/index.ts | 1 + x-pack/plugins/fleet/server/mocks/index.ts | 1 + x-pack/plugins/fleet/server/plugin.ts | 10 + .../fleet/server/routes/output/index.ts | 2 +- .../routes/settings/settings_handler.test.ts | 8 + .../fleet/server/saved_objects/index.ts | 21 ++ .../epm/packages/get_prerelease_setting.ts | 4 +- .../delete_unenrolled_agent_setting.test.ts | 71 +++++++ .../delete_unenrolled_agent_setting.ts | 39 ++++ .../fleet/server/services/settings.test.ts | 117 +++++++++++ .../plugins/fleet/server/services/settings.ts | 18 +- x-pack/plugins/fleet/server/services/setup.ts | 10 + .../delete_unenrolled_agents_task.test.ts | 138 +++++++++++++ .../tasks/delete_unenrolled_agents_task.ts | 181 ++++++++++++++++++ .../fleet/server/types/rest_spec/settings.ts | 14 +- .../fleet/server/types/so_attributes.ts | 6 +- .../check_registered_task_types.ts | 1 + 31 files changed, 1041 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/advanced_section.tsx create mode 100644 x-pack/plugins/fleet/server/services/preconfiguration/delete_unenrolled_agent_setting.test.ts create mode 100644 x-pack/plugins/fleet/server/services/preconfiguration/delete_unenrolled_agent_setting.ts create mode 100644 x-pack/plugins/fleet/server/tasks/delete_unenrolled_agents_task.test.ts create mode 100644 x-pack/plugins/fleet/server/tasks/delete_unenrolled_agents_task.ts diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index e52362ff13a6a1..8fdffea9bac41f 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -26150,7 +26150,7 @@ }, "/api/fleet/logstash_api_keys": { "post": { - "description": "Generate Logstash API keyy", + "description": "Generate Logstash API key", "operationId": "%2Fapi%2Ffleet%2Flogstash_api_keys#0", "parameters": [ { @@ -40259,6 +40259,22 @@ "item": { "additionalProperties": false, "properties": { + "delete_unenrolled_agents": { + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "is_preconfigured": { + "type": "boolean" + } + }, + "required": [ + "enabled", + "is_preconfigured" + ], + "type": "object" + }, "fleet_server_hosts": { "items": { "type": "string" @@ -40305,7 +40321,6 @@ } }, "required": [ - "prerelease_integrations_enabled", "id" ], "type": "object" @@ -40404,6 +40419,22 @@ "additional_yaml_config": { "type": "string" }, + "delete_unenrolled_agents": { + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "is_preconfigured": { + "type": "boolean" + } + }, + "required": [ + "enabled", + "is_preconfigured" + ], + "type": "object" + }, "fleet_server_hosts": { "items": { "format": "uri", @@ -40443,6 +40474,22 @@ "item": { "additionalProperties": false, "properties": { + "delete_unenrolled_agents": { + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "is_preconfigured": { + "type": "boolean" + } + }, + "required": [ + "enabled", + "is_preconfigured" + ], + "type": "object" + }, "fleet_server_hosts": { "items": { "type": "string" @@ -40489,7 +40536,6 @@ } }, "required": [ - "prerelease_integrations_enabled", "id" ], "type": "object" diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index 531ab412ce1bf0..4719fcb479bb51 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -26150,7 +26150,7 @@ }, "/api/fleet/logstash_api_keys": { "post": { - "description": "Generate Logstash API keyy", + "description": "Generate Logstash API key", "operationId": "%2Fapi%2Ffleet%2Flogstash_api_keys#0", "parameters": [ { @@ -40259,6 +40259,22 @@ "item": { "additionalProperties": false, "properties": { + "delete_unenrolled_agents": { + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "is_preconfigured": { + "type": "boolean" + } + }, + "required": [ + "enabled", + "is_preconfigured" + ], + "type": "object" + }, "fleet_server_hosts": { "items": { "type": "string" @@ -40305,7 +40321,6 @@ } }, "required": [ - "prerelease_integrations_enabled", "id" ], "type": "object" @@ -40404,6 +40419,22 @@ "additional_yaml_config": { "type": "string" }, + "delete_unenrolled_agents": { + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "is_preconfigured": { + "type": "boolean" + } + }, + "required": [ + "enabled", + "is_preconfigured" + ], + "type": "object" + }, "fleet_server_hosts": { "items": { "format": "uri", @@ -40443,6 +40474,22 @@ "item": { "additionalProperties": false, "properties": { + "delete_unenrolled_agents": { + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "is_preconfigured": { + "type": "boolean" + } + }, + "required": [ + "enabled", + "is_preconfigured" + ], + "type": "object" + }, "fleet_server_hosts": { "items": { "type": "string" @@ -40489,7 +40536,6 @@ } }, "required": [ - "prerelease_integrations_enabled", "id" ], "type": "object" diff --git a/oas_docs/output/kibana.serverless.staging.yaml b/oas_docs/output/kibana.serverless.staging.yaml index dc95e1014073af..aad5256524f4a8 100644 --- a/oas_docs/output/kibana.serverless.staging.yaml +++ b/oas_docs/output/kibana.serverless.staging.yaml @@ -23652,7 +23652,7 @@ paths: - Elastic Agent policies /api/fleet/logstash_api_keys: post: - description: Generate Logstash API keyy + description: Generate Logstash API key operationId: '%2Fapi%2Ffleet%2Flogstash_api_keys#0' parameters: - description: The version of the API to use @@ -33283,6 +33283,17 @@ paths: additionalProperties: false type: object properties: + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: type: string @@ -33314,7 +33325,6 @@ paths: version: type: string required: - - prerelease_integrations_enabled - id required: - item @@ -33376,6 +33386,17 @@ paths: properties: additional_yaml_config: type: string + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: format: uri @@ -33404,6 +33425,17 @@ paths: additionalProperties: false type: object properties: + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: type: string @@ -33435,7 +33467,6 @@ paths: version: type: string required: - - prerelease_integrations_enabled - id required: - item diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index dc95e1014073af..aad5256524f4a8 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -23652,7 +23652,7 @@ paths: - Elastic Agent policies /api/fleet/logstash_api_keys: post: - description: Generate Logstash API keyy + description: Generate Logstash API key operationId: '%2Fapi%2Ffleet%2Flogstash_api_keys#0' parameters: - description: The version of the API to use @@ -33283,6 +33283,17 @@ paths: additionalProperties: false type: object properties: + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: type: string @@ -33314,7 +33325,6 @@ paths: version: type: string required: - - prerelease_integrations_enabled - id required: - item @@ -33376,6 +33386,17 @@ paths: properties: additional_yaml_config: type: string + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: format: uri @@ -33404,6 +33425,17 @@ paths: additionalProperties: false type: object properties: + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: type: string @@ -33435,7 +33467,6 @@ paths: version: type: string required: - - prerelease_integrations_enabled - id required: - item diff --git a/oas_docs/output/kibana.staging.yaml b/oas_docs/output/kibana.staging.yaml index 222a1c5d162b8c..d8310ef7973875 100644 --- a/oas_docs/output/kibana.staging.yaml +++ b/oas_docs/output/kibana.staging.yaml @@ -27081,7 +27081,7 @@ paths: - Elastic Agent policies /api/fleet/logstash_api_keys: post: - description: Generate Logstash API keyy + description: Generate Logstash API key operationId: '%2Fapi%2Ffleet%2Flogstash_api_keys#0' parameters: - description: The version of the API to use @@ -36712,6 +36712,17 @@ paths: additionalProperties: false type: object properties: + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: type: string @@ -36743,7 +36754,6 @@ paths: version: type: string required: - - prerelease_integrations_enabled - id required: - item @@ -36805,6 +36815,17 @@ paths: properties: additional_yaml_config: type: string + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: format: uri @@ -36833,6 +36854,17 @@ paths: additionalProperties: false type: object properties: + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: type: string @@ -36864,7 +36896,6 @@ paths: version: type: string required: - - prerelease_integrations_enabled - id required: - item diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 222a1c5d162b8c..d8310ef7973875 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -27081,7 +27081,7 @@ paths: - Elastic Agent policies /api/fleet/logstash_api_keys: post: - description: Generate Logstash API keyy + description: Generate Logstash API key operationId: '%2Fapi%2Ffleet%2Flogstash_api_keys#0' parameters: - description: The version of the API to use @@ -36712,6 +36712,17 @@ paths: additionalProperties: false type: object properties: + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: type: string @@ -36743,7 +36754,6 @@ paths: version: type: string required: - - prerelease_integrations_enabled - id required: - item @@ -36805,6 +36815,17 @@ paths: properties: additional_yaml_config: type: string + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: format: uri @@ -36833,6 +36854,17 @@ paths: additionalProperties: false type: object properties: + delete_unenrolled_agents: + additionalProperties: false + type: object + properties: + enabled: + type: boolean + is_preconfigured: + type: boolean + required: + - enabled + - is_preconfigured fleet_server_hosts: items: type: string @@ -36864,7 +36896,6 @@ paths: version: type: string required: - - prerelease_integrations_enabled - id required: - item diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 3bd084fc677a6f..fa55bd4800c8be 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -233,9 +233,7 @@ "payload.connector.type", "type" ], - "cloud-security-posture-settings": [ - "rules" - ], + "cloud-security-posture-settings": [], "config": [ "buildNum" ], @@ -718,6 +716,9 @@ "vars" ], "ingest_manager_settings": [ + "delete_unenrolled_agents", + "delete_unenrolled_agents.enabled", + "delete_unenrolled_agents.is_preconfigured", "fleet_server_hosts", "has_seen_add_data_notice", "output_secret_storage_requirements_met", diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 9eb4375f082dad..9f94d36af50f77 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -2373,6 +2373,18 @@ }, "ingest_manager_settings": { "properties": { + "delete_unenrolled_agents": { + "properties": { + "enabled": { + "index": false, + "type": "boolean" + }, + "is_preconfigured": { + "index": false, + "type": "boolean" + } + } + }, "fleet_server_hosts": { "type": "keyword" }, diff --git a/packages/kbn-check-mappings-update-cli/src/compatibility/check_incompatible_mappings.ts b/packages/kbn-check-mappings-update-cli/src/compatibility/check_incompatible_mappings.ts index 1355e8547250e7..ce9e2acef8d4c1 100644 --- a/packages/kbn-check-mappings-update-cli/src/compatibility/check_incompatible_mappings.ts +++ b/packages/kbn-check-mappings-update-cli/src/compatibility/check_incompatible_mappings.ts @@ -54,5 +54,7 @@ export async function checkIncompatibleMappings({ throw createFailError( `Only mappings changes that are compatible with current mappings are allowed. Consider reaching out to the Kibana core team if you are stuck.` ); + } finally { + await esClient.indices.delete({ index: TEST_INDEX_NAME }).catch(() => {}); } } 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 d6cfcae558b2cb..c662bf9e5f7531 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 @@ -124,7 +124,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", "ingest-outputs": "daafff49255ab700e07491376fe89f04fc998b91", "ingest-package-policies": "53a94064674835fdb35e5186233bcd7052eabd22", - "ingest_manager_settings": "e794576a05d19dd5306a1e23cbb82c09bffabd65", + "ingest_manager_settings": "111a616eb72627c002029c19feb9e6c439a10505", "inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83", "kql-telemetry": "93c1d16c1a0dfca9c8842062cf5ef8f62ae401ad", "legacy-url-alias": "9b8cca3fbb2da46fd12823d3cd38fdf1c9f24bc8", diff --git a/x-pack/plugins/fleet/common/types/models/settings.ts b/x-pack/plugins/fleet/common/types/models/settings.ts index 9a5166e41df962..a63211ef14c55c 100644 --- a/x-pack/plugins/fleet/common/types/models/settings.ts +++ b/x-pack/plugins/fleet/common/types/models/settings.ts @@ -8,7 +8,7 @@ export interface BaseSettings { has_seen_add_data_notice?: boolean; fleet_server_hosts?: string[]; - prerelease_integrations_enabled: boolean; + prerelease_integrations_enabled?: boolean; } export interface Settings extends BaseSettings { @@ -19,4 +19,8 @@ export interface Settings extends BaseSettings { output_secret_storage_requirements_met?: boolean; use_space_awareness_migration_status?: 'pending' | 'success' | 'error'; use_space_awareness_migration_started_at?: string | null; + delete_unenrolled_agents?: { + enabled: boolean; + is_preconfigured: boolean; + }; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/advanced_section.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/advanced_section.tsx new file mode 100644 index 00000000000000..c4e9478e0762ee --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/advanced_section.tsx @@ -0,0 +1,143 @@ +/* + * 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, useEffect } from 'react'; + +import { + EuiTitle, + EuiLink, + EuiSpacer, + EuiDescribedFormGroup, + EuiSwitch, + EuiForm, + EuiFormRow, + EuiToolTip, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { i18n } from '@kbn/i18n'; + +import { + useAuthz, + useGetSettings, + usePutSettingsMutation, + useStartServices, +} from '../../../../hooks'; + +export const AdvancedSection: React.FunctionComponent<{}> = ({}) => { + const authz = useAuthz(); + const { docLinks, notifications } = useStartServices(); + const deleteUnenrolledAgents = + useGetSettings().data?.item?.delete_unenrolled_agents?.enabled ?? false; + const isPreconfigured = + useGetSettings().data?.item?.delete_unenrolled_agents?.is_preconfigured ?? false; + const [deleteUnenrolledAgentsChecked, setDeleteUnenrolledAgentsChecked] = + React.useState(deleteUnenrolledAgents); + const { mutateAsync: mutateSettingsAsync } = usePutSettingsMutation(); + + useEffect(() => { + if (deleteUnenrolledAgents) { + setDeleteUnenrolledAgentsChecked(deleteUnenrolledAgents); + } + }, [deleteUnenrolledAgents]); + + const updateSettings = useCallback( + async (deleteFlag: boolean) => { + try { + setDeleteUnenrolledAgentsChecked(deleteFlag); + const res = await mutateSettingsAsync({ + delete_unenrolled_agents: { + enabled: deleteFlag, + is_preconfigured: false, + }, + }); + + if (res.error) { + throw res.error; + } + } catch (error) { + setDeleteUnenrolledAgentsChecked(!deleteFlag); + notifications.toasts.addError(error, { + title: i18n.translate('xpack.fleet.errorUpdatingSettings', { + defaultMessage: 'Error updating settings', + }), + }); + } + }, + [mutateSettingsAsync, notifications.toasts] + ); + + return ( + <> + +

+ +

+ + + + + + + } + description={ +

+ + + + ), + }} + /> +

+ } + > + + + + } + checked={deleteUnenrolledAgentsChecked} + onChange={(e) => updateSettings(e.target.checked)} + disabled={!authz.fleet.allSettings || isPreconfigured} + /> + + +
+
+ + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx index 809639ecae6924..cfeb76ba0d240c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/settings_page/index.tsx @@ -14,6 +14,7 @@ import { FleetServerHostsSection } from './fleet_server_hosts_section'; import { OutputSection } from './output_section'; import { AgentBinarySection } from './agent_binary_section'; import { FleetProxiesSection } from './fleet_proxies_section'; +import { AdvancedSection } from './advanced_section'; export interface SettingsPageProps { outputs: Output[]; @@ -52,6 +53,8 @@ export const SettingsPage: React.FunctionComponent = ({ /> + + ); }; diff --git a/x-pack/plugins/fleet/server/config.ts b/x-pack/plugins/fleet/server/config.ts index 6df693096f7c6b..1797c30d15f4d2 100644 --- a/x-pack/plugins/fleet/server/config.ts +++ b/x-pack/plugins/fleet/server/config.ts @@ -130,6 +130,7 @@ export const config: PluginConfigDescriptor = { schema: schema.object( { isAirGapped: schema.maybe(schema.boolean({ defaultValue: false })), + enableDeleteUnenrolledAgents: schema.maybe(schema.boolean({ defaultValue: false })), registryUrl: schema.maybe(schema.uri({ scheme: ['http', 'https'] })), registryProxyUrl: schema.maybe(schema.uri({ scheme: ['http', 'https'] })), agents: schema.object({ diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index 09b387e7a5cee7..6782b8122a552a 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -100,6 +100,7 @@ export class OutputUnauthorizedError extends FleetError {} export class OutputInvalidError extends FleetError {} export class OutputLicenceError extends FleetError {} export class DownloadSourceError extends FleetError {} +export class DeleteUnenrolledAgentsPreconfiguredError extends FleetError {} // Not found errors export class AgentNotFoundError extends FleetNotFoundError {} diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 17f85cd252c2b5..43b113899072ed 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -139,6 +139,7 @@ export const createAppContextStartContractMock = ( } : {}), unenrollInactiveAgentsTask: {} as any, + deleteUnenrolledAgentsTask: {} as any, }; }; diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index c767424cef36a9..ced8da63e97030 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -142,6 +142,7 @@ import { fetchAgentMetrics } from './services/metrics/fetch_agent_metrics'; import { registerIntegrationFieldsExtractor } from './services/register_integration_fields_extractor'; import { registerUpgradeManagedPackagePoliciesTask } from './services/setup/managed_package_policies'; import { registerDeployAgentPoliciesTask } from './services/agent_policies/deploy_agent_policies_task'; +import { DeleteUnenrolledAgentsTask } from './tasks/delete_unenrolled_agents_task'; export interface FleetSetupDeps { security: SecurityPluginSetup; @@ -192,6 +193,7 @@ export interface FleetAppContext { auditLogger?: AuditLogger; uninstallTokenService: UninstallTokenServiceInterface; unenrollInactiveAgentsTask: UnenrollInactiveAgentsTask; + deleteUnenrolledAgentsTask: DeleteUnenrolledAgentsTask; taskManagerStart?: TaskManagerStartContract; } @@ -284,6 +286,7 @@ export class FleetPlugin private checkDeletedFilesTask?: CheckDeletedFilesTask; private fleetMetricsTask?: FleetMetricsTask; private unenrollInactiveAgentsTask?: UnenrollInactiveAgentsTask; + private deleteUnenrolledAgentsTask?: DeleteUnenrolledAgentsTask; private agentService?: AgentService; private packageService?: PackageService; @@ -628,6 +631,11 @@ export class FleetPlugin taskManager: deps.taskManager, logFactory: this.initializerContext.logger, }); + this.deleteUnenrolledAgentsTask = new DeleteUnenrolledAgentsTask({ + core, + taskManager: deps.taskManager, + logFactory: this.initializerContext.logger, + }); // Register fields metadata extractor registerIntegrationFieldsExtractor({ core, fieldsMetadata: deps.fieldsMetadata }); @@ -674,6 +682,7 @@ export class FleetPlugin messageSigningService, uninstallTokenService, unenrollInactiveAgentsTask: this.unenrollInactiveAgentsTask!, + deleteUnenrolledAgentsTask: this.deleteUnenrolledAgentsTask!, taskManagerStart: plugins.taskManager, }); licenseService.start(plugins.licensing.license$); @@ -682,6 +691,7 @@ export class FleetPlugin this.fleetUsageSender?.start(plugins.taskManager).catch(() => {}); this.checkDeletedFilesTask?.start({ taskManager: plugins.taskManager }).catch(() => {}); this.unenrollInactiveAgentsTask?.start({ taskManager: plugins.taskManager }).catch(() => {}); + this.deleteUnenrolledAgentsTask?.start({ taskManager: plugins.taskManager }).catch(() => {}); startFleetUsageLogger(plugins.taskManager).catch(() => {}); this.fleetMetricsTask ?.start(plugins.taskManager, core.elasticsearch.client.asInternalUser) diff --git a/x-pack/plugins/fleet/server/routes/output/index.ts b/x-pack/plugins/fleet/server/routes/output/index.ts index a90735f053208e..c9d5b6acdd7d33 100644 --- a/x-pack/plugins/fleet/server/routes/output/index.ts +++ b/x-pack/plugins/fleet/server/routes/output/index.ts @@ -189,7 +189,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => { fleetAuthz: { fleet: { allSettings: true }, }, - description: 'Generate Logstash API keyy', + description: 'Generate Logstash API key', options: { tags: ['oas-tag:Fleet outputs'], }, diff --git a/x-pack/plugins/fleet/server/routes/settings/settings_handler.test.ts b/x-pack/plugins/fleet/server/routes/settings/settings_handler.test.ts index 0b52c44cde269d..73641bfaed71a6 100644 --- a/x-pack/plugins/fleet/server/routes/settings/settings_handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/settings/settings_handler.test.ts @@ -31,6 +31,10 @@ jest.mock('../../services', () => ({ has_seen_add_data_notice: true, fleet_server_hosts: ['http://localhost:8220'], prerelease_integrations_enabled: true, + delete_unenrolled_agents: { + enabled: true, + is_preconfigured: false, + }, }), }, appContextService: { @@ -74,6 +78,10 @@ describe('SettingsHandler', () => { has_seen_add_data_notice: true, fleet_server_hosts: ['http://localhost:8220'], prerelease_integrations_enabled: true, + delete_unenrolled_agents: { + enabled: true, + is_preconfigured: false, + }, }, }; expect(response.ok).toHaveBeenCalledWith({ diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 0eb6c86df01e2d..ffb9381f8b30cd 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -161,6 +161,12 @@ export const getSavedObjectTypes = ( output_secret_storage_requirements_met: { type: 'boolean' }, use_space_awareness_migration_status: { type: 'keyword', index: false }, use_space_awareness_migration_started_at: { type: 'date', index: false }, + delete_unenrolled_agents: { + properties: { + enabled: { type: 'boolean', index: false }, + is_preconfigured: { type: 'boolean', index: false }, + }, + }, }, }, migrations: { @@ -181,6 +187,21 @@ export const getSavedObjectTypes = ( }, ], }, + 3: { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + delete_unenrolled_agents: { + properties: { + enabled: { type: 'boolean', index: false }, + is_preconfigured: { type: 'boolean', index: false }, + }, + }, + }, + }, + ], + }, }, }, [LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE]: { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get_prerelease_setting.ts b/x-pack/plugins/fleet/server/services/epm/packages/get_prerelease_setting.ts index df4b47d13ef2f0..48783fa7a6b54c 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get_prerelease_setting.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get_prerelease_setting.ts @@ -13,7 +13,7 @@ import { getSettings } from '../../settings'; export async function getPrereleaseFromSettings( savedObjectsClient: SavedObjectsClientContract ): Promise { - let prerelease: boolean = false; + let prerelease: boolean | undefined = false; try { ({ prerelease_integrations_enabled: prerelease } = await getSettings(savedObjectsClient)); } catch (err) { @@ -21,5 +21,5 @@ export async function getPrereleaseFromSettings( .getLogger() .warn('Error while trying to load prerelease flag from settings, defaulting to false', err); } - return prerelease; + return prerelease ?? false; } diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/delete_unenrolled_agent_setting.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration/delete_unenrolled_agent_setting.test.ts new file mode 100644 index 00000000000000..aa1a7573f225b0 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/preconfiguration/delete_unenrolled_agent_setting.test.ts @@ -0,0 +1,71 @@ +/* + * 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 { settingsService } from '..'; + +import { ensureDeleteUnenrolledAgentsSetting } from './delete_unenrolled_agent_setting'; + +jest.mock('..', () => ({ + settingsService: { + getSettingsOrUndefined: jest.fn(), + saveSettings: jest.fn(), + }, +})); + +describe('delete_unenrolled_agent_setting', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should update settings with delete_unenrolled_agents enabled', async () => { + await ensureDeleteUnenrolledAgentsSetting({} as any, true); + + expect(settingsService.saveSettings).toHaveBeenCalledWith( + expect.anything(), + { delete_unenrolled_agents: { enabled: true, is_preconfigured: true } }, + { fromSetup: true } + ); + }); + + it('should update settings with delete_unenrolled_agents disabled', async () => { + await ensureDeleteUnenrolledAgentsSetting({} as any, false); + + expect(settingsService.saveSettings).toHaveBeenCalledWith( + expect.anything(), + { delete_unenrolled_agents: { enabled: false, is_preconfigured: true } }, + { fromSetup: true } + ); + }); + + it('should update settings when previously preconfigured', async () => { + (settingsService.getSettingsOrUndefined as jest.Mock).mockResolvedValue({ + delete_unenrolled_agents: { + enabled: false, + is_preconfigured: true, + }, + }); + await ensureDeleteUnenrolledAgentsSetting({} as any); + + expect(settingsService.saveSettings).toHaveBeenCalledWith( + expect.anything(), + { delete_unenrolled_agents: { enabled: false, is_preconfigured: false } }, + { fromSetup: true } + ); + }); + + it('should not update settings when previously not preconfigured', async () => { + (settingsService.getSettingsOrUndefined as jest.Mock).mockResolvedValue({ + delete_unenrolled_agents: { + enabled: false, + is_preconfigured: false, + }, + }); + await ensureDeleteUnenrolledAgentsSetting({} as any); + + expect(settingsService.saveSettings).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/delete_unenrolled_agent_setting.ts b/x-pack/plugins/fleet/server/services/preconfiguration/delete_unenrolled_agent_setting.ts new file mode 100644 index 00000000000000..ba54e1c5146b21 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/preconfiguration/delete_unenrolled_agent_setting.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 type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; + +import { settingsService } from '..'; +import type { FleetConfigType } from '../../config'; + +export function getPreconfiguredDeleteUnenrolledAgentsSettingFromConfig( + config?: FleetConfigType +): boolean | undefined { + return config.enableDeleteUnenrolledAgents; +} + +export async function ensureDeleteUnenrolledAgentsSetting( + soClient: SavedObjectsClientContract, + enableDeleteUnenrolledAgents?: boolean +) { + if (enableDeleteUnenrolledAgents === undefined) { + const settings = await settingsService.getSettingsOrUndefined(soClient); + if (!settings?.delete_unenrolled_agents?.is_preconfigured) { + return; + } + } + await settingsService.saveSettings( + soClient, + { + delete_unenrolled_agents: { + enabled: !!enableDeleteUnenrolledAgents, + is_preconfigured: enableDeleteUnenrolledAgents !== undefined, + }, + }, + { fromSetup: true } + ); +} diff --git a/x-pack/plugins/fleet/server/services/settings.test.ts b/x-pack/plugins/fleet/server/services/settings.test.ts index 33926b0ec12f78..92fb85a335775a 100644 --- a/x-pack/plugins/fleet/server/services/settings.test.ts +++ b/x-pack/plugins/fleet/server/services/settings.test.ts @@ -14,6 +14,8 @@ import { GLOBAL_SETTINGS_ID, GLOBAL_SETTINGS_SAVED_OBJECT_TYPE } from '../../com import type { Settings } from '../types'; +import { DeleteUnenrolledAgentsPreconfiguredError } from '../errors'; + import { appContextService } from './app_context'; import { getSettings, saveSettings, settingsSetup } from './settings'; import { auditLoggingService } from './audit_logging'; @@ -225,4 +227,119 @@ describe('saveSettings', () => { }); }); }); + + it('should allow updating preconfigured setting if called from setup', async () => { + const soClient = savedObjectsClientMock.create(); + + const newData: Partial> = { + delete_unenrolled_agents: { + enabled: true, + is_preconfigured: true, + }, + }; + + soClient.find.mockResolvedValueOnce({ + saved_objects: [ + { + id: GLOBAL_SETTINGS_ID, + attributes: { + delete_unenrolled_agents: { + enabled: false, + is_preconfigured: true, + }, + }, + references: [], + type: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, + score: 0, + }, + ], + page: 1, + per_page: 10, + total: 1, + }); + mockListFleetServerHosts.mockResolvedValueOnce({ + items: [ + { + id: 'fleet-server-host', + name: 'Fleet Server Host', + is_default: true, + is_preconfigured: false, + host_urls: ['http://localhost:8220'], + }, + ], + page: 1, + perPage: 10, + total: 1, + }); + + soClient.update.mockResolvedValueOnce({ + id: GLOBAL_SETTINGS_ID, + attributes: {}, + references: [], + type: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, + }); + + await saveSettings(soClient, newData, { fromSetup: true }); + + expect(soClient.update).toHaveBeenCalled(); + }); + + it('should not allow updating preconfigured setting if not called from setup', async () => { + const soClient = savedObjectsClientMock.create(); + + const newData: Partial> = { + delete_unenrolled_agents: { + enabled: true, + is_preconfigured: true, + }, + }; + + soClient.find.mockResolvedValueOnce({ + saved_objects: [ + { + id: GLOBAL_SETTINGS_ID, + attributes: { + delete_unenrolled_agents: { + enabled: false, + is_preconfigured: true, + }, + }, + references: [], + type: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, + score: 0, + }, + ], + page: 1, + per_page: 10, + total: 1, + }); + mockListFleetServerHosts.mockResolvedValueOnce({ + items: [ + { + id: 'fleet-server-host', + name: 'Fleet Server Host', + is_default: true, + is_preconfigured: false, + host_urls: ['http://localhost:8220'], + }, + ], + page: 1, + perPage: 10, + total: 1, + }); + + soClient.update.mockResolvedValueOnce({ + id: GLOBAL_SETTINGS_ID, + attributes: {}, + references: [], + type: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, + }); + + try { + await saveSettings(soClient, newData); + fail('Expected to throw'); + } catch (e) { + expect(e).toBeInstanceOf(DeleteUnenrolledAgentsPreconfiguredError); + } + }); }); diff --git a/x-pack/plugins/fleet/server/services/settings.ts b/x-pack/plugins/fleet/server/services/settings.ts index 394a2365f3c03e..5f7433403c4e6f 100644 --- a/x-pack/plugins/fleet/server/services/settings.ts +++ b/x-pack/plugins/fleet/server/services/settings.ts @@ -13,6 +13,8 @@ import { GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, GLOBAL_SETTINGS_ID } from '../../com import type { Settings, BaseSettings } from '../../common/types'; import type { SettingsSOAttributes } from '../types'; +import { DeleteUnenrolledAgentsPreconfiguredError } from '../errors'; + import { appContextService } from './app_context'; import { listFleetServerHosts } from './fleet_server_host'; import { auditLoggingService } from './audit_logging'; @@ -47,6 +49,7 @@ export async function getSettings(soClient: SavedObjectsClientContract): Promise settingsSo.attributes.use_space_awareness_migration_started_at, fleet_server_hosts: fleetServerHosts.items.flatMap((item) => item.host_urls), preconfigured_fields: getConfigFleetServerHosts() ? ['fleet_server_hosts'] : [], + delete_unenrolled_agents: settingsSo.attributes.delete_unenrolled_agents, }; } @@ -80,7 +83,10 @@ export async function settingsSetup(soClient: SavedObjectsClientContract) { export async function saveSettings( soClient: SavedObjectsClientContract, newData: Partial>, - options?: SavedObjectsUpdateOptions & { createWithOverwrite?: boolean } + options?: SavedObjectsUpdateOptions & { + createWithOverwrite?: boolean; + fromSetup?: boolean; + } ): Promise & Pick> { const data = { ...newData }; if (data.fleet_server_hosts) { @@ -91,6 +97,16 @@ export async function saveSettings( try { const settings = await getSettings(soClient); + if ( + !options?.fromSetup && + settings.delete_unenrolled_agents?.is_preconfigured && + data.delete_unenrolled_agents + ) { + throw new DeleteUnenrolledAgentsPreconfiguredError( + `Setting delete_unenrolled_agents is preconfigured as 'enableDeleteUnenrolledAgents' and cannot be updated outside of kibana config file.` + ); + } + auditLoggingService.writeCustomSoAuditLog({ action: 'update', id: settings.id, diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index c0b86d63947690..0d6ec183531a47 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -53,6 +53,10 @@ import { cleanUpOldFileIndices } from './setup/clean_old_fleet_indices'; import type { UninstallTokenInvalidError } from './security/uninstall_token_service'; import { ensureAgentPoliciesFleetServerKeysAndPolicies } from './setup/fleet_server_policies_enrollment_keys'; import { ensureSpaceSettings } from './preconfiguration/space_settings'; +import { + ensureDeleteUnenrolledAgentsSetting, + getPreconfiguredDeleteUnenrolledAgentsSettingFromConfig, +} from './preconfiguration/delete_unenrolled_agent_setting'; export interface SetupStatus { isInitialized: boolean; @@ -195,6 +199,12 @@ async function createSetupSideEffects( logger.debug('Setting up Space settings'); await ensureSpaceSettings(appContextService.getConfig()?.spaceSettings ?? []); + logger.debug('Setting up delete unenrolled agents setting'); + await ensureDeleteUnenrolledAgentsSetting( + soClient, + getPreconfiguredDeleteUnenrolledAgentsSettingFromConfig(appContextService.getConfig()) + ); + logger.debug('Setting up Fleet outputs'); await Promise.all([ ensurePreconfiguredOutputs( diff --git a/x-pack/plugins/fleet/server/tasks/delete_unenrolled_agents_task.test.ts b/x-pack/plugins/fleet/server/tasks/delete_unenrolled_agents_task.test.ts new file mode 100644 index 00000000000000..54996c27975e33 --- /dev/null +++ b/x-pack/plugins/fleet/server/tasks/delete_unenrolled_agents_task.test.ts @@ -0,0 +1,138 @@ +/* + * 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 { ElasticsearchClientMock } from '@kbn/core/server/mocks'; +import { coreMock } from '@kbn/core/server/mocks'; +import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; +import type { TaskManagerSetupContract } from '@kbn/task-manager-plugin/server'; +import { TaskStatus } from '@kbn/task-manager-plugin/server'; +import { getDeleteTaskRunResult } from '@kbn/task-manager-plugin/server/task'; +import type { CoreSetup } from '@kbn/core/server'; +import { loggingSystemMock } from '@kbn/core/server/mocks'; + +import { settingsService } from '../services'; +import { createAppContextStartContractMock } from '../mocks'; + +import { appContextService } from '../services'; + +import { DeleteUnenrolledAgentsTask, TYPE, VERSION } from './delete_unenrolled_agents_task'; + +jest.mock('../services'); + +const MOCK_TASK_INSTANCE = { + id: `${TYPE}:${VERSION}`, + runAt: new Date(), + attempts: 0, + ownerId: '', + status: TaskStatus.Running, + startedAt: new Date(), + scheduledAt: new Date(), + retryAt: new Date(), + params: {}, + state: {}, + taskType: TYPE, +}; + +describe('DeleteUnenrolledAgentsTask', () => { + const { createSetup: coreSetupMock } = coreMock; + const { createSetup: tmSetupMock, createStart: tmStartMock } = taskManagerMock; + + let mockContract: ReturnType; + let mockTask: DeleteUnenrolledAgentsTask; + let mockCore: CoreSetup; + let mockTaskManagerSetup: jest.Mocked; + const mockSettingsService = settingsService as jest.Mocked; + + beforeEach(() => { + mockContract = createAppContextStartContractMock(); + appContextService.start(mockContract); + mockCore = coreSetupMock(); + mockTaskManagerSetup = tmSetupMock(); + mockTask = new DeleteUnenrolledAgentsTask({ + core: mockCore, + taskManager: mockTaskManagerSetup, + logFactory: loggingSystemMock.create(), + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('Task lifecycle', () => { + it('Should create task', () => { + expect(mockTask).toBeInstanceOf(DeleteUnenrolledAgentsTask); + }); + + it('Should register task', () => { + expect(mockTaskManagerSetup.registerTaskDefinitions).toHaveBeenCalled(); + }); + + it('Should schedule task', async () => { + const mockTaskManagerStart = tmStartMock(); + await mockTask.start({ taskManager: mockTaskManagerStart }); + expect(mockTaskManagerStart.ensureScheduled).toHaveBeenCalled(); + }); + }); + + describe('Task logic', () => { + let esClient: ElasticsearchClientMock; + const runTask = async (taskInstance = MOCK_TASK_INSTANCE) => { + const mockTaskManagerStart = tmStartMock(); + await mockTask.start({ taskManager: mockTaskManagerStart }); + const createTaskRunner = + mockTaskManagerSetup.registerTaskDefinitions.mock.calls[0][0][TYPE].createTaskRunner; + const taskRunner = createTaskRunner({ taskInstance }); + return taskRunner.run(); + }; + + beforeEach(async () => { + const [{ elasticsearch }] = await mockCore.getStartServices(); + esClient = elasticsearch.client.asInternalUser as ElasticsearchClientMock; + esClient.deleteByQuery.mockResolvedValue({ deleted: 10 }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('Should delete unenrolled agents', async () => { + mockSettingsService.getSettingsOrUndefined.mockResolvedValue({ + delete_unenrolled_agents: { + enabled: true, + is_preconfigured: false, + }, + id: '1', + }); + + await runTask(); + + expect(esClient.deleteByQuery).toHaveBeenCalled(); + }); + + it('Should not run if task is outdated', async () => { + const result = await runTask({ ...MOCK_TASK_INSTANCE, id: 'old-id' }); + + expect(esClient.deleteByQuery).not.toHaveBeenCalled(); + expect(result).toEqual(getDeleteTaskRunResult()); + }); + + it('Should exit if delete unenrolled agents flag is false', async () => { + mockSettingsService.getSettingsOrUndefined.mockResolvedValue({ + delete_unenrolled_agents: { + enabled: false, + is_preconfigured: false, + }, + id: '1', + }); + + await runTask(); + + expect(esClient.deleteByQuery).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/tasks/delete_unenrolled_agents_task.ts b/x-pack/plugins/fleet/server/tasks/delete_unenrolled_agents_task.ts new file mode 100644 index 00000000000000..440567effac7d3 --- /dev/null +++ b/x-pack/plugins/fleet/server/tasks/delete_unenrolled_agents_task.ts @@ -0,0 +1,181 @@ +/* + * 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 { SavedObjectsClient } from '@kbn/core/server'; +import type { + CoreSetup, + ElasticsearchClient, + Logger, + SavedObjectsClientContract, +} from '@kbn/core/server'; +import type { + ConcreteTaskInstance, + TaskManagerSetupContract, + TaskManagerStartContract, +} from '@kbn/task-manager-plugin/server'; +import { getDeleteTaskRunResult } from '@kbn/task-manager-plugin/server/task'; +import type { LoggerFactory } from '@kbn/core/server'; +import { errors } from '@elastic/elasticsearch'; + +import { AGENTS_INDEX } from '../../common/constants'; + +import { settingsService } from '../services'; + +export const TYPE = 'fleet:delete-unenrolled-agents-task'; +export const VERSION = '1.0.0'; +const TITLE = 'Fleet Delete Unenrolled Agents Task'; +const SCOPE = ['fleet']; +const INTERVAL = '1h'; +const TIMEOUT = '1m'; + +interface DeleteUnenrolledAgentsTaskSetupContract { + core: CoreSetup; + taskManager: TaskManagerSetupContract; + logFactory: LoggerFactory; +} + +interface DeleteUnenrolledAgentsTaskStartContract { + taskManager: TaskManagerStartContract; +} + +export class DeleteUnenrolledAgentsTask { + private logger: Logger; + private wasStarted: boolean = false; + private abortController = new AbortController(); + + constructor(setupContract: DeleteUnenrolledAgentsTaskSetupContract) { + const { core, taskManager, logFactory } = setupContract; + this.logger = logFactory.get(this.taskId); + + taskManager.registerTaskDefinitions({ + [TYPE]: { + title: TITLE, + timeout: TIMEOUT, + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + return { + run: async () => { + return this.runTask(taskInstance, core); + }, + cancel: async () => { + this.abortController.abort('Task timed out'); + }, + }; + }, + }, + }); + } + + public start = async ({ taskManager }: DeleteUnenrolledAgentsTaskStartContract) => { + if (!taskManager) { + this.logger.error('[DeleteUnenrolledAgentsTask] Missing required service during start'); + return; + } + + this.wasStarted = true; + this.logger.info(`[DeleteUnenrolledAgentsTask] Started with interval of [${INTERVAL}]`); + + try { + await taskManager.ensureScheduled({ + id: this.taskId, + taskType: TYPE, + scope: SCOPE, + schedule: { + interval: INTERVAL, + }, + state: {}, + params: { version: VERSION }, + }); + } catch (e) { + this.logger.error(`Error scheduling task DeleteUnenrolledAgentsTask, error: ${e.message}`, e); + } + }; + + private get taskId(): string { + return `${TYPE}:${VERSION}`; + } + + private endRun(msg: string = '') { + this.logger.info(`[DeleteUnenrolledAgentsTask] runTask ended${msg ? ': ' + msg : ''}`); + } + + public async deleteUnenrolledAgents(esClient: ElasticsearchClient) { + this.logger.debug(`[DeleteUnenrolledAgentsTask] Fetching unenrolled agents`); + + const response = await esClient.deleteByQuery( + { + index: AGENTS_INDEX, + body: { + query: { + bool: { + filter: [ + { + term: { + active: false, + }, + }, + { exists: { field: 'unenrolled_at' } }, + ], + }, + }, + }, + }, + { signal: this.abortController.signal } + ); + + this.logger.debug( + `[DeleteUnenrolledAgentsTask] Executed deletion of ${response.deleted} unenrolled agents` + ); + } + + public async isDeleteUnenrolledAgentsEnabled( + soClient: SavedObjectsClientContract + ): Promise { + const settings = await settingsService.getSettingsOrUndefined(soClient); + return settings?.delete_unenrolled_agents?.enabled ?? false; + } + + public runTask = async (taskInstance: ConcreteTaskInstance, core: CoreSetup) => { + if (!this.wasStarted) { + this.logger.debug('[DeleteUnenrolledAgentsTask] runTask Aborted. Task not started yet'); + return; + } + // Check that this task is current + if (taskInstance.id !== this.taskId) { + this.logger.debug( + `[DeleteUnenrolledAgentsTask] Outdated task version: Got [${taskInstance.id}] from task instance. Current version is [${this.taskId}]` + ); + return getDeleteTaskRunResult(); + } + + this.logger.info(`[runTask()] started`); + + const [coreStart] = await core.getStartServices(); + const esClient = coreStart.elasticsearch.client.asInternalUser; + const soClient = new SavedObjectsClient(coreStart.savedObjects.createInternalRepository()); + + try { + if (!(await this.isDeleteUnenrolledAgentsEnabled(soClient))) { + this.logger.debug( + '[DeleteUnenrolledAgentsTask] Delete unenrolled agents flag is disabled, returning.' + ); + this.endRun('Delete unenrolled agents is disabled'); + return; + } + await this.deleteUnenrolledAgents(esClient); + + this.endRun('success'); + } catch (err) { + if (err instanceof errors.RequestAbortedError) { + this.logger.warn(`[DeleteUnenrolledAgentsTask] request aborted due to timeout: ${err}`); + this.endRun(); + return; + } + this.logger.error(`[DeleteUnenrolledAgentsTask] error: ${err}`); + this.endRun('error'); + } + }; +} diff --git a/x-pack/plugins/fleet/server/types/rest_spec/settings.ts b/x-pack/plugins/fleet/server/types/rest_spec/settings.ts index 0adaa69f1d30a7..459070fa9591a2 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/settings.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/settings.ts @@ -40,6 +40,12 @@ export const PutSettingsRequestSchema = { ), kibana_ca_sha256: schema.maybe(schema.string()), prerelease_integrations_enabled: schema.maybe(schema.boolean()), + delete_unenrolled_agents: schema.maybe( + schema.object({ + enabled: schema.boolean(), + is_preconfigured: schema.boolean(), + }) + ), }), }; @@ -56,7 +62,7 @@ export const SettingsResponseSchema = schema.object({ item: schema.object({ has_seen_add_data_notice: schema.maybe(schema.boolean()), fleet_server_hosts: schema.maybe(schema.arrayOf(schema.string())), - prerelease_integrations_enabled: schema.boolean(), + prerelease_integrations_enabled: schema.maybe(schema.boolean()), id: schema.string(), version: schema.maybe(schema.string()), preconfigured_fields: schema.maybe(schema.arrayOf(schema.literal('fleet_server_hosts'))), @@ -66,6 +72,12 @@ export const SettingsResponseSchema = schema.object({ schema.oneOf([schema.literal('pending'), schema.literal('success'), schema.literal('error')]) ), use_space_awareness_migration_started_at: schema.maybe(schema.string()), + delete_unenrolled_agents: schema.maybe( + schema.object({ + enabled: schema.boolean(), + is_preconfigured: schema.boolean(), + }) + ), }), }); diff --git a/x-pack/plugins/fleet/server/types/so_attributes.ts b/x-pack/plugins/fleet/server/types/so_attributes.ts index 9be09fe4ee5549..31207dda64bc8e 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -234,13 +234,17 @@ export type OutputSOAttributes = | OutputSoKafkaAttributes; export interface SettingsSOAttributes { - prerelease_integrations_enabled: boolean; + prerelease_integrations_enabled?: boolean; has_seen_add_data_notice?: boolean; fleet_server_hosts?: string[]; secret_storage_requirements_met?: boolean; output_secret_storage_requirements_met?: boolean; use_space_awareness_migration_status?: 'pending' | 'success' | 'error'; use_space_awareness_migration_started_at?: string | null; + delete_unenrolled_agents?: { + enabled: boolean; + is_preconfigured: boolean; + }; } export interface SpaceSettingsSOAttributes { diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts index f5ccfcc3ca56f0..eeb8b6e3474c9e 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/check_registered_task_types.ts @@ -139,6 +139,7 @@ export default function ({ getService }: FtrProviderContext) { 'endpoint:metadata-check-transforms-task', 'endpoint:user-artifact-packager', 'fleet:check-deleted-files-task', + 'fleet:delete-unenrolled-agents-task', 'fleet:deploy_agent_policies', 'fleet:reassign_action:retry', 'fleet:request_diagnostics:retry', From fed9a193869739c2fae0bd7a49087fa1e216f5ac Mon Sep 17 00:00:00 2001 From: Abdul Wahab Zahid Date: Mon, 14 Oct 2024 11:26:24 +0200 Subject: [PATCH 09/38] Remember tab choice between logs explorer and discover (#194930) Closes #193321 ## Summary The PR adds the redirection point when "Discover" menu item is clicked on the sidenav in serverless (or solution nav on stateful). Based on what tab between "Discover" or "Logs Explorer" the user clicked recently, "Discover" will point to that app/tab. Previously, "Discover" would always point to "Logs Explorer" on serverless and to "Discover" on stateful. In order to implement this, a temporary app `last-used-logs-viewer` is registered in `observability-logs-explorer` plugin whose only job is to read the last stored value in local storage and perform the redirection. Doing the redirection from a temporary app should help prevent triggering unnecessary telemetry and history entries. And it should be fairly easy to undo once context aware redirection is in place. ~With this implementation, only the behavior of user clicking "Discover" on the sidenav and clicking the tabs is affected and any deeplinks from other apps or direct links should work as is.~ The tab choice will be updated even if the apps are visited via url. https://github.com/user-attachments/assets/8a0308db-9ddb-47b6-b1a5-8ed70662040d --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- packages/deeplinks/observability/constants.ts | 3 + .../deeplinks/observability/deep_links.ts | 3 + packages/deeplinks/observability/index.ts | 1 + .../locators/observability_logs_explorer.ts | 3 + .../logs_explorer_tabs.test.tsx | 26 +++- .../logs_explorer_tabs/logs_explorer_tabs.tsx | 25 +++- .../collectors/application_usage/schema.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 131 ++++++++++++++++++ .../observability/public/navigation_tree.ts | 16 ++- .../applications/last_used_logs_viewer.tsx | 64 +++++++++ .../public/plugin.ts | 16 +++ .../observability_logs_explorer/tsconfig.json | 1 + .../public/navigation_tree.ts | 2 +- .../test_suites/observability/navigation.ts | 5 +- 14 files changed, 287 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/observability_solution/observability_logs_explorer/public/applications/last_used_logs_viewer.tsx diff --git a/packages/deeplinks/observability/constants.ts b/packages/deeplinks/observability/constants.ts index 45868fa3a16b2f..25642ba69613a6 100644 --- a/packages/deeplinks/observability/constants.ts +++ b/packages/deeplinks/observability/constants.ts @@ -11,6 +11,9 @@ export const LOGS_APP_ID = 'logs'; export const OBSERVABILITY_LOGS_EXPLORER_APP_ID = 'observability-logs-explorer'; +// TODO: Remove the app once context-aware switching between discover and observability logs explorer is implemented +export const LAST_USED_LOGS_VIEWER_APP_ID = 'last-used-logs-viewer'; + export const OBSERVABILITY_OVERVIEW_APP_ID = 'observability-overview'; export const METRICS_APP_ID = 'metrics'; diff --git a/packages/deeplinks/observability/deep_links.ts b/packages/deeplinks/observability/deep_links.ts index 088b9c866c03de..1253b4e889fcf8 100644 --- a/packages/deeplinks/observability/deep_links.ts +++ b/packages/deeplinks/observability/deep_links.ts @@ -12,6 +12,7 @@ import { LOGS_APP_ID, METRICS_APP_ID, OBSERVABILITY_LOGS_EXPLORER_APP_ID, + LAST_USED_LOGS_VIEWER_APP_ID, OBSERVABILITY_ONBOARDING_APP_ID, OBSERVABILITY_OVERVIEW_APP_ID, SYNTHETICS_APP_ID, @@ -24,6 +25,7 @@ import { type LogsApp = typeof LOGS_APP_ID; type ObservabilityLogsExplorerApp = typeof OBSERVABILITY_LOGS_EXPLORER_APP_ID; +type LastUsedLogsViewerApp = typeof LAST_USED_LOGS_VIEWER_APP_ID; type ObservabilityOverviewApp = typeof OBSERVABILITY_OVERVIEW_APP_ID; type MetricsApp = typeof METRICS_APP_ID; type ApmApp = typeof APM_APP_ID; @@ -38,6 +40,7 @@ type InventoryApp = typeof INVENTORY_APP_ID; export type AppId = | LogsApp | ObservabilityLogsExplorerApp + | LastUsedLogsViewerApp | ObservabilityOverviewApp | ObservabilityOnboardingApp | ApmApp diff --git a/packages/deeplinks/observability/index.ts b/packages/deeplinks/observability/index.ts index 7185a4cfbcd6f0..8bedc43f5d6a8b 100644 --- a/packages/deeplinks/observability/index.ts +++ b/packages/deeplinks/observability/index.ts @@ -10,6 +10,7 @@ export { LOGS_APP_ID, OBSERVABILITY_LOGS_EXPLORER_APP_ID, + LAST_USED_LOGS_VIEWER_APP_ID, OBSERVABILITY_ONBOARDING_APP_ID, OBSERVABILITY_OVERVIEW_APP_ID, AI_ASSISTANT_APP_ID, diff --git a/packages/deeplinks/observability/locators/observability_logs_explorer.ts b/packages/deeplinks/observability/locators/observability_logs_explorer.ts index 037acb7d946add..ae4cbc12cec6d1 100644 --- a/packages/deeplinks/observability/locators/observability_logs_explorer.ts +++ b/packages/deeplinks/observability/locators/observability_logs_explorer.ts @@ -49,3 +49,6 @@ export interface ObsLogsExplorerDataViewLocatorParams extends DatasetLocatorPara */ id: string; } + +// To store the last used logs viewer (either of discover or observability-logs-explorer) +export const OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY = 'obs-logs-explorer:lastUsedViewer'; diff --git a/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.test.tsx b/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.test.tsx index 1782dc7ad6ac74..e353fe1971ec94 100644 --- a/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.test.tsx +++ b/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.test.tsx @@ -10,10 +10,21 @@ import userEvent from '@testing-library/user-event'; import { render, screen } from '@testing-library/react'; import React from 'react'; +import { DISCOVER_APP_ID } from '@kbn/deeplinks-analytics'; +import { + ALL_DATASETS_LOCATOR_ID, + OBSERVABILITY_LOGS_EXPLORER_APP_ID, +} from '@kbn/deeplinks-observability'; import { discoverServiceMock } from '../../__mocks__/services'; import { LogsExplorerTabs, LogsExplorerTabsProps } from './logs_explorer_tabs'; import { DISCOVER_APP_LOCATOR } from '../../../common'; -import { ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability'; + +const mockSetLastUsedViewer = jest.fn(); +jest.mock('react-use/lib/useLocalStorage', () => { + return jest.fn((key: string, _initialValue: string) => { + return [undefined, mockSetLastUsedViewer]; // Always use undefined as the initial value + }); +}); const createMockLocator = (id: string) => ({ navigate: jest.fn(), @@ -46,11 +57,12 @@ describe('LogsExplorerTabs', () => { }, } as unknown as typeof discoverServiceMock; - render(); + const { unmount } = render(); return { mockDiscoverLocator, mockLogsExplorerLocator, + unmount, }; }; @@ -86,4 +98,14 @@ describe('LogsExplorerTabs', () => { await userEvent.click(getDiscoverTab()); expect(mockDiscoverLocator.navigate).toHaveBeenCalledWith({}); }); + + it('should update the last used viewer in local storage for selectedTab', async () => { + const { unmount } = renderTabs('discover'); + expect(mockSetLastUsedViewer).toHaveBeenCalledWith(DISCOVER_APP_ID); + + unmount(); + mockSetLastUsedViewer.mockClear(); + renderTabs('logs-explorer'); + expect(mockSetLastUsedViewer).toHaveBeenCalledWith(OBSERVABILITY_LOGS_EXPLORER_APP_ID); + }); }); diff --git a/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.tsx b/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.tsx index 1eec001464d2a7..c7082c21344ac1 100644 --- a/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.tsx +++ b/src/plugins/discover/public/components/logs_explorer_tabs/logs_explorer_tabs.tsx @@ -8,9 +8,16 @@ */ import { EuiTab, EuiTabs, useEuiTheme } from '@elastic/eui'; -import { AllDatasetsLocatorParams, ALL_DATASETS_LOCATOR_ID } from '@kbn/deeplinks-observability'; +import { DISCOVER_APP_ID } from '@kbn/deeplinks-analytics'; +import { + AllDatasetsLocatorParams, + ALL_DATASETS_LOCATOR_ID, + OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY, + OBSERVABILITY_LOGS_EXPLORER_APP_ID, +} from '@kbn/deeplinks-observability'; import { i18n } from '@kbn/i18n'; -import React, { MouseEvent } from 'react'; +import React, { MouseEvent, useEffect } from 'react'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; import { DiscoverAppLocatorParams, DISCOVER_APP_LOCATOR } from '../../../common'; import type { DiscoverServices } from '../../build_services'; @@ -29,6 +36,10 @@ export const LogsExplorerTabs = ({ services, selectedTab }: LogsExplorerTabsProp const discoverUrl = discoverLocator?.getRedirectUrl(emptyParams); const logsExplorerUrl = logsExplorerLocator?.getRedirectUrl(emptyParams); + const [lastUsedViewer, setLastUsedViewer] = useLocalStorage< + typeof DISCOVER_APP_ID | typeof OBSERVABILITY_LOGS_EXPLORER_APP_ID + >(OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY, OBSERVABILITY_LOGS_EXPLORER_APP_ID); + const navigateToDiscover = createNavigateHandler(() => { if (selectedTab !== 'discover') { discoverLocator?.navigate(emptyParams); @@ -41,6 +52,16 @@ export const LogsExplorerTabs = ({ services, selectedTab }: LogsExplorerTabsProp } }); + useEffect(() => { + if (selectedTab === 'discover' && lastUsedViewer !== DISCOVER_APP_ID) { + setLastUsedViewer(DISCOVER_APP_ID); + } + + if (selectedTab === 'logs-explorer' && lastUsedViewer !== OBSERVABILITY_LOGS_EXPLORER_APP_ID) { + setLastUsedViewer(OBSERVABILITY_LOGS_EXPLORER_APP_ID); + } + }, [setLastUsedViewer, lastUsedViewer, selectedTab]); + return ( { + ReactDOM.render( + + + , + appParams.element + ); + + return () => { + ReactDOM.unmountComponentAtNode(appParams.element); + }; +}; + +export const LastUsedLogsViewerRedirect = ({ core }: { core: CoreStart }) => { + const location = useLocation(); + const path = `${location.pathname}${location.search}`; + const [lastUsedLogsViewApp] = useLocalStorage< + typeof DISCOVER_APP_ID | typeof OBSERVABILITY_LOGS_EXPLORER_APP_ID + >(OBS_LOGS_EXPLORER_LOGS_VIEWER_KEY, OBSERVABILITY_LOGS_EXPLORER_APP_ID); + + if ( + lastUsedLogsViewApp && + lastUsedLogsViewApp !== DISCOVER_APP_ID && + lastUsedLogsViewApp !== OBSERVABILITY_LOGS_EXPLORER_APP_ID + ) { + throw new Error( + `Invalid last used logs viewer app: "${lastUsedLogsViewApp}". Allowed values are "${DISCOVER_APP_ID}" and "${OBSERVABILITY_LOGS_EXPLORER_APP_ID}"` + ); + } + + useEffect(() => { + if (lastUsedLogsViewApp === DISCOVER_APP_ID) { + core.application.navigateToApp(DISCOVER_APP_ID, { replace: true, path }); + } + + if (lastUsedLogsViewApp === OBSERVABILITY_LOGS_EXPLORER_APP_ID) { + core.application.navigateToApp(OBSERVABILITY_LOGS_EXPLORER_APP_ID, { replace: true, path }); + } + }, [core, path, lastUsedLogsViewApp]); + + return <>; +}; diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/public/plugin.ts b/x-pack/plugins/observability_solution/observability_logs_explorer/public/plugin.ts index 798a03da0ebdfc..2e6ab0aeeaa0f3 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/public/plugin.ts +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/public/plugin.ts @@ -97,6 +97,22 @@ export class ObservabilityLogsExplorerPlugin }, }); + // App used solely to redirect to either "/app/observability-logs-explorer" or "/app/discover" + // based on the last used app value in localStorage + core.application.register({ + id: 'last-used-logs-viewer', + title: logsExplorerAppTitle, + visibleIn: [], + mount: async (appMountParams: AppMountParameters) => { + const [coreStart] = await core.getStartServices(); + const { renderLastUsedLogsViewerRedirect } = await import( + './applications/last_used_logs_viewer' + ); + + return renderLastUsedLogsViewerRedirect(coreStart, appMountParams); + }, + }); + core.analytics.registerEventType(DATA_RECEIVED_TELEMETRY_EVENT); // Register Locators diff --git a/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json b/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json index 6ea751aaed3def..be2b3c9efdff66 100644 --- a/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_logs_explorer/tsconfig.json @@ -50,6 +50,7 @@ "@kbn/core-analytics-browser", "@kbn/react-hooks", "@kbn/logs-data-access-plugin", + "@kbn/deeplinks-analytics", ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/serverless_observability/public/navigation_tree.ts b/x-pack/plugins/serverless_observability/public/navigation_tree.ts index e3c61ec0b29e32..5df900ee468129 100644 --- a/x-pack/plugins/serverless_observability/public/navigation_tree.ts +++ b/x-pack/plugins/serverless_observability/public/navigation_tree.ts @@ -24,7 +24,7 @@ export const navigationTree: NavigationTreeDefinition = { title: i18n.translate('xpack.serverlessObservability.nav.discover', { defaultMessage: 'Discover', }), - link: 'observability-logs-explorer', + link: 'last-used-logs-viewer', // avoid duplicate "Discover" breadcrumbs breadcrumbStatus: 'hidden', renderAs: 'item', diff --git a/x-pack/test_serverless/functional/test_suites/observability/navigation.ts b/x-pack/test_serverless/functional/test_suites/observability/navigation.ts index ce6b68cd618a1e..bcd9c031714f75 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/navigation.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/navigation.ts @@ -35,9 +35,10 @@ export default function ({ getPageObject, getService }: FtrProviderContext) { await svlCommonNavigation.sidenav.expectSectionClosed('project_settings_project_nav'); // navigate to the logs explorer tab by default - await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'observability-logs-explorer' }); + // 'last-used-logs-viewer' is wrapper app to handle the navigation between logs explorer and discover + await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'last-used-logs-viewer' }); await svlCommonNavigation.sidenav.expectLinkActive({ - deepLinkId: 'observability-logs-explorer', + deepLinkId: 'last-used-logs-viewer', }); await svlCommonNavigation.breadcrumbs.expectBreadcrumbExists({ deepLinkId: 'observability-logs-explorer', From f962cdcd796af9908449155c989dd03438165773 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 14 Oct 2024 11:52:16 +0200 Subject: [PATCH 10/38] [ES|QL] [Discover] Displays the histogram suggestion always for non transformational commands (#195863) ## Summary Closes https://github.com/elastic/kibana/issues/195752 This PR is fixing 2 bugs: - It filters out counter fields from the breakdown as they are not supported. I created a new util for this - Fixes a bug unrelated with the breakdown (it also exists in previous minors). The LensVis service is computing suggestions and pushes them to `availableSuggestionsWithType `. In some indexes (it depends on the types of the first 5 columns of the index) the lens suggestions api might return a suggestion. So in that case the array has the histogram suggestion + the suggestion from the suggestions api. So the service will pick the first one which is not the histogram. But we know that in case of non transformational commands we want to suggest the histogram. So this PR is fixing it by ensuring that the array is cleaned up before pushing the histogram suggestion. Note: The 2 bugs are unrelated I just decided to fix them in one PR as they are both histogram bugs. ### 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 --- packages/kbn-esql-utils/index.ts | 1 + packages/kbn-esql-utils/src/index.ts | 2 +- .../src/utils/esql_fields_utils.test.ts | 43 ++++++++++++++++++- .../src/utils/esql_fields_utils.ts | 21 +++++++++ .../public/chart/breakdown_field_selector.tsx | 5 ++- .../lens_vis_service.attributes.test.ts | 5 ++- .../lens_vis_service.suggestions.test.ts | 31 +++++++++++++ .../public/services/lens_vis_service.ts | 5 ++- 8 files changed, 106 insertions(+), 7 deletions(-) diff --git a/packages/kbn-esql-utils/index.ts b/packages/kbn-esql-utils/index.ts index 333557964d8734..ee47c0321e2e32 100644 --- a/packages/kbn-esql-utils/index.ts +++ b/packages/kbn-esql-utils/index.ts @@ -30,6 +30,7 @@ export { retrieveMetadataColumns, getQueryColumnsFromESQLQuery, isESQLColumnSortable, + isESQLColumnGroupable, TextBasedLanguages, } from './src'; diff --git a/packages/kbn-esql-utils/src/index.ts b/packages/kbn-esql-utils/src/index.ts index 3b3228e7a2a4ad..cf530be20d7ae4 100644 --- a/packages/kbn-esql-utils/src/index.ts +++ b/packages/kbn-esql-utils/src/index.ts @@ -31,4 +31,4 @@ export { getStartEndParams, hasStartEndParams, } from './utils/run_query'; -export { isESQLColumnSortable } from './utils/esql_fields_utils'; +export { isESQLColumnSortable, isESQLColumnGroupable } from './utils/esql_fields_utils'; diff --git a/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts b/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts index ef8a24e686bd62..bfc3a4d6708f0f 100644 --- a/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts +++ b/packages/kbn-esql-utils/src/utils/esql_fields_utils.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ import type { DatatableColumn } from '@kbn/expressions-plugin/common'; -import { isESQLColumnSortable } from './esql_fields_utils'; +import { isESQLColumnSortable, isESQLColumnGroupable } from './esql_fields_utils'; describe('esql fields helpers', () => { describe('isESQLColumnSortable', () => { @@ -63,4 +63,45 @@ describe('esql fields helpers', () => { expect(isESQLColumnSortable(keywordField)).toBeTruthy(); }); }); + + describe('isESQLColumnGroupable', () => { + it('returns false for unsupported fields', () => { + const unsupportedField = { + id: 'unsupported', + name: 'unsupported', + meta: { + type: 'unknown', + esType: 'unknown', + }, + isNull: false, + } as DatatableColumn; + expect(isESQLColumnGroupable(unsupportedField)).toBeFalsy(); + }); + + it('returns false for counter fields', () => { + const tsdbField = { + id: 'tsbd_counter', + name: 'tsbd_counter', + meta: { + type: 'number', + esType: 'counter_long', + }, + isNull: false, + } as DatatableColumn; + expect(isESQLColumnGroupable(tsdbField)).toBeFalsy(); + }); + + it('returns true for everything else', () => { + const keywordField = { + id: 'sortable', + name: 'sortable', + meta: { + type: 'string', + esType: 'keyword', + }, + isNull: false, + } as DatatableColumn; + expect(isESQLColumnGroupable(keywordField)).toBeTruthy(); + }); + }); }); diff --git a/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts b/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts index f5a0fe7b813406..e2c5c785f8f58a 100644 --- a/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts +++ b/packages/kbn-esql-utils/src/utils/esql_fields_utils.ts @@ -12,6 +12,7 @@ import type { DatatableColumn } from '@kbn/expressions-plugin/common'; const SPATIAL_FIELDS = ['geo_point', 'geo_shape', 'point', 'shape']; const SOURCE_FIELD = '_source'; const TSDB_COUNTER_FIELDS_PREFIX = 'counter_'; +const UNKNOWN_FIELD = 'unknown'; /** * Check if a column is sortable. @@ -38,3 +39,23 @@ export const isESQLColumnSortable = (column: DatatableColumn): boolean => { return true; }; + +/** + * Check if a column is groupable (| STATS ... BY ). + * + * @param column The DatatableColumn of the field. + * @returns True if the column is groupable, false otherwise. + */ + +export const isESQLColumnGroupable = (column: DatatableColumn): boolean => { + // we don't allow grouping on the unknown field types + if (column.meta?.type === UNKNOWN_FIELD) { + return false; + } + // we don't allow grouping on tsdb counter fields + if (column.meta?.esType && column.meta?.esType?.indexOf(TSDB_COUNTER_FIELDS_PREFIX) !== -1) { + return false; + } + + return true; +}; diff --git a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx index b3c49e27c60115..0d6aa1e75fe801 100644 --- a/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx +++ b/src/plugins/unified_histogram/public/chart/breakdown_field_selector.tsx @@ -11,6 +11,7 @@ import React, { useCallback, useMemo } from 'react'; import { EuiSelectableOption } from '@elastic/eui'; import { FieldIcon, getFieldIconProps, comboBoxFieldOptionMatcher } from '@kbn/field-utils'; import { css } from '@emotion/react'; +import { isESQLColumnGroupable } from '@kbn/esql-utils'; import { type DataView, DataViewField } from '@kbn/data-views-plugin/common'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import { convertDatatableColumnToDataViewFieldSpec } from '@kbn/data-view-utils'; @@ -34,10 +35,10 @@ export interface BreakdownFieldSelectorProps { const mapToDropdownFields = (dataView: DataView, esqlColumns?: DatatableColumn[]) => { if (esqlColumns) { return ( + // filter out unsupported field types and counter time series metrics esqlColumns + .filter(isESQLColumnGroupable) .map((column) => new DataViewField(convertDatatableColumnToDataViewFieldSpec(column))) - // filter out unsupported field types - .filter((field) => field.type !== 'unknown') ); } diff --git a/src/plugins/unified_histogram/public/services/lens_vis_service.attributes.test.ts b/src/plugins/unified_histogram/public/services/lens_vis_service.attributes.test.ts index bc76a0163c8be3..75734387a93688 100644 --- a/src/plugins/unified_histogram/public/services/lens_vis_service.attributes.test.ts +++ b/src/plugins/unified_histogram/public/services/lens_vis_service.attributes.test.ts @@ -674,7 +674,8 @@ describe('LensVisService attributes', () => { }, ], "query": Object { - "esql": "from logstash-* | limit 10", + "esql": "from logstash-* | limit 10 + | EVAL timestamp=DATE_TRUNC(10 minute, timestamp) | stats results = count(*) by timestamp | rename timestamp as \`timestamp every 10 minute\`", }, "visualization": Object { "gridConfig": Object { @@ -706,7 +707,7 @@ describe('LensVisService attributes', () => { "timeField": "timestamp", "timeInterval": undefined, }, - "suggestionType": "lensSuggestion", + "suggestionType": "histogramForESQL", } `); }); diff --git a/src/plugins/unified_histogram/public/services/lens_vis_service.suggestions.test.ts b/src/plugins/unified_histogram/public/services/lens_vis_service.suggestions.test.ts index 1719adebe7a492..09ee2a68ec248c 100644 --- a/src/plugins/unified_histogram/public/services/lens_vis_service.suggestions.test.ts +++ b/src/plugins/unified_histogram/public/services/lens_vis_service.suggestions.test.ts @@ -254,6 +254,37 @@ describe('LensVisService suggestions', () => { expect(lensVis.visContext?.attributes.state.query).toStrictEqual(histogramQuery); }); + test('should return histogramSuggestion even if suggestions returned by the api', async () => { + const lensVis = await getLensVisMock({ + filters: [], + query: { esql: 'from the-data-view | limit 100' }, + dataView: dataViewMock, + timeInterval: 'auto', + timeRange: { + from: '2023-09-03T08:00:00.000Z', + to: '2023-09-04T08:56:28.274Z', + }, + breakdownField: undefined, + columns: [ + { + id: 'var0', + name: 'var0', + meta: { + type: 'number', + }, + }, + ], + isPlainRecord: true, + allSuggestions: allSuggestionsMock, + hasHistogramSuggestionForESQL: true, + }); + + expect(lensVis.currentSuggestionContext?.type).toBe( + UnifiedHistogramSuggestionType.histogramForESQL + ); + expect(lensVis.currentSuggestionContext?.suggestion).toBeDefined(); + }); + test('should return histogramSuggestion if no suggestions returned by the api with a geo point breakdown field correctly', async () => { const lensVis = await getLensVisMock({ filters: [], diff --git a/src/plugins/unified_histogram/public/services/lens_vis_service.ts b/src/plugins/unified_histogram/public/services/lens_vis_service.ts index 25bb8be6f6242b..e48ebc64590713 100644 --- a/src/plugins/unified_histogram/public/services/lens_vis_service.ts +++ b/src/plugins/unified_histogram/public/services/lens_vis_service.ts @@ -235,7 +235,7 @@ export class LensVisService { let currentSuggestion: Suggestion | undefined; // takes lens suggestions if provided - const availableSuggestionsWithType: Array<{ + let availableSuggestionsWithType: Array<{ suggestion: UnifiedHistogramSuggestionContext['suggestion']; type: UnifiedHistogramSuggestionType; }> = []; @@ -254,6 +254,9 @@ export class LensVisService { breakdownField, }); if (histogramSuggestionForESQL) { + // In case if histogram suggestion, we want to empty the array and push the new suggestion + // to ensure that only the histogram suggestion is available + availableSuggestionsWithType = []; availableSuggestionsWithType.push({ suggestion: histogramSuggestionForESQL, type: UnifiedHistogramSuggestionType.histogramForESQL, From a7332ad11611d224a16f2bb3c0d3f207cf746065 Mon Sep 17 00:00:00 2001 From: Ash <1849116+ashokaditya@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:54:22 +0200 Subject: [PATCH 11/38] [DataUsage][Serverless] Data usage metrics page enhancements (#195556) ## Summary This PR is a follow-up of elastic/kibana/pull/193966 and adds: 1. Datastreams filter to data usage metrics page. 2. Metrics filter (hidden for now) that lists out metric types to request. 3. Refactors to make code easier to maintain. 4. Shows a callout if no data stream is selected. ### screen ![Screenshot 2024-10-09 at 17 36 32](https://github.com/user-attachments/assets/a0779c91-25ae-4a64-819e-bc8a626f1f96) ### clip ![latest-metrics-ux](https://github.com/user-attachments/assets/0f4b1a9b-d160-435b-917b-f59c3a5cc9f8) ### 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) - [ ] [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 - [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) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../common/rest_types/data_streams.ts | 4 +- .../common/rest_types/usage_metrics.test.ts | 12 +- .../common/rest_types/usage_metrics.ts | 31 ++- .../app/components/data_usage_metrics.tsx | 150 +++++++++++ .../app/components/filters/charts_filter.tsx | 238 ++++++++++++++++++ .../filters/charts_filter_popover.tsx | 81 ++++++ .../app/components/filters/charts_filters.tsx | 93 +++++++ .../components/filters/clear_all_button.tsx | 43 ++++ .../components/{ => filters}/date_picker.tsx | 46 ++-- .../data_usage/public/app/components/page.tsx | 69 +++++ .../data_usage/public/app/data_usage.tsx | 146 ----------- .../public/app/data_usage_metrics_page.tsx | 25 ++ .../types.ts => public/app/hooks/index.tsx} | 5 +- .../public/app/hooks/use_charts_filter.tsx | 123 +++++++++ .../public/app/hooks/use_date_picker.tsx | 2 +- .../data_usage/public/app/translations.tsx | 54 ++++ .../plugins/data_usage/public/application.tsx | 4 +- .../public/hooks/use_get_data_streams.ts | 84 +++++++ .../public/hooks/use_get_usage_metrics.ts | 7 +- .../public/hooks/use_test_id_generator.ts | 19 ++ .../routes/internal/data_streams_handler.ts | 44 ++-- .../routes/internal/usage_metrics_handler.ts | 24 +- .../data_usage/server/services/autoops_api.ts | 7 +- x-pack/plugins/data_usage/tsconfig.json | 2 +- 24 files changed, 1083 insertions(+), 230 deletions(-) create mode 100644 x-pack/plugins/data_usage/public/app/components/data_usage_metrics.tsx create mode 100644 x-pack/plugins/data_usage/public/app/components/filters/charts_filter.tsx create mode 100644 x-pack/plugins/data_usage/public/app/components/filters/charts_filter_popover.tsx create mode 100644 x-pack/plugins/data_usage/public/app/components/filters/charts_filters.tsx create mode 100644 x-pack/plugins/data_usage/public/app/components/filters/clear_all_button.tsx rename x-pack/plugins/data_usage/public/app/components/{ => filters}/date_picker.tsx (61%) create mode 100644 x-pack/plugins/data_usage/public/app/components/page.tsx delete mode 100644 x-pack/plugins/data_usage/public/app/data_usage.tsx create mode 100644 x-pack/plugins/data_usage/public/app/data_usage_metrics_page.tsx rename x-pack/plugins/data_usage/{common/types.ts => public/app/hooks/index.tsx} (53%) create mode 100644 x-pack/plugins/data_usage/public/app/hooks/use_charts_filter.tsx create mode 100644 x-pack/plugins/data_usage/public/app/translations.tsx create mode 100644 x-pack/plugins/data_usage/public/hooks/use_get_data_streams.ts create mode 100644 x-pack/plugins/data_usage/public/hooks/use_test_id_generator.ts diff --git a/x-pack/plugins/data_usage/common/rest_types/data_streams.ts b/x-pack/plugins/data_usage/common/rest_types/data_streams.ts index b1c02bb40854d1..87af7e29eccb6c 100644 --- a/x-pack/plugins/data_usage/common/rest_types/data_streams.ts +++ b/x-pack/plugins/data_usage/common/rest_types/data_streams.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { schema } from '@kbn/config-schema'; +import { schema, TypeOf } from '@kbn/config-schema'; export const DataStreamsResponseSchema = { body: () => @@ -16,3 +16,5 @@ export const DataStreamsResponseSchema = { }) ), }; + +export type DataStreamsResponseBodySchemaBody = TypeOf; diff --git a/x-pack/plugins/data_usage/common/rest_types/usage_metrics.test.ts b/x-pack/plugins/data_usage/common/rest_types/usage_metrics.test.ts index 473e64c6b03d9f..e4feb438cc801e 100644 --- a/x-pack/plugins/data_usage/common/rest_types/usage_metrics.test.ts +++ b/x-pack/plugins/data_usage/common/rest_types/usage_metrics.test.ts @@ -41,7 +41,7 @@ describe('usage_metrics schemas', () => { ).not.toThrow(); }); - it('should error if `dataStream` list is empty', () => { + it('should not error if `dataStream` list is empty', () => { expect(() => UsageMetricsRequestSchema.validate({ from: new Date().toISOString(), @@ -49,7 +49,7 @@ describe('usage_metrics schemas', () => { metricTypes: ['storage_retained'], dataStreams: [], }) - ).toThrowError('[dataStreams]: array size is [0], but cannot be smaller than [1]'); + ).not.toThrow(); }); it('should error if `dataStream` is given type not array', () => { @@ -71,7 +71,7 @@ describe('usage_metrics schemas', () => { metricTypes: ['storage_retained'], dataStreams: ['ds_1', ' '], }) - ).toThrow('[dataStreams]: [dataStreams] list cannot contain empty values'); + ).toThrow('[dataStreams]: list cannot contain empty values'); }); it('should error if `metricTypes` is empty string', () => { @@ -82,7 +82,7 @@ describe('usage_metrics schemas', () => { dataStreams: ['data_stream_1', 'data_stream_2', 'data_stream_3'], metricTypes: ' ', }) - ).toThrow(); + ).toThrow('[metricTypes]: could not parse array value from json input'); }); it('should error if `metricTypes` contains an empty item', () => { @@ -93,7 +93,7 @@ describe('usage_metrics schemas', () => { dataStreams: ['data_stream_1', 'data_stream_2', 'data_stream_3'], metricTypes: [' ', 'storage_retained'], // First item is invalid }) - ).toThrowError(/list cannot contain empty values/); + ).toThrow('list cannot contain empty values'); }); it('should error if `metricTypes` is not a valid type', () => { @@ -116,7 +116,7 @@ describe('usage_metrics schemas', () => { metricTypes: ['storage_retained', 'foo'], }) ).toThrow( - '[metricTypes] must be one of storage_retained, ingest_rate, search_vcu, ingest_vcu, ml_vcu, index_latency, index_rate, search_latency, search_rate' + '[metricTypes]: must be one of ingest_rate, storage_retained, search_vcu, ingest_vcu, ml_vcu, index_latency, index_rate, search_latency, search_rate' ); }); diff --git a/x-pack/plugins/data_usage/common/rest_types/usage_metrics.ts b/x-pack/plugins/data_usage/common/rest_types/usage_metrics.ts index 3dceeadc198b0e..40194494854fc8 100644 --- a/x-pack/plugins/data_usage/common/rest_types/usage_metrics.ts +++ b/x-pack/plugins/data_usage/common/rest_types/usage_metrics.ts @@ -7,9 +7,11 @@ import { schema, type TypeOf } from '@kbn/config-schema'; -const METRIC_TYPE_VALUES = [ - 'storage_retained', - 'ingest_rate', +// note these should be sorted alphabetically as we sort the URL params on the browser side +// before making the request, else the cache key will be different and that would invoke a new request +export const DEFAULT_METRIC_TYPES = ['ingest_rate', 'storage_retained'] as const; +export const METRIC_TYPE_VALUES = [ + ...DEFAULT_METRIC_TYPES, 'search_vcu', 'ingest_vcu', 'ml_vcu', @@ -21,6 +23,22 @@ const METRIC_TYPE_VALUES = [ export type MetricTypes = (typeof METRIC_TYPE_VALUES)[number]; +export const isDefaultMetricType = (metricType: string) => + // @ts-ignore + DEFAULT_METRIC_TYPES.includes(metricType); + +export const METRIC_TYPE_API_VALUES_TO_UI_OPTIONS_MAP = Object.freeze>({ + storage_retained: 'Data Retained in Storage', + ingest_rate: 'Data Ingested', + search_vcu: 'Search VCU', + ingest_vcu: 'Ingest VCU', + ml_vcu: 'ML VCU', + index_latency: 'Index Latency', + index_rate: 'Index Rate', + search_latency: 'Search Latency', + search_rate: 'Search Rate', +}); + // type guard for MetricTypes export const isMetricType = (type: string): type is MetricTypes => METRIC_TYPE_VALUES.includes(type as MetricTypes); @@ -47,21 +65,20 @@ export const UsageMetricsRequestSchema = schema.object({ if (trimmedValues.some((v) => !v.length)) { return '[metricTypes] list cannot contain empty values'; } else if (trimmedValues.some((v) => !isValidMetricType(v))) { - return `[metricTypes] must be one of ${METRIC_TYPE_VALUES.join(', ')}`; + return `must be one of ${METRIC_TYPE_VALUES.join(', ')}`; } }, }), dataStreams: schema.arrayOf(schema.string(), { - minSize: 1, validate: (values) => { if (values.map((v) => v.trim()).some((v) => !v.length)) { - return '[dataStreams] list cannot contain empty values'; + return 'list cannot contain empty values'; } }, }), }); -export type UsageMetricsRequestSchemaQueryParams = TypeOf; +export type UsageMetricsRequestBody = TypeOf; export const UsageMetricsResponseSchema = { body: () => diff --git a/x-pack/plugins/data_usage/public/app/components/data_usage_metrics.tsx b/x-pack/plugins/data_usage/public/app/components/data_usage_metrics.tsx new file mode 100644 index 00000000000000..cc443c78562ee4 --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/components/data_usage_metrics.tsx @@ -0,0 +1,150 @@ +/* + * 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, useEffect, memo, useState } from 'react'; +import { css } from '@emotion/react'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingElastic, EuiCallOut } from '@elastic/eui'; +import { Charts } from './charts'; +import { useBreadcrumbs } from '../../utils/use_breadcrumbs'; +import { useKibanaContextForPlugin } from '../../utils/use_kibana'; +import { PLUGIN_NAME } from '../../../common'; +import { useGetDataUsageMetrics } from '../../hooks/use_get_usage_metrics'; +import { useDataUsageMetricsUrlParams } from '../hooks/use_charts_url_params'; +import { DEFAULT_DATE_RANGE_OPTIONS, useDateRangePicker } from '../hooks/use_date_picker'; +import { DEFAULT_METRIC_TYPES, UsageMetricsRequestBody } from '../../../common/rest_types'; +import { ChartFilters } from './filters/charts_filters'; +import { UX_LABELS } from '../translations'; + +const EuiItemCss = css` + width: 100%; +`; + +const FlexItemWithCss = memo(({ children }: { children: React.ReactNode }) => ( + {children} +)); + +export const DataUsageMetrics = () => { + const { + services: { chrome, appParams }, + } = useKibanaContextForPlugin(); + + const { + metricTypes: metricTypesFromUrl, + dataStreams: dataStreamsFromUrl, + startDate: startDateFromUrl, + endDate: endDateFromUrl, + setUrlMetricTypesFilter, + setUrlDateRangeFilter, + } = useDataUsageMetricsUrlParams(); + + const [metricsFilters, setMetricsFilters] = useState({ + metricTypes: [...DEFAULT_METRIC_TYPES], + dataStreams: [], + from: DEFAULT_DATE_RANGE_OPTIONS.startDate, + to: DEFAULT_DATE_RANGE_OPTIONS.endDate, + }); + + useEffect(() => { + if (!metricTypesFromUrl) { + setUrlMetricTypesFilter(metricsFilters.metricTypes.join(',')); + } + if (!startDateFromUrl || !endDateFromUrl) { + setUrlDateRangeFilter({ startDate: metricsFilters.from, endDate: metricsFilters.to }); + } + }, [ + endDateFromUrl, + metricTypesFromUrl, + metricsFilters.from, + metricsFilters.metricTypes, + metricsFilters.to, + setUrlDateRangeFilter, + setUrlMetricTypesFilter, + startDateFromUrl, + ]); + + useEffect(() => { + setMetricsFilters((prevState) => ({ + ...prevState, + metricTypes: metricTypesFromUrl?.length ? metricTypesFromUrl : prevState.metricTypes, + dataStreams: dataStreamsFromUrl?.length ? dataStreamsFromUrl : prevState.dataStreams, + })); + }, [metricTypesFromUrl, dataStreamsFromUrl]); + + const { dateRangePickerState, onRefreshChange, onTimeChange } = useDateRangePicker(); + + const { + error, + data, + isFetching, + isFetched, + refetch: refetchDataUsageMetrics, + } = useGetDataUsageMetrics( + { + ...metricsFilters, + from: dateRangePickerState.startDate, + to: dateRangePickerState.endDate, + }, + { + retry: false, + } + ); + + const onRefresh = useCallback(() => { + refetchDataUsageMetrics(); + }, [refetchDataUsageMetrics]); + + const onChangeDataStreamsFilter = useCallback( + (selectedDataStreams: string[]) => { + setMetricsFilters((prevState) => ({ ...prevState, dataStreams: selectedDataStreams })); + }, + [setMetricsFilters] + ); + + const onChangeMetricTypesFilter = useCallback( + (selectedMetricTypes: string[]) => { + setMetricsFilters((prevState) => ({ ...prevState, metricTypes: selectedMetricTypes })); + }, + [setMetricsFilters] + ); + + useBreadcrumbs([{ text: PLUGIN_NAME }], appParams, chrome); + + return ( + + + + + {!isFetching && error?.message && ( + + + + )} + + {isFetched && data?.metrics ? ( + + ) : isFetching ? ( + + ) : null} + + + ); +}; diff --git a/x-pack/plugins/data_usage/public/app/components/filters/charts_filter.tsx b/x-pack/plugins/data_usage/public/app/components/filters/charts_filter.tsx new file mode 100644 index 00000000000000..466bc6debae771 --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/components/filters/charts_filter.tsx @@ -0,0 +1,238 @@ +/* + * 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 { orderBy } from 'lodash/fp'; +import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPopoverTitle, EuiSelectable } from '@elastic/eui'; + +import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; +import { + METRIC_TYPE_API_VALUES_TO_UI_OPTIONS_MAP, + type MetricTypes, +} from '../../../../common/rest_types'; + +import { ClearAllButton } from './clear_all_button'; +import { UX_LABELS } from '../../translations'; +import { ChartsFilterPopover } from './charts_filter_popover'; +import { FilterItems, FilterName, useChartsFilter } from '../../hooks'; + +const getSearchPlaceholder = (filterName: FilterName) => { + if (filterName === 'dataStreams') { + return UX_LABELS.filterSearchPlaceholder('data streams'); + } + return UX_LABELS.filterSearchPlaceholder('metric types'); +}; + +export const ChartsFilter = memo( + ({ + filterName, + onChangeFilterOptions, + 'data-test-subj': dataTestSubj, + }: { + filterName: FilterName; + onChangeFilterOptions?: (selectedOptions: string[]) => void; + 'data-test-subj'?: string; + }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + const isMetricsFilter = filterName === 'metricTypes'; + const isDataStreamsFilter = filterName === 'dataStreams'; + // popover states and handlers + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const onPopoverButtonClick = useCallback(() => { + setIsPopoverOpen(!isPopoverOpen); + }, [setIsPopoverOpen, isPopoverOpen]); + const onClosePopover = useCallback(() => { + setIsPopoverOpen(false); + }, [setIsPopoverOpen]); + + // search string state + const [searchString, setSearchString] = useState(''); + const { + areDataStreamsSelectedOnMount, + isLoading, + items, + setItems, + hasActiveFilters, + numActiveFilters, + numFilters, + setAreDataStreamsSelectedOnMount, + setUrlDataStreamsFilter, + setUrlMetricTypesFilter, + } = useChartsFilter({ + filterName, + searchString, + }); + + // track popover state to pin selected options + const wasPopoverOpen = useRef(isPopoverOpen); + useEffect(() => { + return () => { + wasPopoverOpen.current = isPopoverOpen; + }; + }, [isPopoverOpen, wasPopoverOpen]); + + // compute if selected dataStreams should be pinned + const shouldPinSelectedDataStreams = useCallback( + (isNotChangingOptions: boolean = true) => { + // case 1: when no dataStreams are selected initially + return ( + isNotChangingOptions && + wasPopoverOpen.current && + isPopoverOpen && + filterName === 'dataStreams' + ); + }, + [filterName, isPopoverOpen] + ); + + // augmented options based on the dataStreams filter + const sortedHostsFilterOptions = useMemo(() => { + if (shouldPinSelectedDataStreams() || areDataStreamsSelectedOnMount) { + // pin checked items to the top + return orderBy('checked', 'asc', items); + } + // return options as are for other filters + return items; + }, [areDataStreamsSelectedOnMount, shouldPinSelectedDataStreams, items]); + + const isSearchable = useMemo(() => !isMetricsFilter, [isMetricsFilter]); + + const onOptionsChange = useCallback( + (newOptions: FilterItems) => { + // update filter UI options state + setItems(newOptions.map((option) => option)); + + // compute a selected list of options + const selectedItems = newOptions.reduce((acc, curr) => { + if (curr.checked === 'on' && curr.key) { + acc.push(curr.key); + } + return acc; + }, []); + + // update URL params + if (isMetricsFilter) { + setUrlMetricTypesFilter( + selectedItems + .map((item) => METRIC_TYPE_API_VALUES_TO_UI_OPTIONS_MAP[item as MetricTypes]) + .join() + ); + } else if (isDataStreamsFilter) { + setUrlDataStreamsFilter(selectedItems.join()); + } + // reset shouldPinSelectedDataStreams, setAreDataStreamsSelectedOnMount + shouldPinSelectedDataStreams(false); + setAreDataStreamsSelectedOnMount(false); + + // update overall query state + if (typeof onChangeFilterOptions !== 'undefined') { + onChangeFilterOptions(selectedItems); + } + }, + [ + setItems, + isMetricsFilter, + isDataStreamsFilter, + shouldPinSelectedDataStreams, + setAreDataStreamsSelectedOnMount, + onChangeFilterOptions, + setUrlMetricTypesFilter, + setUrlDataStreamsFilter, + ] + ); + + // clear all selected options + const onClearAll = useCallback(() => { + // update filter UI options state + setItems( + items.map((option) => { + option.checked = undefined; + return option; + }) + ); + + // update URL params based on filter on page + if (isMetricsFilter) { + setUrlMetricTypesFilter(''); + } else if (isDataStreamsFilter) { + setUrlDataStreamsFilter(''); + } + + if (typeof onChangeFilterOptions !== 'undefined') { + onChangeFilterOptions([]); + } + }, [ + setItems, + items, + isMetricsFilter, + isDataStreamsFilter, + onChangeFilterOptions, + setUrlMetricTypesFilter, + setUrlDataStreamsFilter, + ]); + + return ( + + setSearchString(searchValue.trim()), + }} + > + {(list, search) => { + return ( +
+ {isSearchable && ( + + {search} + + )} + {list} + {!isMetricsFilter && ( + + + + + + )} +
+ ); + }} +
+
+ ); + } +); + +ChartsFilter.displayName = 'ChartsFilter'; diff --git a/x-pack/plugins/data_usage/public/app/components/filters/charts_filter_popover.tsx b/x-pack/plugins/data_usage/public/app/components/filters/charts_filter_popover.tsx new file mode 100644 index 00000000000000..2ed96f012c4971 --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/components/filters/charts_filter_popover.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, useMemo } from 'react'; +import { EuiFilterButton, EuiPopover, useGeneratedHtmlId } from '@elastic/eui'; +import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; +import { type FilterName } from '../../hooks/use_charts_filter'; +import { FILTER_NAMES } from '../../translations'; + +export const ChartsFilterPopover = memo( + ({ + children, + closePopover, + filterName, + hasActiveFilters, + isPopoverOpen, + numActiveFilters, + numFilters, + onButtonClick, + 'data-test-subj': dataTestSubj, + }: { + children: React.ReactNode; + closePopover: () => void; + filterName: FilterName; + hasActiveFilters: boolean; + isPopoverOpen: boolean; + numActiveFilters: number; + numFilters: number; + onButtonClick: () => void; + 'data-test-subj'?: string; + }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + const filterGroupPopoverId = useGeneratedHtmlId({ + prefix: 'filterGroupPopover', + }); + + const button = useMemo( + () => ( + + {FILTER_NAMES[filterName]} + + ), + [ + filterName, + getTestId, + hasActiveFilters, + isPopoverOpen, + numActiveFilters, + numFilters, + onButtonClick, + ] + ); + + return ( + + {children} + + ); + } +); + +ChartsFilterPopover.displayName = 'ChartsFilterPopover'; diff --git a/x-pack/plugins/data_usage/public/app/components/filters/charts_filters.tsx b/x-pack/plugins/data_usage/public/app/components/filters/charts_filters.tsx new file mode 100644 index 00000000000000..72608f4a62c753 --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/components/filters/charts_filters.tsx @@ -0,0 +1,93 @@ +/* + * 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, { memo, useCallback, useMemo } from 'react'; +import { EuiFilterGroup, EuiFlexGroup, EuiFlexItem, EuiSuperUpdateButton } from '@elastic/eui'; +import type { + DurationRange, + OnRefreshChangeProps, +} from '@elastic/eui/src/components/date_picker/types'; +import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; +import { useGetDataUsageMetrics } from '../../../hooks/use_get_usage_metrics'; +import { DateRangePickerValues, UsageMetricsDateRangePicker } from './date_picker'; +import { ChartsFilter } from './charts_filter'; + +interface ChartFiltersProps { + dateRangePickerState: DateRangePickerValues; + isDataLoading: boolean; + onChangeDataStreamsFilter: (selectedDataStreams: string[]) => void; + onChangeMetricTypesFilter?: (selectedMetricTypes: string[]) => void; + onRefresh: () => void; + onRefreshChange: (evt: OnRefreshChangeProps) => void; + onTimeChange: ({ start, end }: DurationRange) => void; + onClick: ReturnType['refetch']; + showMetricsTypesFilter?: boolean; + 'data-test-subj'?: string; +} + +export const ChartFilters = memo( + ({ + dateRangePickerState, + isDataLoading, + onClick, + onChangeMetricTypesFilter, + onChangeDataStreamsFilter, + onRefresh, + onRefreshChange, + onTimeChange, + showMetricsTypesFilter = false, + 'data-test-subj': dataTestSubj, + }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + const filters = useMemo(() => { + return ( + <> + {showMetricsTypesFilter && ( + + )} + + + ); + }, [onChangeDataStreamsFilter, onChangeMetricTypesFilter, showMetricsTypesFilter]); + + const onClickRefreshButton = useCallback(() => onClick(), [onClick]); + + return ( + + + {filters} + + + + + + + + + ); + } +); + +ChartFilters.displayName = 'ChartFilters'; diff --git a/x-pack/plugins/data_usage/public/app/components/filters/clear_all_button.tsx b/x-pack/plugins/data_usage/public/app/components/filters/clear_all_button.tsx new file mode 100644 index 00000000000000..afa4c2fe729171 --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/components/filters/clear_all_button.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo } from 'react'; +import { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { UX_LABELS } from '../../translations'; + +const buttonCss = css` + border-top: ${euiThemeVars.euiBorderThin}; + border-radius: 0; +`; +export const ClearAllButton = memo( + ({ + 'data-test-subj': dataTestSubj, + isDisabled, + onClick, + }: { + 'data-test-subj'?: string; + isDisabled: boolean; + onClick: () => void; + }) => { + return ( + + {UX_LABELS.filterClearAll} + + ); + } +); + +ClearAllButton.displayName = 'ClearAllButton'; diff --git a/x-pack/plugins/data_usage/public/app/components/date_picker.tsx b/x-pack/plugins/data_usage/public/app/components/filters/date_picker.tsx similarity index 61% rename from x-pack/plugins/data_usage/public/app/components/date_picker.tsx rename to x-pack/plugins/data_usage/public/app/components/filters/date_picker.tsx index ca29acf8c96a6b..4d9b280d763ceb 100644 --- a/x-pack/plugins/data_usage/public/app/components/date_picker.tsx +++ b/x-pack/plugins/data_usage/public/app/components/filters/date_picker.tsx @@ -6,8 +6,7 @@ */ import React, { memo, useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker, useEuiTheme } from '@elastic/eui'; -import { css } from '@emotion/react'; +import { EuiSuperDatePicker } from '@elastic/eui'; import type { IUnifiedSearchPluginServices } from '@kbn/unified-search-plugin/public'; import type { EuiSuperDatePickerRecentRange } from '@elastic/eui'; import { useKibana } from '@kbn/kibana-react-plugin/public'; @@ -37,7 +36,6 @@ interface UsageMetricsDateRangePickerProps { export const UsageMetricsDateRangePicker = memo( ({ dateRangePickerState, isDataLoading, onRefresh, onRefreshChange, onTimeChange }) => { - const { euiTheme } = useEuiTheme(); const kibana = useKibana(); const { uiSettings } = kibana.services; const [commonlyUsedRanges] = useState(() => { @@ -55,32 +53,22 @@ export const UsageMetricsDateRangePicker = memo - - - - - - + ); } ); diff --git a/x-pack/plugins/data_usage/public/app/components/page.tsx b/x-pack/plugins/data_usage/public/app/components/page.tsx new file mode 100644 index 00000000000000..d7ff20f5e933f7 --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/components/page.tsx @@ -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 type { PropsWithChildren } from 'react'; +import React, { memo, useMemo } from 'react'; +import type { CommonProps } from '@elastic/eui'; +import { + EuiPageHeader, + EuiPageSection, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, +} from '@elastic/eui'; + +interface DataUsagePageProps { + title: React.ReactNode; + subtitle?: React.ReactNode; + actions?: React.ReactNode; + restrictWidth?: boolean | number; + hasBottomBorder?: boolean; + hideHeader?: boolean; +} + +export const DataUsagePage = memo>( + ({ title, subtitle, children, restrictWidth = false, hasBottomBorder = true, ...otherProps }) => { + const header = useMemo(() => { + return ( + + + + {title} + + + + ); + }, [, title]); + + const description = useMemo(() => { + return subtitle ? ( + {subtitle} + ) : undefined; + }, [subtitle]); + + return ( +
+ <> + + + + + {children} + +
+ ); + } +); + +DataUsagePage.displayName = 'DataUsagePage'; diff --git a/x-pack/plugins/data_usage/public/app/data_usage.tsx b/x-pack/plugins/data_usage/public/app/data_usage.tsx deleted file mode 100644 index bea9f2b511a771..00000000000000 --- a/x-pack/plugins/data_usage/public/app/data_usage.tsx +++ /dev/null @@ -1,146 +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 React, { useCallback, useEffect, useState } from 'react'; -import { - EuiTitle, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingElastic, - EuiPageSection, - EuiText, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { i18n } from '@kbn/i18n'; -import { UsageMetricsRequestSchemaQueryParams } from '../../common/rest_types'; -import { Charts } from './components/charts'; -import { UsageMetricsDateRangePicker } from './components/date_picker'; -import { useBreadcrumbs } from '../utils/use_breadcrumbs'; -import { useKibanaContextForPlugin } from '../utils/use_kibana'; -import { PLUGIN_NAME } from '../../common'; -import { useGetDataUsageMetrics } from '../hooks/use_get_usage_metrics'; -import { DEFAULT_DATE_RANGE_OPTIONS, useDateRangePicker } from './hooks/use_date_picker'; -import { useDataUsageMetricsUrlParams } from './hooks/use_charts_url_params'; - -export const DataUsage = () => { - const { - services: { chrome, appParams }, - } = useKibanaContextForPlugin(); - - const { - metricTypes: metricTypesFromUrl, - dataStreams: dataStreamsFromUrl, - startDate: startDateFromUrl, - endDate: endDateFromUrl, - setUrlMetricTypesFilter, - setUrlDateRangeFilter, - } = useDataUsageMetricsUrlParams(); - - const [metricsFilters, setMetricsFilters] = useState({ - metricTypes: ['storage_retained', 'ingest_rate'], - // TODO: Replace with data streams from /data_streams api - dataStreams: [ - '.alerts-ml.anomaly-detection-health.alerts-default', - '.alerts-stack.alerts-default', - ], - from: DEFAULT_DATE_RANGE_OPTIONS.startDate, - to: DEFAULT_DATE_RANGE_OPTIONS.endDate, - }); - - useEffect(() => { - if (!metricTypesFromUrl) { - setUrlMetricTypesFilter(metricsFilters.metricTypes.join(',')); - } - if (!startDateFromUrl || !endDateFromUrl) { - setUrlDateRangeFilter({ startDate: metricsFilters.from, endDate: metricsFilters.to }); - } - }, [ - endDateFromUrl, - metricTypesFromUrl, - metricsFilters.from, - metricsFilters.metricTypes, - metricsFilters.to, - setUrlDateRangeFilter, - setUrlMetricTypesFilter, - startDateFromUrl, - ]); - - useEffect(() => { - setMetricsFilters((prevState) => ({ - ...prevState, - metricTypes: metricTypesFromUrl?.length ? metricTypesFromUrl : prevState.metricTypes, - dataStreams: dataStreamsFromUrl?.length ? dataStreamsFromUrl : prevState.dataStreams, - })); - }, [metricTypesFromUrl, dataStreamsFromUrl]); - - const { dateRangePickerState, onRefreshChange, onTimeChange } = useDateRangePicker(); - - const { - error, - data, - isFetching, - isFetched, - refetch: refetchDataUsageMetrics, - } = useGetDataUsageMetrics( - { - ...metricsFilters, - from: dateRangePickerState.startDate, - to: dateRangePickerState.endDate, - }, - { - retry: false, - } - ); - - const onRefresh = useCallback(() => { - refetchDataUsageMetrics(); - }, [refetchDataUsageMetrics]); - - useBreadcrumbs([{ text: PLUGIN_NAME }], appParams, chrome); - - // TODO: show a toast? - if (!isFetching && error?.body) { - return
{error.body.message}
; - } - - return ( - <> - -

- {i18n.translate('xpack.dataUsage.pageTitle', { - defaultMessage: 'Data Usage', - })} -

-
- - - - - - - - - - - - - - {isFetched && data ? : } - - - ); -}; diff --git a/x-pack/plugins/data_usage/public/app/data_usage_metrics_page.tsx b/x-pack/plugins/data_usage/public/app/data_usage_metrics_page.tsx new file mode 100644 index 00000000000000..69edb7a7f01cee --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/data_usage_metrics_page.tsx @@ -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 React from 'react'; +import { DataUsagePage } from './components/page'; +import { DATA_USAGE_PAGE } from './translations'; +import { DataUsageMetrics } from './components/data_usage_metrics'; + +export const DataUsageMetricsPage = () => { + return ( + + + + ); +}; + +DataUsageMetricsPage.displayName = 'DataUsageMetricsPage'; diff --git a/x-pack/plugins/data_usage/common/types.ts b/x-pack/plugins/data_usage/public/app/hooks/index.tsx similarity index 53% rename from x-pack/plugins/data_usage/common/types.ts rename to x-pack/plugins/data_usage/public/app/hooks/index.tsx index d80bae2458d09f..984f9ebde46f03 100644 --- a/x-pack/plugins/data_usage/common/types.ts +++ b/x-pack/plugins/data_usage/public/app/hooks/index.tsx @@ -5,5 +5,6 @@ * 2.0. */ -// temporary type until agreed on -export type MetricKey = 'ingestedMax' | 'retainedMax'; +export { useChartsFilter, type FilterName, type FilterItems } from './use_charts_filter'; +export { useDataUsageMetricsUrlParams } from './use_charts_url_params'; +export { useDateRangePicker } from './use_date_picker'; diff --git a/x-pack/plugins/data_usage/public/app/hooks/use_charts_filter.tsx b/x-pack/plugins/data_usage/public/app/hooks/use_charts_filter.tsx new file mode 100644 index 00000000000000..330c9a633396dc --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/hooks/use_charts_filter.tsx @@ -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 { useState, useEffect, useMemo } from 'react'; +import { + isDefaultMetricType, + METRIC_TYPE_API_VALUES_TO_UI_OPTIONS_MAP, + METRIC_TYPE_VALUES, +} from '../../../common/rest_types'; +import { useGetDataUsageDataStreams } from '../../hooks/use_get_data_streams'; +import { FILTER_NAMES } from '../translations'; +import { useDataUsageMetricsUrlParams } from './use_charts_url_params'; + +export type FilterName = keyof typeof FILTER_NAMES; + +export type FilterItems = Array<{ + key?: string; + label: string; + isGroupLabel?: boolean; + checked?: 'on' | undefined; + 'data-test-subj'?: string; +}>; + +export const useChartsFilter = ({ + filterName, + searchString, +}: { + filterName: FilterName; + searchString: string; +}): { + areDataStreamsSelectedOnMount: boolean; + isLoading: boolean; + items: FilterItems; + setItems: React.Dispatch>; + hasActiveFilters: boolean; + numActiveFilters: number; + numFilters: number; + setAreDataStreamsSelectedOnMount: (value: React.SetStateAction) => void; + setUrlDataStreamsFilter: ReturnType< + typeof useDataUsageMetricsUrlParams + >['setUrlDataStreamsFilter']; + setUrlMetricTypesFilter: ReturnType< + typeof useDataUsageMetricsUrlParams + >['setUrlMetricTypesFilter']; +} => { + const { + dataStreams: selectedDataStreamsFromUrl, + setUrlMetricTypesFilter, + setUrlDataStreamsFilter, + } = useDataUsageMetricsUrlParams(); + const isMetricTypesFilter = filterName === 'metricTypes'; + const isDataStreamsFilter = filterName === 'dataStreams'; + const { data: dataStreams, isFetching } = useGetDataUsageDataStreams({ + searchString, + selectedDataStreams: selectedDataStreamsFromUrl, + }); + + // track the state of selected data streams via URL + // when the page is loaded via selected data streams on URL + const [areDataStreamsSelectedOnMount, setAreDataStreamsSelectedOnMount] = + useState(false); + + useEffect(() => { + if (selectedDataStreamsFromUrl && selectedDataStreamsFromUrl.length > 0) { + setAreDataStreamsSelectedOnMount(true); + } + // don't sync with changes to further selectedDataStreamsFromUrl + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // filter options + const [items, setItems] = useState( + isMetricTypesFilter + ? METRIC_TYPE_VALUES.map((metricType) => ({ + key: metricType, + label: METRIC_TYPE_API_VALUES_TO_UI_OPTIONS_MAP[metricType], + checked: isDefaultMetricType(metricType) ? 'on' : undefined, // default metrics are selected by default + disabled: isDefaultMetricType(metricType), + 'data-test-subj': `${filterName}-filter-option`, + })) + : [] + ); + + useEffect(() => { + if (isDataStreamsFilter && dataStreams) { + setItems( + dataStreams?.map((dataStream) => ({ + key: dataStream.name, + label: dataStream.name, + checked: dataStream.selected ? 'on' : undefined, + 'data-test-subj': `${filterName}-filter-option`, + })) + ); + } + }, [dataStreams, filterName, isDataStreamsFilter, setItems]); + + const hasActiveFilters = useMemo(() => !!items.find((item) => item.checked === 'on'), [items]); + const numActiveFilters = useMemo( + () => items.filter((item) => item.checked === 'on').length, + [items] + ); + const numFilters = useMemo( + () => items.filter((item) => item.key && item.checked !== 'on').length, + [items] + ); + + return { + areDataStreamsSelectedOnMount, + isLoading: isDataStreamsFilter && isFetching, + items, + setItems, + hasActiveFilters, + numActiveFilters, + numFilters, + setAreDataStreamsSelectedOnMount, + setUrlMetricTypesFilter, + setUrlDataStreamsFilter, + }; +}; diff --git a/x-pack/plugins/data_usage/public/app/hooks/use_date_picker.tsx b/x-pack/plugins/data_usage/public/app/hooks/use_date_picker.tsx index b5407ae9e46d59..cc4bfd2376da1a 100644 --- a/x-pack/plugins/data_usage/public/app/hooks/use_date_picker.tsx +++ b/x-pack/plugins/data_usage/public/app/hooks/use_date_picker.tsx @@ -11,7 +11,7 @@ import type { OnRefreshChangeProps, } from '@elastic/eui/src/components/date_picker/types'; import { useDataUsageMetricsUrlParams } from './use_charts_url_params'; -import { DateRangePickerValues } from '../components/date_picker'; +import { DateRangePickerValues } from '../components/filters/date_picker'; export const DEFAULT_DATE_RANGE_OPTIONS = Object.freeze({ autoRefreshOptions: { diff --git a/x-pack/plugins/data_usage/public/app/translations.tsx b/x-pack/plugins/data_usage/public/app/translations.tsx new file mode 100644 index 00000000000000..687cdcf499b0d3 --- /dev/null +++ b/x-pack/plugins/data_usage/public/app/translations.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 { i18n } from '@kbn/i18n'; + +export const FILTER_NAMES = Object.freeze({ + metricTypes: i18n.translate('xpack.dataUsage.metrics.filter.metricTypes', { + defaultMessage: 'Metric types', + }), + dataStreams: i18n.translate('xpack.dataUsage.metrics.filter.dataStreams', { + defaultMessage: 'Data streams', + }), +}); + +export const CHART_TITLES = Object.freeze({ + ingest_rate: i18n.translate('xpack.dataUsage.charts.ingestedMax', { + defaultMessage: 'Data Ingested', + }), + storage_retained: i18n.translate('xpack.dataUsage.charts.retainedMax', { + defaultMessage: 'Data Retained in Storage', + }), +}); + +export const DATA_USAGE_PAGE = Object.freeze({ + title: i18n.translate('xpack.dataUsage.name', { + defaultMessage: 'Data Usage', + }), + subTitle: i18n.translate('xpack.dataUsage.pageSubtitle', { + defaultMessage: 'Monitor data ingested and retained by data streams.', + }), +}); + +export const UX_LABELS = Object.freeze({ + filterClearAll: i18n.translate('xpack.dataUsage.metrics.filter.clearAll', { + defaultMessage: 'Clear all', + }), + filterSearchPlaceholder: (filterName: string) => + i18n.translate('xpack.dataUsage.metrics.filter.searchPlaceholder', { + defaultMessage: 'Search {filterName}', + values: { filterName }, + }), + filterEmptyMessage: (filterName: string) => + i18n.translate('xpack.dataUsage.metrics.filter.emptyMessage', { + defaultMessage: 'No {filterName} available', + values: { filterName }, + }), + noDataStreamsSelected: i18n.translate('xpack.dataUsage.metrics.noDataStreamsSelected', { + defaultMessage: 'Select one or more data streams to view data usage metrics.', + }), +}); diff --git a/x-pack/plugins/data_usage/public/application.tsx b/x-pack/plugins/data_usage/public/application.tsx index 054aae397e5e15..0e6cdc6192c7cf 100644 --- a/x-pack/plugins/data_usage/public/application.tsx +++ b/x-pack/plugins/data_usage/public/application.tsx @@ -16,7 +16,7 @@ import { PerformanceContextProvider } from '@kbn/ebt-tools'; import { useKibanaContextForPluginProvider } from './utils/use_kibana'; import { DataUsageStartDependencies, DataUsagePublicStart } from './types'; import { PLUGIN_ID } from '../common'; -import { DataUsage } from './app/data_usage'; +import { DataUsageMetricsPage } from './app/data_usage_metrics_page'; import { DataUsageReactQueryClientProvider } from '../common/query_client'; export const renderApp = ( @@ -53,7 +53,7 @@ const AppWithExecutionContext = ({ - + diff --git a/x-pack/plugins/data_usage/public/hooks/use_get_data_streams.ts b/x-pack/plugins/data_usage/public/hooks/use_get_data_streams.ts new file mode 100644 index 00000000000000..59b36e156a8246 --- /dev/null +++ b/x-pack/plugins/data_usage/public/hooks/use_get_data_streams.ts @@ -0,0 +1,84 @@ +/* + * 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 { UseQueryOptions, UseQueryResult } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { DATA_USAGE_DATA_STREAMS_API_ROUTE } from '../../common'; +import { useKibanaContextForPlugin } from '../utils/use_kibana'; + +type GetDataUsageDataStreamsResponse = Array<{ + name: string; + selected: boolean; +}>; + +const PAGING_PARAMS = Object.freeze({ + default: 50, + all: 10000, +}); + +export const useGetDataUsageDataStreams = ({ + searchString, + selectedDataStreams, + options = {}, +}: { + searchString: string; + selectedDataStreams?: string[]; + options?: UseQueryOptions; +}): UseQueryResult => { + const http = useKibanaContextForPlugin().services.http; + + return useQuery({ + queryKey: ['get-data-usage-data-streams'], + ...options, + keepPreviousData: true, + queryFn: async () => { + const dataStreamsResponse = await http.get( + DATA_USAGE_DATA_STREAMS_API_ROUTE, + { + version: '1', + query: {}, + } + ); + + const augmentedDataStreamsBasedOnSelectedItems = dataStreamsResponse.reduce<{ + selected: GetDataUsageDataStreamsResponse; + rest: GetDataUsageDataStreamsResponse; + }>( + (acc, list) => { + const item = { + name: list.name, + }; + + if (selectedDataStreams?.includes(list.name)) { + acc.selected.push({ ...item, selected: true }); + } else { + acc.rest.push({ ...item, selected: false }); + } + + return acc; + }, + { selected: [], rest: [] } + ); + + let selectedDataStreamsCount = 0; + if (selectedDataStreams) { + selectedDataStreamsCount = selectedDataStreams.length; + } + + return [ + ...augmentedDataStreamsBasedOnSelectedItems.selected, + ...augmentedDataStreamsBasedOnSelectedItems.rest, + ].slice( + 0, + selectedDataStreamsCount >= PAGING_PARAMS.default + ? selectedDataStreamsCount + 10 + : PAGING_PARAMS.default + ); + }, + }); +}; diff --git a/x-pack/plugins/data_usage/public/hooks/use_get_usage_metrics.ts b/x-pack/plugins/data_usage/public/hooks/use_get_usage_metrics.ts index 3d648eb183f070..3998c736c839e2 100644 --- a/x-pack/plugins/data_usage/public/hooks/use_get_usage_metrics.ts +++ b/x-pack/plugins/data_usage/public/hooks/use_get_usage_metrics.ts @@ -8,10 +8,7 @@ import type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; import type { IHttpFetchError } from '@kbn/core-http-browser'; -import { - UsageMetricsRequestSchemaQueryParams, - UsageMetricsResponseSchemaBody, -} from '../../common/rest_types'; +import { UsageMetricsRequestBody, UsageMetricsResponseSchemaBody } from '../../common/rest_types'; import { DATA_USAGE_METRICS_API_ROUTE } from '../../common'; import { useKibanaContextForPlugin } from '../utils/use_kibana'; @@ -21,7 +18,7 @@ interface ErrorType { } export const useGetDataUsageMetrics = ( - body: UsageMetricsRequestSchemaQueryParams, + body: UsageMetricsRequestBody, options: UseQueryOptions> = {} ): UseQueryResult> => { const http = useKibanaContextForPlugin().services.http; diff --git a/x-pack/plugins/data_usage/public/hooks/use_test_id_generator.ts b/x-pack/plugins/data_usage/public/hooks/use_test_id_generator.ts new file mode 100644 index 00000000000000..662cb2abd9813f --- /dev/null +++ b/x-pack/plugins/data_usage/public/hooks/use_test_id_generator.ts @@ -0,0 +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. + */ + +import { useCallback } from 'react'; + +export const useTestIdGenerator = (prefix?: string): ((suffix?: string) => string | undefined) => { + return useCallback( + (suffix: string = ''): string | undefined => { + if (prefix) { + return `${prefix}${suffix ? `-${suffix}` : ''}`; + } + }, + [prefix] + ); +}; diff --git a/x-pack/plugins/data_usage/server/routes/internal/data_streams_handler.ts b/x-pack/plugins/data_usage/server/routes/internal/data_streams_handler.ts index 686edd0c4f4b7e..5794d06f16ead7 100644 --- a/x-pack/plugins/data_usage/server/routes/internal/data_streams_handler.ts +++ b/x-pack/plugins/data_usage/server/routes/internal/data_streams_handler.ts @@ -5,36 +5,50 @@ * 2.0. */ -import { RequestHandler } from '@kbn/core/server'; +import { type ElasticsearchClient, RequestHandler } from '@kbn/core/server'; import { DataUsageContext, DataUsageRequestHandlerContext } from '../../types'; - import { errorHandler } from '../error_handler'; +export interface MeteringStats { + name: string; + num_docs: number; + size_in_bytes: number; +} + +interface MeteringStatsResponse { + datastreams: MeteringStats[]; +} + +const getMeteringStats = (client: ElasticsearchClient) => { + return client.transport.request({ + method: 'GET', + path: '/_metering/stats', + }); +}; + export const getDataStreamsHandler = ( dataUsageContext: DataUsageContext ): RequestHandler => { const logger = dataUsageContext.logFactory.get('dataStreamsRoute'); return async (context, _, response) => { - logger.debug(`Retrieving user data streams`); + logger.debug('Retrieving user data streams'); try { const core = await context.core; - const esClient = core.elasticsearch.client.asCurrentUser; - - const { data_streams: dataStreamsResponse } = await esClient.indices.dataStreamsStats({ - name: '*', - expand_wildcards: 'all', - }); + const { datastreams: meteringStats } = await getMeteringStats( + core.elasticsearch.client.asSecondaryAuthUser + ); - const sorted = dataStreamsResponse - .sort((a, b) => b.store_size_bytes - a.store_size_bytes) - .map((dataStream) => ({ - name: dataStream.data_stream, - storageSizeBytes: dataStream.store_size_bytes, + const body = meteringStats + .sort((a, b) => b.size_in_bytes - a.size_in_bytes) + .map((stat) => ({ + name: stat.name, + storageSizeBytes: stat.size_in_bytes, })); + return response.ok({ - body: sorted, + body, }); } catch (error) { return errorHandler(logger, response, error); diff --git a/x-pack/plugins/data_usage/server/routes/internal/usage_metrics_handler.ts b/x-pack/plugins/data_usage/server/routes/internal/usage_metrics_handler.ts index 09e9f88721c63f..2b68dc3d37a647 100644 --- a/x-pack/plugins/data_usage/server/routes/internal/usage_metrics_handler.ts +++ b/x-pack/plugins/data_usage/server/routes/internal/usage_metrics_handler.ts @@ -11,24 +11,20 @@ import { MetricTypes, UsageMetricsAutoOpsResponseSchema, UsageMetricsAutoOpsResponseSchemaBody, - UsageMetricsRequestSchemaQueryParams, + UsageMetricsRequestBody, UsageMetricsResponseSchemaBody, } from '../../../common/rest_types'; import { DataUsageContext, DataUsageRequestHandlerContext } from '../../types'; import { errorHandler } from '../error_handler'; +import { CustomHttpRequestError } from '../../utils'; const formatStringParams = (value: T | T[]): T[] | MetricTypes[] => typeof value === 'string' ? [value] : value; export const getUsageMetricsHandler = ( dataUsageContext: DataUsageContext -): RequestHandler< - never, - UsageMetricsRequestSchemaQueryParams, - unknown, - DataUsageRequestHandlerContext -> => { +): RequestHandler => { const logger = dataUsageContext.logFactory.get('usageMetricsRoute'); return async (context, request, response) => { @@ -36,8 +32,16 @@ export const getUsageMetricsHandler = ( const core = await context.core; const esClient = core.elasticsearch.client.asCurrentUser; - const { from, to, metricTypes, dataStreams: requestDsNames } = request.query; logger.debug(`Retrieving usage metrics`); + const { from, to, metricTypes, dataStreams: requestDsNames } = request.body; + + if (!requestDsNames?.length) { + return errorHandler( + logger, + response, + new CustomHttpRequestError('[request body.dataStreams]: no data streams selected', 400) + ); + } const { data_streams: dataStreamsResponse }: IndicesGetDataStreamResponse = await esClient.indices.getDataStream({ @@ -52,10 +56,10 @@ export const getUsageMetricsHandler = ( dataStreams: formatStringParams(dataStreamsResponse.map((ds) => ds.name)), }); - const processedMetrics = transformMetricsData(metrics); + const body = transformMetricsData(metrics); return response.ok({ - body: processedMetrics, + body, }); } catch (error) { logger.error(`Error retrieving usage metrics: ${error.message}`); diff --git a/x-pack/plugins/data_usage/server/services/autoops_api.ts b/x-pack/plugins/data_usage/server/services/autoops_api.ts index 5f3a636301f874..a61815a3679492 100644 --- a/x-pack/plugins/data_usage/server/services/autoops_api.ts +++ b/x-pack/plugins/data_usage/server/services/autoops_api.ts @@ -12,15 +12,12 @@ import apm from 'elastic-apm-node'; import type { AxiosError, AxiosRequestConfig } from 'axios'; import axios from 'axios'; import { LogMeta } from '@kbn/core/server'; -import { - UsageMetricsRequestSchemaQueryParams, - UsageMetricsResponseSchemaBody, -} from '../../common/rest_types'; +import { UsageMetricsResponseSchemaBody } from '../../common/rest_types'; import { appContextService } from '../app_context'; import { AutoOpsConfig } from '../types'; class AutoOpsAPIService { - public async autoOpsUsageMetricsAPI(requestBody: UsageMetricsRequestSchemaQueryParams) { + public async autoOpsUsageMetricsAPI(requestBody: UsageMetricsResponseSchemaBody) { const logger = appContextService.getLogger().get(); const traceId = apm.currentTransaction?.traceparent; const withRequestIdMessage = (message: string) => `${message} [Request Id: ${traceId}]`; diff --git a/x-pack/plugins/data_usage/tsconfig.json b/x-pack/plugins/data_usage/tsconfig.json index 7ca0276c1f4970..6d3818b88b9fe0 100644 --- a/x-pack/plugins/data_usage/tsconfig.json +++ b/x-pack/plugins/data_usage/tsconfig.json @@ -24,11 +24,11 @@ "@kbn/logging", "@kbn/deeplinks-observability", "@kbn/unified-search-plugin", - "@kbn/i18n-react", "@kbn/core-http-browser", "@kbn/core-chrome-browser", "@kbn/features-plugin", "@kbn/index-management-shared-types", + "@kbn/ui-theme", "@kbn/repo-info", "@kbn/cloud-plugin", "@kbn/server-http-tools", From 4428c80189ff2899e86c9d1705d50932814cb29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:05:16 +0100 Subject: [PATCH 12/38] [Inventory] Adding feedback button (#195716) Screenshot 2024-10-10 at 10 04 51 Screenshot 2024-10-10 at 10 05 24 --------- Co-authored-by: jennypavlova Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../.storybook/get_mock_inventory_context.tsx | 5 +++++ .../inventory/kibana.jsonc | 2 +- .../inventory/public/application.tsx | 20 +++++++------------ .../public/components/app_root/index.tsx | 4 ++++ .../inventory_page_template/index.tsx | 18 +++++++++++++++-- .../inventory/public/hooks/use_kibana.tsx | 10 +++++++++- .../inventory/public/plugin.ts | 14 ++++++++++++- .../inventory/public/types.ts | 2 ++ .../inventory/tsconfig.json | 3 ++- 9 files changed, 59 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx b/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx index d90ce08aab1c6b..52ec669a9a75c6 100644 --- a/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx +++ b/x-pack/plugins/observability_solution/inventory/.storybook/get_mock_inventory_context.tsx @@ -35,5 +35,10 @@ export function getMockInventoryContext(): InventoryKibanaContext { stream: jest.fn(), }, spaces: {} as unknown as SpacesPluginStart, + kibanaEnvironment: { + isCloudEnv: false, + isServerlessEnv: false, + kibanaVersion: '9.0.0', + }, }; } diff --git a/x-pack/plugins/observability_solution/inventory/kibana.jsonc b/x-pack/plugins/observability_solution/inventory/kibana.jsonc index 28556c7bcc5831..1467d294a4f499 100644 --- a/x-pack/plugins/observability_solution/inventory/kibana.jsonc +++ b/x-pack/plugins/observability_solution/inventory/kibana.jsonc @@ -19,7 +19,7 @@ "share" ], "requiredBundles": ["kibanaReact"], - "optionalPlugins": ["spaces"], + "optionalPlugins": ["spaces", "cloud"], "extraPublicDirs": [] } } diff --git a/x-pack/plugins/observability_solution/inventory/public/application.tsx b/x-pack/plugins/observability_solution/inventory/public/application.tsx index 7b611d1d04c223..53616a27de9e75 100644 --- a/x-pack/plugins/observability_solution/inventory/public/application.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/application.tsx @@ -11,27 +11,21 @@ import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import type { InventoryStartDependencies } from './types'; import { InventoryServices } from './services/types'; import { AppRoot } from './components/app_root'; +import { KibanaEnvironment } from './hooks/use_kibana'; -export const renderApp = ({ - coreStart, - pluginsStart, - services, - appMountParameters, -}: { +export const renderApp = (props: { coreStart: CoreStart; pluginsStart: InventoryStartDependencies; services: InventoryServices; -} & { appMountParameters: AppMountParameters }) => { + appMountParameters: AppMountParameters; + kibanaEnvironment: KibanaEnvironment; +}) => { + const { appMountParameters, coreStart } = props; const { element } = appMountParameters; ReactDOM.render( - + , element ); diff --git a/x-pack/plugins/observability_solution/inventory/public/components/app_root/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/app_root/index.tsx index d46e2b76012d9a..6bec4335c7193f 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/app_root/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/app_root/index.tsx @@ -17,16 +17,19 @@ import { inventoryRouter } from '../../routes/config'; import { InventoryServices } from '../../services/types'; import { InventoryStartDependencies } from '../../types'; import { HeaderActionMenuItems } from './header_action_menu'; +import { KibanaEnvironment } from '../../hooks/use_kibana'; export function AppRoot({ coreStart, pluginsStart, services, appMountParameters, + kibanaEnvironment, }: { coreStart: CoreStart; pluginsStart: InventoryStartDependencies; services: InventoryServices; + kibanaEnvironment: KibanaEnvironment; } & { appMountParameters: AppMountParameters }) { const { history } = appMountParameters; @@ -34,6 +37,7 @@ export function AppRoot({ ...coreStart, ...pluginsStart, ...services, + kibanaEnvironment, }; return ( diff --git a/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/index.tsx index 29a6ac31348b49..08ff287b58cfdd 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/index.tsx @@ -7,7 +7,10 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiEmptyPrompt, EuiLoadingLogo } from '@elastic/eui'; -import { TechnicalPreviewBadge } from '@kbn/observability-shared-plugin/public'; +import { + FeatureFeedbackButton, + TechnicalPreviewBadge, +} from '@kbn/observability-shared-plugin/public'; import { useKibana } from '../../hooks/use_kibana'; import { SearchBar } from '../search_bar'; import { getEntityManagerEnablement } from './no_data_config'; @@ -29,9 +32,11 @@ const pageTitle = ( ); +const INVENTORY_FEEDBACK_LINK = 'https://ela.st/feedback-new-inventory'; + export function InventoryPageTemplate({ children }: { children: React.ReactNode }) { const { - services: { observabilityShared, inventoryAPIClient }, + services: { observabilityShared, inventoryAPIClient, kibanaEnvironment }, } = useKibana(); const { PageTemplate: ObservabilityPageTemplate } = observabilityShared.navigation; @@ -73,6 +78,15 @@ export function InventoryPageTemplate({ children }: { children: React.ReactNode , + ], }} noDataConfig={getEntityManagerEnablement({ enabled: isEntityManagerEnabled, diff --git a/x-pack/plugins/observability_solution/inventory/public/hooks/use_kibana.tsx b/x-pack/plugins/observability_solution/inventory/public/hooks/use_kibana.tsx index 0baf2acbc32b83..e70f0aa7326c69 100644 --- a/x-pack/plugins/observability_solution/inventory/public/hooks/use_kibana.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/hooks/use_kibana.tsx @@ -10,7 +10,15 @@ import { type KibanaReactContextValue, useKibana } from '@kbn/kibana-react-plugi import type { InventoryStartDependencies } from '../types'; import type { InventoryServices } from '../services/types'; -export type InventoryKibanaContext = CoreStart & InventoryStartDependencies & InventoryServices; +export interface KibanaEnvironment { + kibanaVersion?: string; + isCloudEnv?: boolean; + isServerlessEnv?: boolean; +} + +export type InventoryKibanaContext = CoreStart & + InventoryStartDependencies & + InventoryServices & { kibanaEnvironment: KibanaEnvironment }; const useTypedKibana = useKibana as () => KibanaReactContextValue; diff --git a/x-pack/plugins/observability_solution/inventory/public/plugin.ts b/x-pack/plugins/observability_solution/inventory/public/plugin.ts index 30e3a1eed3681e..4567e8f34a94a4 100644 --- a/x-pack/plugins/observability_solution/inventory/public/plugin.ts +++ b/x-pack/plugins/observability_solution/inventory/public/plugin.ts @@ -7,12 +7,12 @@ import { AppMountParameters, + AppStatus, CoreSetup, CoreStart, DEFAULT_APP_CATEGORIES, Plugin, PluginInitializerContext, - AppStatus, } from '@kbn/core/public'; import { INVENTORY_APP_ID } from '@kbn/deeplinks-observability/constants'; import { i18n } from '@kbn/i18n'; @@ -40,10 +40,14 @@ export class InventoryPlugin { logger: Logger; telemetry: TelemetryService; + kibanaVersion: string; + isServerlessEnv: boolean; constructor(context: PluginInitializerContext) { this.logger = context.logger.get(); this.telemetry = new TelemetryService(); + this.kibanaVersion = context.env.packageInfo.version; + this.isServerlessEnv = context.env.packageInfo.buildFlavor === 'serverless'; } setup( coreSetup: CoreSetup, @@ -104,6 +108,9 @@ export class InventoryPlugin this.telemetry.setup({ analytics: coreSetup.analytics }); const telemetry = this.telemetry.start(); + const isCloudEnv = !!pluginsSetup.cloud?.isCloudEnabled; + const isServerlessEnv = pluginsSetup.cloud?.isServerlessEnabled || this.isServerlessEnv; + coreSetup.application.register({ id: INVENTORY_APP_ID, title: i18n.translate('xpack.inventory.appTitle', { @@ -134,6 +141,11 @@ export class InventoryPlugin pluginsStart, services, appMountParameters, + kibanaEnvironment: { + isCloudEnv, + isServerlessEnv, + kibanaVersion: this.kibanaVersion, + }, }); }, }); diff --git a/x-pack/plugins/observability_solution/inventory/public/types.ts b/x-pack/plugins/observability_solution/inventory/public/types.ts index 48fe7e7eed1c7a..cb4d7719e3199c 100644 --- a/x-pack/plugins/observability_solution/inventory/public/types.ts +++ b/x-pack/plugins/observability_solution/inventory/public/types.ts @@ -17,6 +17,7 @@ import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/publi import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { CloudSetup } from '@kbn/cloud-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; /* eslint-disable @typescript-eslint/no-empty-interface*/ @@ -29,6 +30,7 @@ export interface InventorySetupDependencies { share: SharePluginSetup; data: DataPublicPluginSetup; entityManager: EntityManagerPublicPluginSetup; + cloud?: CloudSetup; } export interface InventoryStartDependencies { diff --git a/x-pack/plugins/observability_solution/inventory/tsconfig.json b/x-pack/plugins/observability_solution/inventory/tsconfig.json index 20b5e2e37232a7..6492cd51d067ae 100644 --- a/x-pack/plugins/observability_solution/inventory/tsconfig.json +++ b/x-pack/plugins/observability_solution/inventory/tsconfig.json @@ -46,6 +46,7 @@ "@kbn/elastic-agent-utils", "@kbn/custom-icons", "@kbn/ui-theme", - "@kbn/spaces-plugin" + "@kbn/spaces-plugin", + "@kbn/cloud-plugin" ] } From f90dc39f7e8547467c53ffc7f74ada07a06be040 Mon Sep 17 00:00:00 2001 From: Sergi Massaneda Date: Mon, 14 Oct 2024 12:10:46 +0200 Subject: [PATCH 13/38] [SecuritySolution] Set correct onboarding cards capabilities (#195990) ## Summary Sets the correct capabilities for Onboarding cards: - Integrations: - capability: `fleet.read`: The only privilege a user needs to access the Integrations page, it won't be able to install anything though. (`fleet` is the id for "Integrations" capability, the one for "Fleet" is `fleetv2`). - Dashboards: - capability: `dashboard.show` - AI Assisant: - capability: `securitySolutionAssistant.ai-assistant`, - license: `enterprise` - Attack Discovery (still hidden): - capability: `securitySolutionAttackDiscovery.attack-discovery`, - license: `enterprise` --------- Co-authored-by: Elastic Machine Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Angela Chuang Co-authored-by: Agustina Nahir Ruidiaz --- .../components/onboarding_body/cards/assistant/index.ts | 2 ++ .../components/onboarding_body/cards/attack_discovery/index.ts | 2 ++ .../components/onboarding_body/cards/integrations/index.ts | 3 +-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts index 4110575ecc7124..bf4195b8145905 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/assistant/index.ts @@ -25,4 +25,6 @@ export const assistantCardConfig: OnboardingCardConfig = ) ), checkComplete: checkAssistantCardComplete, + capabilities: 'securitySolutionAssistant.ai-assistant', + licenseType: 'enterprise', }; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/attack_discovery/index.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/attack_discovery/index.ts index 827fbf25cf8ce6..3e174caa271577 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/attack_discovery/index.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/attack_discovery/index.ts @@ -22,4 +22,6 @@ export const attackDiscoveryCardConfig: OnboardingCardConfig = { './attack_discovery_card' ) ), + capabilities: 'securitySolutionAttackDiscovery.attack-discovery', + licenseType: 'enterprise', }; diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/index.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/index.ts index 10812c565801b1..07e80ab64f5221 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/index.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/index.ts @@ -27,6 +27,5 @@ export const integrationsCardConfig: OnboardingCardConfig Date: Mon, 14 Oct 2024 12:14:37 +0200 Subject: [PATCH 14/38] [ML] Remove legacy scss overwrites Single Metric Viewer (#195259) ## Summary Removes SCSS files for the Single Metric Viewer and adds BEM classes for `annotations`. Affects the Single Metric Viewer in ML and the embeddable. Part of [#140695](https://github.com/elastic/kibana/issues/140695) --- .../plugins/ml/public/application/_index.scss | 1 - .../explorer/annotation_timeline.tsx | 2 +- .../swimlane_annotation_container.tsx | 7 +- .../plugins/ml/public/application/styles.ts | 17 + .../timeseriesexplorer/_index.scss | 2 - .../_timeseriesexplorer.scss | 267 -------------- .../_timeseriesexplorer_annotations.scss | 103 ------ .../timeseries_chart/timeseries_chart.js | 20 +- .../timeseries_chart_annotations.ts | 44 +-- .../application/timeseriesexplorer/styles.ts | 332 ++++++++++++++++++ .../timeseriesexplorer_page.tsx | 7 +- .../single_metric_viewer/_index.scss | 6 - .../single_metric_viewer.tsx | 10 +- .../services/ml/job_annotations_table.ts | 2 +- 14 files changed, 403 insertions(+), 417 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/styles.ts delete mode 100644 x-pack/plugins/ml/public/application/timeseriesexplorer/_index.scss delete mode 100644 x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss delete mode 100644 x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss create mode 100644 x-pack/plugins/ml/public/application/timeseriesexplorer/styles.ts delete mode 100644 x-pack/plugins/ml/public/shared_components/single_metric_viewer/_index.scss diff --git a/x-pack/plugins/ml/public/application/_index.scss b/x-pack/plugins/ml/public/application/_index.scss index ac9e16e5f3e78c..3025b8f7d921bb 100644 --- a/x-pack/plugins/ml/public/application/_index.scss +++ b/x-pack/plugins/ml/public/application/_index.scss @@ -7,7 +7,6 @@ // Sub applications @import 'data_frame_analytics/index'; @import 'explorer/index'; // SASSTODO: This file needs to be rewritten - @import 'timeseriesexplorer/index'; // Components @import 'components/annotations/annotation_description_list/index'; // SASSTODO: This file overwrites EUI directly diff --git a/x-pack/plugins/ml/public/application/explorer/annotation_timeline.tsx b/x-pack/plugins/ml/public/application/explorer/annotation_timeline.tsx index 2dd0376a739c62..3d12c55e05e16f 100644 --- a/x-pack/plugins/ml/public/application/explorer/annotation_timeline.tsx +++ b/x-pack/plugins/ml/public/application/explorer/annotation_timeline.tsx @@ -139,7 +139,7 @@ export const AnnotationTimeline = = endingXPos ? endingXPos - annotationWidth : xPos) .attr('y', 0) diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_annotation_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_annotation_container.tsx index ea4599c47d4ffe..ce506e03dae315 100644 --- a/x-pack/plugins/ml/public/application/explorer/swimlane_annotation_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/swimlane_annotation_container.tsx @@ -15,6 +15,7 @@ import { useCurrentThemeVars } from '../contexts/kibana'; import type { Annotation, AnnotationsTable } from '../../../common/types/annotations'; import type { ChartTooltipService } from '../components/chart_tooltip'; import { Y_AXIS_LABEL_PADDING, Y_AXIS_LABEL_WIDTH } from './constants'; +import { getAnnotationStyles } from '../timeseriesexplorer/styles'; const ANNOTATION_CONTAINER_HEIGHT = 12; const ANNOTATION_MIN_WIDTH = 8; @@ -29,6 +30,8 @@ interface SwimlaneAnnotationContainerProps { tooltipService: ChartTooltipService; } +const annotationStyles = getAnnotationStyles(); + export const SwimlaneAnnotationContainer: FC = ({ chartWidth, domain, @@ -135,7 +138,7 @@ export const SwimlaneAnnotationContainer: FC = const xPos = d.start >= domain.min ? (xScale(d.start) as number) : startingXPos; svg .append('rect') - .classed('mlAnnotationRect', true) + .classed('ml-annotation__rect', true) // If annotation is at the end, prevent overflow by shifting it back .attr('x', xPos + annotationWidth >= endingXPos ? endingXPos - annotationWidth : xPos) .attr('y', 0) @@ -221,5 +224,5 @@ export const SwimlaneAnnotationContainer: FC = // eslint-disable-next-line react-hooks/exhaustive-deps }, [chartWidth, domain, annotationsData, tooltipService]); - return
@@ -303,13 +302,12 @@ exports[`extend index management ilm summary extension should render a step info - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -470,13 +468,12 @@ exports[`extend index management ilm summary extension should render an error pa - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -638,13 +635,12 @@ exports[`extend index management ilm summary extension should render the tab whe - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) diff --git a/x-pack/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap index 9456222eca7aaa..4cc6917e1e87e8 100644 --- a/x-pack/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/request_trial_extension.test.js.snap @@ -40,13 +40,12 @@ exports[`RequestTrialExtension component should display when enterprise license - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) , request an extension now. @@ -116,13 +115,12 @@ exports[`RequestTrialExtension component should display when license is active a - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) , request an extension now. @@ -192,13 +190,12 @@ exports[`RequestTrialExtension component should display when license is not acti - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) , request an extension now. @@ -268,13 +265,12 @@ exports[`RequestTrialExtension component should display when platinum license is - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) , request an extension now. diff --git a/x-pack/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap index f1e5a50a309c8a..9ea24982c655cd 100644 --- a/x-pack/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/revert_to_basic.test.js.snap @@ -40,13 +40,12 @@ exports[`RevertToBasic component should display when license is about to expire - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) . @@ -114,13 +113,12 @@ exports[`RevertToBasic component should display when license is expired 1`] = ` - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) . @@ -188,13 +186,12 @@ exports[`RevertToBasic component should display when trial is active 1`] = ` - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) . diff --git a/x-pack/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap b/x-pack/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap index 64221ba1d1d5c7..5dee5efbe89f0c 100644 --- a/x-pack/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap +++ b/x-pack/plugins/license_management/__jest__/__snapshots__/start_trial.test.js.snap @@ -40,13 +40,12 @@ exports[`StartTrial component when trial is allowed display for basic license 1` - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) have to offer. @@ -114,13 +113,12 @@ exports[`StartTrial component when trial is allowed should display for expired e - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) have to offer. @@ -188,13 +186,12 @@ exports[`StartTrial component when trial is allowed should display for expired p - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) have to offer. @@ -262,13 +259,12 @@ exports[`StartTrial component when trial is allowed should display for gold lice - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) have to offer. diff --git a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap index 10b8e5f0733d97..58c596b063bad3 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__snapshots__/exporters.test.js.snap @@ -89,13 +89,12 @@ Array [ - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) Go to @@ -109,13 +108,12 @@ Array [ - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) section for a deployment to configure monitoring. For more information visit @@ -129,13 +127,12 @@ Array [ - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window)

diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap index 749620c85bea50..bc30f206fe4797 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/reason_found.test.js.snap @@ -156,13 +156,12 @@ Array [ - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) Go to @@ -176,13 +175,12 @@ Array [ - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) section for a deployment to configure monitoring. For more information visit @@ -196,13 +194,12 @@ Array [ - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window)

diff --git a/x-pack/plugins/observability_solution/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/link_preview.test.tsx b/x-pack/plugins/observability_solution/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/link_preview.test.tsx index 560620f27a2f8e..29307a0d32e584 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/link_preview.test.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/app/settings/custom_link/create_edit_custom_link_flyout/link_preview.test.tsx @@ -12,8 +12,7 @@ import * as stories from './link_preview.stories'; const { Example } = composeStories(stories); -export const removeExternalLinkText = (str: string) => - str.replace(/\(opens in a new tab or window\)/g, ''); +export const removeExternalLinkText = (str: string) => str.replace(/\(external[^)]*\)/g, ''); describe('LinkPreview', () => { const getElementValue = (container: HTMLElement, id: string) => 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 42c9894da9a8bd..d2e495e0cc17a0 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 @@ -43,13 +43,13 @@ journey('AlertingDefaults', async ({ page, params }) => { await page.press('input[type="text"]', 'Tab'); }); step( - 'Fill text=Webhook URLCreate a Slack Webhook URL(opens in a new tab or window) >> input[type="text"]', + 'Fill text=Webhook URLCreate a Slack Webhook URL(external, opens in a new tab or window) >> input[type="text"]', async () => { if (await page.isVisible(byTestId('webhookButton'))) { await page.click(byTestId('webhookButton')); } await page.fill( - 'text=Webhook URLCreate a Slack Webhook URL(opens in a new tab or window) >> input[type="text"]', + 'text=Webhook URLCreate a Slack Webhook URL(external, opens in a new tab or window) >> input[type="text"]', 'https://www.slack.com' ); await page.click('button:has-text("Save")'); @@ -74,10 +74,10 @@ journey('AlertingDefaults', async ({ page, params }) => { await page.fill('input[type="password"]', 'changeme'); await page.click('button:has-text("Save")'); await page.click( - 'text=Sender is required.Configure email accounts(opens in a new tab or window) >> input[type="text"]' + 'text=Sender is required.Configure email accounts(external, opens in a new tab or window) >> input[type="text"]' ); await page.fill( - 'text=Sender is required.Configure email accounts(opens in a new tab or window) >> input[type="text"]', + 'text=Sender is required.Configure email accounts(external, opens in a new tab or window) >> input[type="text"]', 'test@gmail.com' ); await page.click('button:has-text("Save")'); 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 7f7a395b09b367..1dd2fe3a44aedb 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 @@ -36,7 +36,7 @@ journey(`DataRetentionPage`, async ({ page, params }) => { await page.click(':nth-match(:text("365 days + rollover"), 2)'); await page.click(':nth-match(:text("365 days + rollover"), 3)'); await page.click(':nth-match(:text("365 days + rollover"), 4)'); - await page.click('tbody div:has-text("synthetics(opens in a new tab or window)")'); + await page.click('tbody div:has-text("synthetics(external, opens in a new tab or window)")'); }); step('validate data sizes', async () => { @@ -60,7 +60,7 @@ journey(`DataRetentionPage`, async ({ page, params }) => { [page1] = await Promise.all([ page.waitForEvent('popup'), page.click( - 'tbody div:has-text("synthetics-synthetics.browser-default_policy(opens in a new tab or window)")' + 'tbody div:has-text("synthetics-synthetics.browser-default_policy(external, opens in a new tab or window)")' ), ]); recordVideo(page1, 'data_retention_policy_change'); @@ -98,7 +98,7 @@ journey(`DataRetentionPage`, async ({ page, params }) => { await page.reload(); - await page.click('tbody div:has-text("synthetics(opens in a new tab or window)")'); + await page.click('tbody div:has-text("synthetics(external, opens in a new tab or window)")'); await page1.close(); await assertText({ page, text: '10000 days + rollover' }); diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ping_list/__snapshots__/expanded_row.test.tsx.snap b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ping_list/__snapshots__/expanded_row.test.tsx.snap index b4b7a53c69909b..925167cc8db39b 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ping_list/__snapshots__/expanded_row.test.tsx.snap +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ping_list/__snapshots__/expanded_row.test.tsx.snap @@ -180,13 +180,12 @@ exports[`PingListExpandedRow renders link to docs if body is not recorded but it - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) for more information on recording response bodies. diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ping_list/doc_link_body.test.tsx b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ping_list/doc_link_body.test.tsx index a3698904eb366c..69069b1bd74dac 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ping_list/doc_link_body.test.tsx +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/ping_list/doc_link_body.test.tsx @@ -16,7 +16,7 @@ describe('PingListExpandedRow', () => { expect(screen.getByText(/Body not recorded. Read our/)); expect( - screen.getByRole('link', { name: 'docs External link (opens in a new tab or window)' }) + screen.getByRole('link', { name: 'docs (external, opens in a new tab or window)' }) ).toBeInTheDocument(); expect(screen.getByText(/for more information on recording response bodies./)); }); diff --git a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap index 380070d4d173e6..d8acb8a3cd7152 100644 --- a/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap +++ b/x-pack/plugins/observability_solution/uptime/public/legacy_uptime/components/monitor/status_details/__snapshots__/monitor_status.bar.test.tsx.snap @@ -67,13 +67,12 @@ Array [ - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) diff --git a/x-pack/plugins/security/server/authentication/__snapshots__/unauthenticated_page.test.tsx.snap b/x-pack/plugins/security/server/authentication/__snapshots__/unauthenticated_page.test.tsx.snap index 2466a01112102b..80a7e7a24e1e91 100644 --- a/x-pack/plugins/security/server/authentication/__snapshots__/unauthenticated_page.test.tsx.snap +++ b/x-pack/plugins/security/server/authentication/__snapshots__/unauthenticated_page.test.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UnauthenticatedPage renders as expected 1`] = `"ElasticMockedFonts

We hit an authentication error

Try logging in again, and if the problem persists, contact your system administrator.

"`; +exports[`UnauthenticatedPage renders as expected 1`] = `"ElasticMockedFonts

We hit an authentication error

Try logging in again, and if the problem persists, contact your system administrator.

"`; -exports[`UnauthenticatedPage renders as expected with custom title 1`] = `"My Company NameMockedFonts

We hit an authentication error

Try logging in again, and if the problem persists, contact your system administrator.

"`; +exports[`UnauthenticatedPage renders as expected with custom title 1`] = `"My Company NameMockedFonts

We hit an authentication error

Try logging in again, and if the problem persists, contact your system administrator.

"`; diff --git a/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap b/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap index f851cb9139ebd0..e7a902015afa7c 100644 --- a/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap +++ b/x-pack/plugins/security/server/authorization/__snapshots__/reset_session_page.test.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ResetSessionPage renders as expected 1`] = `"ElasticMockedFonts

You do not have permission to access the requested page

Either go back to the previous page or log in as a different user.

"`; +exports[`ResetSessionPage renders as expected 1`] = `"ElasticMockedFonts

You do not have permission to access the requested page

Either go back to the previous page or log in as a different user.

"`; -exports[`ResetSessionPage renders as expected with custom page title 1`] = `"My Company NameMockedFonts

You do not have permission to access the requested page

Either go back to the previous page or log in as a different user.

"`; +exports[`ResetSessionPage renders as expected with custom page title 1`] = `"My Company NameMockedFonts

You do not have permission to access the requested page

Either go back to the previous page or log in as a different user.

"`; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.test.tsx index fc0e140a73d9ff..8a71b115071a89 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/investigation_guide.test.tsx @@ -17,7 +17,7 @@ import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guid jest.mock('../../shared/hooks/use_investigation_guide'); const NO_DATA_TEXT = - "There's no investigation guide for this rule. Edit the rule's settingsExternal link(opens in a new tab or window) to add one."; + "There's no investigation guide for this rule. Edit the rule's settings(external, opens in a new tab or window) to add one."; const PREVIEW_MESSAGE = 'Investigation guide is not available in alert preview.'; const renderInvestigationGuide = (context: DocumentDetailsContext = mockContextValue) => ( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/response_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/response_details.test.tsx index 9b11ccbb516ba3..4d7de6e1091655 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/response_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/response_details.test.tsx @@ -59,7 +59,7 @@ jest.mock('../../../../common/lib/kibana', () => { }); const NO_DATA_MESSAGE = - "There are no response actions defined for this event. To add some, edit the rule's settings and set up response actionsExternal link(opens in a new tab or window)."; + "There are no response actions defined for this event. To add some, edit the rule's settings and set up response actions(external, opens in a new tab or window)."; const PREVIEW_MESSAGE = 'Response is not available in alert preview.'; const defaultContextValue = { diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/session_view.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/session_view.test.tsx index 29d12721c3ef7b..6db3c4fb4a90da 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/session_view.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/session_view.test.tsx @@ -31,7 +31,7 @@ jest.mock('../../../../common/hooks/use_license'); jest.mock('../../../../sourcerer/containers'); const NO_DATA_MESSAGE = - 'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View dataExternal link(opens in a new tab or window) for more information.'; + 'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View data(external, opens in a new tab or window) for more information.'; const UPSELL_TEXT = 'This feature requires an Enterprise subscription'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx index 9c743f2b1bc9d9..f9179cecc6b5ab 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx @@ -68,7 +68,7 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { }); const NO_ANALYZER_MESSAGE = - 'You can only visualize events triggered by hosts configured with the Elastic Defend integration or any sysmon data from winlogbeat. Refer to Visual event analyzerExternal link(opens in a new tab or window) for more information.'; + 'You can only visualize events triggered by hosts configured with the Elastic Defend integration or any sysmon data from winlogbeat. Refer to Visual event analyzer(external, opens in a new tab or window) for more information.'; const renderAnalyzerPreview = (context = mockContextValue) => render( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.test.tsx index 4d4c20787ce1af..db7f60938c0c37 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.test.tsx @@ -40,7 +40,7 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { }); const NO_DATA_MESSAGE = - 'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View dataExternal link(opens in a new tab or window) for more information.'; + 'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View data(external, opens in a new tab or window) for more information.'; const UPSELL_TEXT = 'This feature requires an Enterprise subscription'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/session_view_no_data_message.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/session_view_no_data_message.test.tsx index 49f0056c50c0a6..e5153e24c15198 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/session_view_no_data_message.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/components/session_view_no_data_message.test.tsx @@ -13,7 +13,7 @@ import { SESSION_VIEW_UPSELL_TEST_ID, SESSION_VIEW_NO_DATA_TEST_ID } from './tes import { SessionViewNoDataMessage } from './session_view_no_data_message'; const NO_DATA_MESSAGE = - 'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View dataExternal link(opens in a new tab or window) for more information.'; + 'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View data(external, opens in a new tab or window) for more information.'; const UPSELL_TEXT = 'This feature requires an Enterprise subscription'; diff --git a/x-pack/plugins/security_solution/public/management/components/console/components/command_list.test.tsx b/x-pack/plugins/security_solution/public/management/components/console/components/command_list.test.tsx index b3a4a1d9ab14e7..62ef40ec276682 100644 --- a/x-pack/plugins/security_solution/public/management/components/console/components/command_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console/components/command_list.test.tsx @@ -69,7 +69,7 @@ describe('When rendering the command list (help output)', () => { expect(renderResult.getByTestId('test-commandList-helpfulTips')).toHaveTextContent( 'Helpful tips:You can enter consecutive response actions — no need to wait for previous ' + 'actions to complete.Leaving the response console does not terminate any actions that have ' + - 'been submitted.Learn moreExternal link(opens in a new tab or window) about response actions ' + + 'been submitted.Learn more(external, opens in a new tab or window) about response actions ' + 'and using the console.' ); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/related_detection_rules_callout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/related_detection_rules_callout.test.tsx index 0044e29af60cf7..d742f27b2fc187 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/related_detection_rules_callout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/related_detection_rules_callout.test.tsx @@ -29,7 +29,7 @@ describe('Policy form RelatedDetectionRulesCallout component', () => { expect(renderResult.getByTestId('test')).toHaveTextContent( exactMatchText( - 'The Endpoint Security detection rule is enabled automatically with Elastic Defend. This rule must remain enabled to receive Endpoint alerts. Learn MoreExternal link(opens in a new tab or window).' + 'The Endpoint Security detection rule is enabled automatically with Elastic Defend. This rule must remain enabled to receive Endpoint alerts. Learn More(external, opens in a new tab or window).' ) ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/setting_locked_card.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/setting_locked_card.test.tsx index 37327121e5a4b8..91e74d1f35d5af 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/setting_locked_card.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_settings_form/components/setting_locked_card.test.tsx @@ -41,7 +41,7 @@ describe('Policy form SettingLockedCard component', () => { 'To turn on this protection, you must upgrade your license to Platinum, start a free 30-day ' + 'trial, or spin up a ' + 'cloud deployment' + - 'External link(opens in a new tab or window) ' + + '(external, opens in a new tab or window) ' + 'on AWS, GCP, or Azure.Platinum' ) ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/__snapshots__/field_renderers.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/__snapshots__/field_renderers.test.tsx.snap index 17ae6d1941be86..06650bf2734bda 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/__snapshots__/field_renderers.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/__snapshots__/field_renderers.test.tsx.snap @@ -276,13 +276,12 @@ exports[`Field Renderers #whoisRenderer it renders correctly against snapshot 1` - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap index cbefd017dfbdab..e881d9d5d1ce10 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap @@ -1012,13 +1012,12 @@ tr:hover .c3:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -1755,13 +1754,12 @@ tr:hover .c3:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -2135,13 +2133,12 @@ tr:hover .c3:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -2221,13 +2218,12 @@ tr:hover .c3:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -2307,13 +2303,12 @@ tr:hover .c3:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap index a5cd1d996a0073..d99d87ffb6981f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap @@ -1202,13 +1202,12 @@ tr:hover .c5:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -2106,13 +2105,12 @@ tr:hover .c5:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -2556,13 +2554,12 @@ tr:hover .c5:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -2652,13 +2649,12 @@ tr:hover .c5:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) @@ -2748,13 +2744,12 @@ tr:hover .c5:focus::before { - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/custom_timeline_data_grid_body.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/custom_timeline_data_grid_body.test.tsx index e93b9014785f4c..b6d7f52f2d92fa 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/custom_timeline_data_grid_body.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/custom_timeline_data_grid_body.test.tsx @@ -46,6 +46,9 @@ const defaultProps: CustomTimelineDataGridBodyProps = { enabledRowRenderers: [], setCustomGridBodyProps: jest.fn(), visibleColumns: mockVisibleColumns, + headerRow: <>, + footerRow: null, + gridWidth: 0, }; const renderTestComponents = (props?: CustomTimelineDataGridBodyProps) => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/custom_timeline_data_grid_body.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/custom_timeline_data_grid_body.tsx index fdf46c50a55f49..559dcbf10c4e6b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/custom_timeline_data_grid_body.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/custom_timeline_data_grid_body.tsx @@ -42,8 +42,17 @@ const DEFAULT_UDT_ROW_HEIGHT = 34; * */ export const CustomTimelineDataGridBody: FC = memo( function CustomTimelineDataGridBody(props) { - const { Cell, visibleColumns, visibleRowData, rows, rowHeight, enabledRowRenderers, refetch } = - props; + const { + Cell, + headerRow, + footerRow, + visibleColumns, + visibleRowData, + rows, + rowHeight, + enabledRowRenderers, + refetch, + } = props; const visibleRows = useMemo( () => (rows ?? []).slice(visibleRowData.startRow, visibleRowData.endRow), @@ -52,6 +61,7 @@ export const CustomTimelineDataGridBody: FC = m return ( <> + {headerRow} {visibleRows.map((row, rowIndex) => { return ( = m /> ); })} + {footerRow} ); } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx index 0b629788b7691a..e4862fe8d72f6b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/unified_components/data_table/index.tsx @@ -331,15 +331,21 @@ export const TimelineDataTableComponent: React.FC = memo( visibleRowData, visibleColumns, setCustomGridBodyProps, + gridWidth, + headerRow, + footerRow, }: EuiDataGridCustomBodyProps) => ( ), diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 9bd57ee7ca120a..cd83be255a90e0 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -720,7 +720,6 @@ "core.euiDisplaySelector.rowHeightLabel": "Hauteur de la ligne", "core.euiDualRange.sliderScreenReaderInstructions": "Vous êtes dans un curseur de plage personnalisé. Utilisez les flèches vers le haut et vers le bas pour modifier la valeur minimale. Appuyez sur Tabulation pour interagir avec la valeur maximale.", "core.euiErrorBoundary.error": "Erreur", - "core.euiExternalLinkIcon.ariaLabel": "Lien externe", "core.euiExternalLinkIcon.newTarget.screenReaderOnlyText": "(s’ouvre dans un nouvel onglet ou une nouvelle fenêtre)", "core.euiFieldPassword.maskPassword": "Masquer le mot de passe", "core.euiFieldPassword.showPassword": "Afficher le mot de passe en texte brut. Remarque : votre mot de passe sera visible à l'écran.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8ec4dcb9e23aad..bed5620413f7b5 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -721,7 +721,6 @@ "core.euiDisplaySelector.rowHeightLabel": "行高さ", "core.euiDualRange.sliderScreenReaderInstructions": "カスタム範囲スライダーを操作しています。上下矢印キーを使用すると、最小値を変更できます。Tabを押すと、最大値を操作できます。", "core.euiErrorBoundary.error": "エラー", - "core.euiExternalLinkIcon.ariaLabel": "外部リンク", "core.euiExternalLinkIcon.newTarget.screenReaderOnlyText": "(新しいタブまたはウィンドウで開く)", "core.euiFieldPassword.maskPassword": "パスワードをマスク", "core.euiFieldPassword.showPassword": "プレーンテキストとしてパスワードを表示します。注記:パスワードは画面上に見えるように表示されます。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 740c7fdbb25d97..026f4f6a76dd6e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -720,7 +720,6 @@ "core.euiDisplaySelector.rowHeightLabel": "行高", "core.euiDualRange.sliderScreenReaderInstructions": "您正使用定制范围滑块。使用向上和向下箭头键可更改最小值。按 Tab 键与最大值进行交互。", "core.euiErrorBoundary.error": "错误", - "core.euiExternalLinkIcon.ariaLabel": "外部链接", "core.euiExternalLinkIcon.newTarget.screenReaderOnlyText": "(在新选项卡或窗口中打开)", "core.euiFieldPassword.maskPassword": "屏蔽密码", "core.euiFieldPassword.showPassword": "将密码显示为纯文本。注意:这会将您的密码暴露在屏幕上。", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx index 4259c62fe0f509..4108233c4bf292 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx @@ -115,11 +115,11 @@ describe('health check', () => { const [action] = queryAllByText(/Learn more/i); expect(description.textContent).toMatchInlineSnapshot( - `"You must enable API keys to use Alerting. Learn more.External link(opens in a new tab or window)"` + `"You must enable API keys to use Alerting. Learn more.(external, opens in a new tab or window)"` ); expect(action.textContent).toMatchInlineSnapshot( - `"Learn more.External link(opens in a new tab or window)"` + `"Learn more.(external, opens in a new tab or window)"` ); expect(action.getAttribute('href')).toMatchInlineSnapshot( @@ -153,12 +153,12 @@ describe('health check', () => { const description = queryByRole(/banner/i); expect(description!.textContent).toMatchInlineSnapshot( - `"You must configure an encryption key to use Alerting. Learn more.External link(opens in a new tab or window)"` + `"You must configure an encryption key to use Alerting. Learn more.(external, opens in a new tab or window)"` ); const action = queryByText(/Learn/i); expect(action!.textContent).toMatchInlineSnapshot( - `"Learn more.External link(opens in a new tab or window)"` + `"Learn more.(external, opens in a new tab or window)"` ); expect(action!.getAttribute('href')).toMatchInlineSnapshot( `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/alert-action-settings-kb.html#general-alert-action-settings"` @@ -193,12 +193,12 @@ describe('health check', () => { const description = queryByText(/You must enable/i); expect(description!.textContent).toMatchInlineSnapshot( - `"You must enable API keys and configure an encryption key to use Alerting. Learn more.External link(opens in a new tab or window)"` + `"You must enable API keys and configure an encryption key to use Alerting. Learn more.(external, opens in a new tab or window)"` ); const action = queryByText(/Learn/i); expect(action!.textContent).toMatchInlineSnapshot( - `"Learn more.External link(opens in a new tab or window)"` + `"Learn more.(external, opens in a new tab or window)"` ); expect(action!.getAttribute('href')).toMatchInlineSnapshot( `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/alerting-setup.html#alerting-prerequisites"` diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx index 5fc3def2adc81d..617b0f9c70a0a2 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx @@ -683,17 +683,21 @@ const AlertsTable: React.FunctionComponent = memo((props: Aler }, [props.gridStyle, mergedGridStyle]); const renderCustomGridBody = useCallback>( - ({ visibleColumns: _visibleColumns, Cell }) => ( - + ({ visibleColumns: _visibleColumns, Cell, headerRow, footerRow }) => ( + <> + {headerRow} + + {footerRow} + ), [actualGridStyle, oldAlertsData, pageIndex, pageSize, isLoading, props.gridStyle?.stripes] ); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_response_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_response_tab.cy.ts index 2a5966c4c2b34b..97935eabd626e0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_response_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_response_tab.cy.ts @@ -45,7 +45,7 @@ describe( cy.get(DOCUMENT_DETAILS_FLYOUT_RESPONSE_EMPTY).and( 'contain.text', - "There are no response actions defined for this event. To add some, edit the rule's settings and set up response actions(opens in a new tab or window)." + "There are no response actions defined for this event. To add some, edit the rule's settings and set up response actions(external, opens in a new tab or window)." ); }); } diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts index d243e3f51cd2ec..debc4181294a0d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel_overview_tab.cy.ts @@ -163,7 +163,7 @@ describe( cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_SESSION_PREVIEW_CONTAINER).should( 'contain.text', - 'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View data(opens in a new tab or window) for more information.' + 'You can only view Linux session details if you’ve enabled the Include session data setting in your Elastic Defend integration policy. Refer to Enable Session View data(external, opens in a new tab or window) for more information.' ); cy.log('analyzer graph preview'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/notes_tab.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/notes_tab.cy.ts index c9ff65129bfe19..13a4c73f149de7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/notes_tab.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/notes_tab.cy.ts @@ -72,7 +72,9 @@ describe('Timeline notes tab', { tags: ['@ess', '@serverless'] }, () => { it('should be able to render a link', () => { addNotesToTimeline(`[${author}](${link})`); - cy.get(NOTES_LINK).last().should('have.text', `${author}(opens in a new tab or window)`); + cy.get(NOTES_LINK) + .last() + .should('have.text', `${author}(external, opens in a new tab or window)`); cy.get(NOTES_LINK).last().click(); }); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts index 5909f1655eb86c..e5d6711b7d16e4 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts @@ -138,8 +138,7 @@ export const TIMELINE_FIELD = (field: string) => { return `[data-test-subj="formatted-field-${field}"]`; }; -export const removeExternalLinkText = (str: string) => - str.replace(/\(opens in a new tab or window\)/g, ''); +export const removeExternalLinkText = (str: string) => str.replace(/\(external[^)]*\)/g, ''); export const DEFINE_RULE_PANEL_PROGRESS = '[data-test-subj="defineRule"] [data-test-subj="stepPanelProgress"]'; diff --git a/yarn.lock b/yarn.lock index e7042d208b96dc..4f2c4b5435749e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1753,10 +1753,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@96.1.0": - version "96.1.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-96.1.0.tgz#cd75f2a7a2ca07df6fb8f9af985dff3f05172fb6" - integrity sha512-LmB92xr704Dfth9UnxCOm4b63lghb/7svXsnd0zcbSQA/BPqktUm1evZjWYIWFIek5D4JI4dmM+ygXLWdKSM+Q== +"@elastic/eui@97.0.0": + version "97.0.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-97.0.0.tgz#b7828b8818de1328e47b4c47024d8455a5795f23" + integrity sha512-ha7oer/0ou0MnZMgwZzqKE97tx/IPhQtb04nNLZvwOiBAH+ANtUqohYSM/3VxLuJT13cxbsLC2CLT0Ktcibo4w== dependencies: "@hello-pangea/dnd" "^16.6.0" "@types/lodash" "^4.14.202" From 414ae5d638d27f6cb9d85d5d74bc8d0e0b99198d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Gonz=C3=A1lez?= Date: Mon, 14 Oct 2024 13:41:48 +0200 Subject: [PATCH 27/38] [Search][Ent Search deprecation] Web Crawler tile points out GH repo instead become disabled (#194743) ## Summary This PR sets the Web Crawler tile to point out the external Open Web Crawler repo when there is no ent-search node running rather than become disabled using the `crawlerDisabled` Before: ![CleanShot 2024-10-02 at 18 25 57@2x](https://github.com/user-attachments/assets/2cffe7c8-fbb1-4192-956f-69ba8ec5529a) After: ![CleanShot 2024-10-02 at 18 25 11@2x](https://github.com/user-attachments/assets/fcf7ac0f-2985-4b7a-9100-3968054505c7) Also the empty state of Web crawler points out to the Source code repo when there is no ent-search instance running using the `errorConnectingMessage`. This improvement should fix this issue https://github.com/elastic/search-team/issues/8319?reload=1?reload=1 ![CleanShot 2024-10-08 at 11 48 44@2x](https://github.com/user-attachments/assets/1dedc24e-e23a-4188-a676-f910a9b2ce6c) ### 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: Elastic Machine --- .../enterprise_search/common/constants.ts | 4 ++ .../connectors/crawler_empty_state.tsx | 45 +++++++++++++------ .../shared/ingestion_card/ingestion_card.tsx | 13 ++++++ .../product_selector/ingestion_selector.tsx | 34 ++++++++++---- .../applications/shared/icons/github_icon.tsx | 32 +++++++++++++ .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 8 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/shared/icons/github_icon.tsx diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index 10b472b1efca1b..795237ef9b427c 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -281,5 +281,9 @@ export const PLUGIN_ID = 'enterpriseSearch'; export const CONNECTOR_NATIVE_TYPE = 'native'; export const CONNECTOR_CLIENTS_TYPE = 'connector_clients'; +export const CRAWLER = { + github_repo: 'https://github.com/elastic/crawler', +}; + // TODO remove this once the connector service types are no longer in "example" state export const EXAMPLE_CONNECTOR_SERVICE_TYPES = ['opentext_documentum']; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/crawler_empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/crawler_empty_state.tsx index 8e5b91b94e39b5..5a03d0560dfbf1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/crawler_empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/crawler_empty_state.tsx @@ -11,7 +11,9 @@ import { useValues } from 'kea'; import { EuiButton, EuiEmptyPrompt, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { CRAWLER } from '../../../../../common/constants'; import { HttpLogic } from '../../../shared/http'; +import { GithubIcon } from '../../../shared/icons/github_icon'; import { KibanaLogic } from '../../../shared/kibana'; import { NEW_CRAWLER_PATH } from '../../routes'; @@ -40,19 +42,36 @@ export const CrawlerEmptyState: React.FC = () => {

} actions={ - KibanaLogic.values.navigateToUrl(NEW_CRAWLER_PATH)} - > - {i18n.translate('xpack.enterpriseSearch.crawlerEmptyState.newWebCrawlerButtonLabel', { - defaultMessage: 'New web crawler', - })} - + Boolean(errorConnectingMessage) ? ( + + {i18n.translate( + 'xpack.enterpriseSearch.crawlerEmptyState.openSourceCrawlerButtonLabel', + { + defaultMessage: 'Source code', + } + )} + + ) : ( + KibanaLogic.values.navigateToUrl(NEW_CRAWLER_PATH)} + > + {i18n.translate('xpack.enterpriseSearch.crawlerEmptyState.newWebCrawlerButtonLabel', { + defaultMessage: 'New web crawler', + })} + + ) } /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/ingestion_card/ingestion_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/ingestion_card/ingestion_card.tsx index 0d01eea4e6787d..94bbc515f92bd8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/ingestion_card/ingestion_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/shared/ingestion_card/ingestion_card.tsx @@ -18,6 +18,8 @@ import { IconType, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + import { EuiLinkTo } from '../../../../shared/react_router_helpers'; interface IngestionCardProps { @@ -25,6 +27,7 @@ interface IngestionCardProps { buttonLabel: string; description: string; href?: string; + isBeta?: boolean; isDisabled?: boolean; logo: IconType; onClick?: () => void; @@ -37,6 +40,7 @@ export const IngestionCard: React.FC = ({ description, href, isDisabled, + isBeta, logo, onClick, title, @@ -44,6 +48,15 @@ export const IngestionCard: React.FC = ({ return ( { @@ -76,13 +78,23 @@ export const IngestionSelector: React.FC = () => { {productFeatures.hasWebCrawler && ( { 'Discover, extract, and index searchable content from websites and knowledge bases.', } )} - href={generatePath(ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + NEW_CRAWLER_PATH)} - isDisabled={crawlerDisabled} + href={ + crawlerDisabled + ? CRAWLER.github_repo + : generatePath(ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL + NEW_CRAWLER_PATH) + } + isBeta={crawlerDisabled} logo={crawlerLogo} title={i18n.translate('xpack.enterpriseSearch.ingestSelector.method.crawler', { defaultMessage: 'Web Crawler', diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/icons/github_icon.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/icons/github_icon.tsx new file mode 100644 index 00000000000000..0fc9160272838f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/icons/github_icon.tsx @@ -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 React from 'react'; + +export const GithubIcon = () => { + return ( + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index cd83be255a90e0..af71b7b1b9eda9 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -17385,7 +17385,6 @@ "xpack.enterpriseSearch.ingestSelector.method.connectors.description": "Extraire, transformer, indexer et synchroniser des données issues d'une source de données tiers.", "xpack.enterpriseSearch.ingestSelector.method.crawler": "Robot d'indexation", "xpack.enterpriseSearch.ingestSelector.method.crawler.description": "Découvrir, extraire et indexer du contenu interrogeable provenant de sites web et de bases de connaissances.", - "xpack.enterpriseSearch.ingestSelector.method.crawlerButtonLabel": "Indexer l'URL", "xpack.enterpriseSearch.ingestSelector.method.fileUpload": "Charger un fichier", "xpack.enterpriseSearch.ingestSelector.method.fileUpload.description": "Fichiers texte délimités, tels que CSV et TSV, JSON délimité par une nouvelle ligne.", "xpack.enterpriseSearch.ingestSelector.method.fileUploadLabel": "Choisir un fichier", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index bed5620413f7b5..cdd8afc68af2eb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17131,7 +17131,6 @@ "xpack.enterpriseSearch.ingestSelector.method.connectors.description": "サードパーティのデータソースからデータを抽出、変換、インデックス化、同期します。", "xpack.enterpriseSearch.ingestSelector.method.crawler": "Webクローラー", "xpack.enterpriseSearch.ingestSelector.method.crawler.description": "Webサイトやナレッジベースから検索可能なコンテンツを検出、抽出、インデックス化します。", - "xpack.enterpriseSearch.ingestSelector.method.crawlerButtonLabel": "クロールURL", "xpack.enterpriseSearch.ingestSelector.method.fileUpload": "ファイルをアップロード", "xpack.enterpriseSearch.ingestSelector.method.fileUpload.description": "CSVやTSV、改行区切りのJSONなどの区切られたテキストファイル。", "xpack.enterpriseSearch.ingestSelector.method.fileUploadLabel": "ファイルを選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 026f4f6a76dd6e..b94fb455c8ad59 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -17160,7 +17160,6 @@ "xpack.enterpriseSearch.ingestSelector.method.connectors.description": "提取、转换、索引和同步来自第三方数据源的数据。", "xpack.enterpriseSearch.ingestSelector.method.crawler": "网络爬虫", "xpack.enterpriseSearch.ingestSelector.method.crawler.description": "发现、提取和索引网站和知识库中的可搜索内容。", - "xpack.enterpriseSearch.ingestSelector.method.crawlerButtonLabel": "爬网 URL", "xpack.enterpriseSearch.ingestSelector.method.fileUpload": "上传文件", "xpack.enterpriseSearch.ingestSelector.method.fileUpload.description": "分隔的文本文件,例如 CSV 和 TSV、换行符分隔的 JSON。", "xpack.enterpriseSearch.ingestSelector.method.fileUploadLabel": "选择文件", From efab00b36ede744916f924c6a7965dd93624493b Mon Sep 17 00:00:00 2001 From: Joe McElroy Date: Mon, 14 Oct 2024 12:53:25 +0100 Subject: [PATCH 28/38] [Onboarding] only update the index details page when plugin is enabled (#196077) ## Summary The index details page is always updated even when the plugin is disabled. Using the pluginEnabled conditional to only update when enabled. ### How to replicate 1. disable uisetting for search indices plugin 2. go to index management and click on a index detail Expected: see the old index detail page actual: goes to the new index detail url but does not render the search detail page (as plugin disabled) ### Checklist Delete any items that are not applicable to this PR. - [ ] [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 --- x-pack/plugins/search_indices/public/plugin.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/search_indices/public/plugin.ts b/x-pack/plugins/search_indices/public/plugin.ts index a2dd610234b829..8ff86d4d3c7747 100644 --- a/x-pack/plugins/search_indices/public/plugin.ts +++ b/x-pack/plugins/search_indices/public/plugin.ts @@ -87,11 +87,13 @@ export class SearchIndicesPlugin ): SearchIndicesPluginStart { const { indexManagement } = deps; docLinks.setDocLinks(core.docLinks.links); - indexManagement?.extensionsService.setIndexDetailsPageRoute({ - renderRoute: (indexName) => { - return `/app/elasticsearch/indices/index_details/${indexName}`; - }, - }); + if (this.pluginEnabled) { + indexManagement?.extensionsService.setIndexDetailsPageRoute({ + renderRoute: (indexName) => { + return `/app/elasticsearch/indices/index_details/${indexName}`; + }, + }); + } return { enabled: this.pluginEnabled, startAppId: START_APP_ID, From 02266345cb4199342867e3d9d5718090297f5700 Mon Sep 17 00:00:00 2001 From: Mykola Harmash Date: Mon, 14 Oct 2024 14:14:23 +0200 Subject: [PATCH 29/38] [Onboarding][Auto-detect] Update design for supported integrations badges (#195351) Closes https://github.com/elastic/observability-dev/issues/4007 Updates integration badges according to [the latest designs](https://www.figma.com/design/CPhMyRNOgo0wsEiaIMZJ14/Onboarding-Quick-Starts?node-id=3015-58062&t=5tvnrPIOkfg7xAJp-1). ![CleanShot 2024-10-08 at 08 57 48@2x](https://github.com/user-attachments/assets/1ae0d9d7-3b1f-4acd-b16f-ad1cbc09db88) --- .../auto_detect/auto_detect_panel.tsx | 27 +---- .../supported_integrations_list.tsx | 98 +++++++++++++++++++ .../public/assets/docker.svg | 10 ++ .../public/assets/nginx.svg | 4 + 4 files changed, 114 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/supported_integrations_list.tsx create mode 100644 x-pack/plugins/observability_solution/observability_onboarding/public/assets/docker.svg create mode 100644 x-pack/plugins/observability_solution/observability_onboarding/public/assets/nginx.svg diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx index d03936c6a89ac0..5d62f1060b50e2 100644 --- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/auto_detect_panel.tsx @@ -13,9 +13,6 @@ import { EuiCodeBlock, EuiSpacer, EuiSkeletonText, - EuiBadge, - EuiFlexGroup, - EuiFlexItem, EuiText, useGeneratedHtmlId, EuiIcon, @@ -38,6 +35,7 @@ import { isSupportedLogo, LogoIcon } from '../../shared/logo_icon'; import { FeedbackButtons } from '../shared/feedback_buttons'; import { ObservabilityOnboardingContextValue } from '../../../plugin'; import { useAutoDetectTelemetry } from './use_auto_detect_telemetry'; +import { SupportedIntegrationsList } from './supported_integrations_list'; export const AutoDetectPanel: FunctionComponent = () => { const { status, data, error, refetch, installedIntegrations } = useOnboardingFlow(); @@ -92,28 +90,7 @@ export const AutoDetectPanel: FunctionComponent = () => {

- - {[ - 'Apache', - 'Docker', - 'Nginx', - 'System', - 'MySQL', - 'PostgreSQL', - 'Redis', - 'HAProxy', - 'Kafka', - 'RabbitMQ', - 'Prometheus', - 'Tomcat', - 'MongoDB', - 'Custom .log files', - ].map((item) => ( - - {item} - - ))} - + {/* Bash syntax highlighting only highlights a few random numbers (badly) so it looks less messy to go with plain text */} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/supported_integrations_list.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/supported_integrations_list.tsx new file mode 100644 index 00000000000000..8351b7d709917b --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/quickstart_flows/auto_detect/supported_integrations_list.tsx @@ -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 React from 'react'; +import { + EuiBadge, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiTextColor, + EuiToolTip, + IconType, + useEuiTheme, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import apacheIconSrc from '../../../assets/apache.svg'; +import dockerIconSrc from '../../../assets/docker.svg'; +import nginxIconSrc from '../../../assets/nginx.svg'; +import mysqlIconSrc from '../../../assets/mysql.svg'; + +const SUPPORTED_INTEGRATIONS_LIST = [ + 'Apache', + 'Docker', + 'Nginx', + 'System', + 'MySQL', + 'PostgreSQL', + 'Redis', + 'Haproxy', + 'Kafka', + 'RabbitMQ', + 'Prometheus', + 'Apache Tomcat', + 'MongoDB', +] as const; + +type SupportedIntegrationName = (typeof SUPPORTED_INTEGRATIONS_LIST)[number]; + +interface SupportedIntegrationItem { + title: SupportedIntegrationName; + icon: IconType; +} + +const FEATURED_INTEGRATIONS_LIST: SupportedIntegrationItem[] = [ + { title: 'Apache', icon: apacheIconSrc }, + { title: 'Docker', icon: dockerIconSrc }, + { title: 'Nginx', icon: nginxIconSrc }, + { title: 'MySQL', icon: mysqlIconSrc }, + { title: 'System', icon: 'desktop' }, +]; + +export function SupportedIntegrationsList() { + const { + euiTheme: { colors }, + } = useEuiTheme(); + const customLogFilesTitle = i18n.translate( + 'xpack.observability_onboarding.autoDetectPanel.supportedIntegrationsList.customIntegrationTitle', + { defaultMessage: 'Custom .log files' } + ); + return ( + + {FEATURED_INTEGRATIONS_LIST.map(({ title, icon }) => ( + + + {title} + + + ))} + + + {customLogFilesTitle} + + + +
    + {SUPPORTED_INTEGRATIONS_LIST.map((integration) => ( +
  • {integration}
  • + ))} +
  • {customLogFilesTitle}
  • +
+ + } + > + + + {`+${SUPPORTED_INTEGRATIONS_LIST.length - FEATURED_INTEGRATIONS_LIST.length}`} + + +
+
+ ); +} diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/assets/docker.svg b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/docker.svg new file mode 100644 index 00000000000000..0833b403ad87c2 --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/docker.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/assets/nginx.svg b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/nginx.svg new file mode 100644 index 00000000000000..d01940ba02a91e --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_onboarding/public/assets/nginx.svg @@ -0,0 +1,4 @@ + + + + From f787b852b23139fbc8e9926263d827ded4a1f451 Mon Sep 17 00:00:00 2001 From: Julian Gernun <17549662+jcger@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:18:42 +0200 Subject: [PATCH 30/38] [Response Ops][Rules] Version Mute All Rule API (#195572) ## Summary `POST /api/alerting/rule/{id}/_mute_all` in https://github.com/elastic/kibana/issues/195181 --- .../common/routes/rule/apis/mute_all/index.ts | 12 +++ .../rule/apis/mute_all/schemas/latest.ts | 8 ++ .../routes/rule/apis/mute_all/schemas/v1.ts | 16 ++++ .../routes/rule/apis/mute_all/types/latest.ts | 8 ++ .../routes/rule/apis/mute_all/types/v1.ts | 11 +++ .../rule/methods/mute_all/index.ts | 9 +++ .../rule/methods/mute_all/mute_all.test.ts | 75 +++++++++++++++++++ .../rule/methods/mute_all}/mute_all.ts | 35 ++++++--- .../rule/methods/mute_all/schemas/index.ts | 8 ++ .../mute_all/schemas/mute_all_rule_schemas.ts | 12 +++ .../rule/methods/mute_all/types/index.ts | 8 ++ .../mute_all/types/mute_all_rule_types.ts | 11 +++ .../plugins/alerting/server/routes/index.ts | 2 +- .../apis/mute_all}/mute_all_rule.test.ts | 14 ++-- .../{ => rule/apis/mute_all}/mute_all_rule.ts | 27 +++---- .../server/rules_client/rules_client.ts | 2 +- 16 files changed, 222 insertions(+), 36 deletions(-) create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts create mode 100644 x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts rename x-pack/plugins/alerting/server/{rules_client/methods => application/rule/methods/mute_all}/mute_all.ts (65%) create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts create mode 100644 x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts rename x-pack/plugins/alerting/server/routes/{ => rule/apis/mute_all}/mute_all_rule.test.ts (85%) rename x-pack/plugins/alerting/server/routes/{ => rule/apis/mute_all}/mute_all_rule.ts (72%) diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.ts new file mode 100644 index 00000000000000..ba1dd568aeeb2d --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/index.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 { muteAllRuleRequestParamsSchema } from './schemas/latest'; +export type { MuteAllRuleRequestParams } from './types/latest'; + +export { muteAllRuleRequestParamsSchema as muteAllRuleRequestParamsSchemaV1 } from './schemas/v1'; +export type { MuteAllRuleRequestParams as MuteAllRuleRequestParamsV1 } from './types/v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.ts new file mode 100644 index 00000000000000..25300c97a6d2e1 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/latest.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 * from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.ts new file mode 100644 index 00000000000000..9305dac3d46eb8 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/schemas/v1.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 { schema } from '@kbn/config-schema'; + +export const muteAllRuleRequestParamsSchema = schema.object({ + id: schema.string({ + meta: { + description: 'The identifier for the rule.', + }, + }), +}); diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.ts new file mode 100644 index 00000000000000..25300c97a6d2e1 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/latest.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 * from './v1'; diff --git a/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts new file mode 100644 index 00000000000000..c18aa22dadd131 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/rule/apis/mute_all/types/v1.ts @@ -0,0 +1,11 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; +import { muteAllRuleRequestParamsSchemaV1 } from '..'; + +export type MuteAllRuleRequestParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/index.ts new file mode 100644 index 00000000000000..c8b85c149314e7 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type { MuteAllRuleParams } from './types'; +export { muteAll } from './mute_all'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts new file mode 100644 index 00000000000000..eba9fc4cbf7d4d --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.test.ts @@ -0,0 +1,75 @@ +/* + * 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 { RulesClientContext } from '../../../../rules_client'; +import { muteAll } from './mute_all'; +import { savedObjectsRepositoryMock } from '@kbn/core-saved-objects-api-server-mocks'; + +jest.mock('../../../../lib/retry_if_conflicts', () => ({ + retryIfConflicts: (_: unknown, id: unknown, asyncFn: () => Promise) => { + return asyncFn(); + }, +})); + +jest.mock('../../../../rules_client/lib', () => ({ + updateMetaAttributes: () => {}, +})); + +jest.mock('../../../../saved_objects', () => ({ + partiallyUpdateRule: async () => {}, +})); + +const loggerErrorMock = jest.fn(); +const getBulkMock = jest.fn(); + +const savedObjectsMock = savedObjectsRepositoryMock.create(); +savedObjectsMock.get = jest.fn().mockReturnValue({ + attributes: { + actions: [], + }, + version: '9.0.0', +}); + +const context = { + logger: { error: loggerErrorMock }, + getActionsClient: () => { + return { + getBulk: getBulkMock, + }; + }, + unsecuredSavedObjectsClient: savedObjectsMock, + authorization: { ensureAuthorized: async () => {} }, + ruleTypeRegistry: { + ensureRuleTypeEnabled: () => {}, + }, + getUserName: async () => {}, +} as unknown as RulesClientContext; + +describe('validateMuteAllParams', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should not throw an error for valid params', () => { + const validParams = { + id: 'ble', + }; + + expect(() => muteAll(context, validParams)).not.toThrow(); + expect(savedObjectsMock.get).toHaveBeenCalled(); + }); + + it('should throw Boom.badRequest for invalid params', async () => { + const invalidParams = { + id: 22 as unknown as string, // type workaround to send wrong data validation + }; + + await expect(muteAll(context, invalidParams)).rejects.toThrowErrorMatchingInlineSnapshot( + `"Error validating mute all parameters - [id]: expected value of type [string] but got [number]"` + ); + }); +}); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts similarity index 65% rename from x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts rename to x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts index 4e647ee6e58ac9..73cfe6e26fdce3 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/mute_all.ts @@ -5,17 +5,23 @@ * 2.0. */ -import { RawRule } from '../../types'; -import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; -import { retryIfConflicts } from '../../lib/retry_if_conflicts'; -import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; -import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; -import { RulesClientContext } from '../types'; -import { updateMetaAttributes } from '../lib'; -import { clearUnscheduledSnoozeAttributes } from '../common'; -import { RuleAttributes } from '../../data/rule/types'; +import Boom from '@hapi/boom'; +import { RawRule } from '../../../../types'; +import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization'; +import { retryIfConflicts } from '../../../../lib/retry_if_conflicts'; +import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; +import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; +import { RulesClientContext } from '../../../../rules_client/types'; +import { updateMetaAttributes } from '../../../../rules_client/lib'; +import { clearUnscheduledSnoozeAttributes } from '../../../../rules_client/common'; +import { RuleAttributes } from '../../../../data/rule/types'; +import { MuteAllRuleParams } from './types'; +import { muteAllRuleParamsSchema } from './schemas'; -export async function muteAll(context: RulesClientContext, { id }: { id: string }): Promise { +export async function muteAll( + context: RulesClientContext, + { id }: MuteAllRuleParams +): Promise { return await retryIfConflicts( context.logger, `rulesClient.muteAll('${id}')`, @@ -23,7 +29,14 @@ export async function muteAll(context: RulesClientContext, { id }: { id: string ); } -async function muteAllWithOCC(context: RulesClientContext, { id }: { id: string }) { +async function muteAllWithOCC(context: RulesClientContext, params: MuteAllRuleParams) { + try { + muteAllRuleParamsSchema.validate(params); + } catch (error) { + throw Boom.badRequest(`Error validating mute all parameters - ${error.message}`); + } + + const { id } = params; const { attributes, version } = await context.unsecuredSavedObjectsClient.get( RULE_SAVED_OBJECT_TYPE, id diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/index.ts new file mode 100644 index 00000000000000..b6c6729ac50293 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/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 * from './mute_all_rule_schemas'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.ts new file mode 100644 index 00000000000000..0d0ae33394e72a --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/schemas/mute_all_rule_schemas.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. + */ + +import { schema } from '@kbn/config-schema'; + +export const muteAllRuleParamsSchema = schema.object({ + id: schema.string(), +}); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/index.ts new file mode 100644 index 00000000000000..c2d2f7401b3505 --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/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 * from './mute_all_rule_types'; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts new file mode 100644 index 00000000000000..4f4ad36dbc23ab --- /dev/null +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_all/types/mute_all_rule_types.ts @@ -0,0 +1,11 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; +import { muteAllRuleParamsSchema } from '../schemas'; + +export type MuteAllRuleParams = TypeOf; diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index 93b1800208c7d6..352eb293fff773 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -31,7 +31,7 @@ import { getRuleStateRoute } from './get_rule_state'; import { healthRoute } from './health'; import { resolveRuleRoute } from './rule/apis/resolve'; import { ruleTypesRoute } from './rule_types'; -import { muteAllRuleRoute } from './mute_all_rule'; +import { muteAllRuleRoute } from './rule/apis/mute_all/mute_all_rule'; import { muteAlertRoute } from './rule/apis/mute_alert/mute_alert'; import { unmuteAllRuleRoute } from './unmute_all_rule'; import { unmuteAlertRoute } from './rule/apis/unmute_alert/unmute_alert_route'; diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts similarity index 85% rename from x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts index 131c20eb84a729..98cabee56904b3 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.test.ts @@ -7,18 +7,18 @@ import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock'; import { muteAllRuleRoute } from './mute_all_rule'; import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { rulesClientMock } from '../rules_client.mock'; -import { RuleTypeDisabledError } from '../lib/errors/rule_type_disabled'; -import { trackDeprecatedRouteUsage } from '../lib/track_deprecated_route_usage'; +import { licenseStateMock } from '../../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../../_mock_handler_arguments'; +import { rulesClientMock } from '../../../../rules_client.mock'; +import { RuleTypeDisabledError } from '../../../../lib/errors/rule_type_disabled'; +import { trackDeprecatedRouteUsage } from '../../../../lib/track_deprecated_route_usage'; const rulesClient = rulesClientMock.create(); -jest.mock('../lib/license_api_access', () => ({ +jest.mock('../../../../lib/license_api_access', () => ({ verifyApiAccess: jest.fn(), })); -jest.mock('../lib/track_deprecated_route_usage', () => ({ +jest.mock('../../../../lib/track_deprecated_route_usage', () => ({ trackDeprecatedRouteUsage: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts similarity index 72% rename from x-pack/plugins/alerting/server/routes/mute_all_rule.ts rename to x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts index ab220f74445901..8ac77973575bb1 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/mute_all/mute_all_rule.ts @@ -6,20 +6,15 @@ */ import { IRouter } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { ILicenseState, RuleTypeDisabledError } from '../lib'; -import { verifyAccessAndContext } from './lib'; -import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; -import { trackDeprecatedRouteUsage } from '../lib/track_deprecated_route_usage'; - -const paramSchema = schema.object({ - id: schema.string({ - meta: { - description: 'The identifier for the rule.', - }, - }), -}); +import { ILicenseState, RuleTypeDisabledError } from '../../../../lib'; +import { verifyAccessAndContext } from '../../../lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types'; +import { trackDeprecatedRouteUsage } from '../../../../lib/track_deprecated_route_usage'; +import { + muteAllRuleRequestParamsSchemaV1, + MuteAllRuleRequestParamsV1, +} from '../../../../../common/routes/rule/apis/mute_all'; export const muteAllRuleRoute = ( router: IRouter, @@ -36,7 +31,7 @@ export const muteAllRuleRoute = ( }, validate: { request: { - params: paramSchema, + params: muteAllRuleRequestParamsSchemaV1, }, response: { 204: { @@ -48,10 +43,10 @@ export const muteAllRuleRoute = ( router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); - const { id } = req.params; + const params: MuteAllRuleRequestParamsV1 = req.params; trackDeprecatedRouteUsage('muteAll', usageCounter); try { - await rulesClient.muteAll({ id }); + await rulesClient.muteAll(params); return res.noContent(); } catch (e) { if (e instanceof RuleTypeDisabledError) { diff --git a/x-pack/plugins/alerting/server/rules_client/rules_client.ts b/x-pack/plugins/alerting/server/rules_client/rules_client.ts index 80f9b82733a9df..163df75cc0e6b9 100644 --- a/x-pack/plugins/alerting/server/rules_client/rules_client.ts +++ b/x-pack/plugins/alerting/server/rules_client/rules_client.ts @@ -58,7 +58,7 @@ import { enableRule } from '../application/rule/methods/enable_rule/enable_rule' import { updateRuleApiKey } from '../application/rule/methods/update_api_key/update_rule_api_key'; import { disableRule } from '../application/rule/methods/disable/disable_rule'; import { muteInstance } from '../application/rule/methods/mute_alert/mute_instance'; -import { muteAll } from './methods/mute_all'; +import { muteAll } from '../application/rule/methods/mute_all'; import { unmuteAll } from './methods/unmute_all'; import { unmuteInstance } from '../application/rule/methods/unmute_alert/unmute_instance'; import { runSoon } from './methods/run_soon'; From 5067f1554cb5fc7f23442d5f9ab5d255e26a3b37 Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Mon, 14 Oct 2024 14:33:22 +0200 Subject: [PATCH 31/38] [APM][Otel] Add Otel client based on PoC data (#192293) Closes [#192115](https://github.com/elastic/kibana/issues/192115) Closes [#192465](https://github.com/elastic/kibana/issues/192465) ## Summary This PR adds synthrace client for Otel native data and a simple scenario. This is the first step of adding it and in the future it will include more metrics and use cases. >[!NOTE] > To run ES the command needs "xpack.otel_data.registry.enabled=true" flag > `yarn es snapshot --license trial --E "xpack.otel_data.registry.enabled=true"` ## Next steps - We currently have only `service_destination` in the metrics indices we can include the other types in the future - After we have all the UI changes we can add more scenarios (also using the opentelemetry demo data and not only the e2e PoC example) ## Testing - Run ES: ```bash yarn es snapshot --license trial --E "xpack.otel_data.registry.enabled=true" ``` - Run Kibana: ```bash yarn start ``` >[!WARNING] If the e2e PoC is used the first 2 steps should be skipped - Run syntrace: ```bash node scripts/synthtrace otel_simple_trace.ts --clean ``` - Check indices in DevTools for the generated data: ```bash GET *metrics-generic.otel*/_search GET *traces-generic.otel*/_search GET *logs-generic.otel*/_search ``` - Check in the APM UI (all the tabs) >[!WARNING] Currently the UI changes done in APM are not merged so some errors are expected) https://github.com/user-attachments/assets/92f63610-82da-40f3-89bb-00be83c55377 --------- Co-authored-by: miriam.aparicio --- packages/kbn-apm-synthtrace-client/index.ts | 1 + .../src/lib/otel/error.ts | 33 +++ .../src/lib/otel/index.ts | 213 ++++++++++++++++++ .../src/lib/otel/metric.ts | 33 +++ .../src/lib/otel/transaction.ts | 44 ++++ packages/kbn-apm-synthtrace/index.ts | 1 + .../kbn-apm-synthtrace/src/cli/scenario.ts | 2 + .../src/cli/utils/bootstrap.ts | 8 + .../src/cli/utils/get_otel_es_client.ts | 34 +++ .../src/cli/utils/start_live_data_upload.ts | 20 +- .../src/cli/utils/synthtrace_worker.ts | 17 +- .../src/lib/otel/otel_synthtrace_es_client.ts | 96 ++++++++ .../src/scenarios/otel_simple_trace.ts | 48 ++++ 13 files changed, 546 insertions(+), 4 deletions(-) create mode 100644 packages/kbn-apm-synthtrace-client/src/lib/otel/error.ts create mode 100644 packages/kbn-apm-synthtrace-client/src/lib/otel/index.ts create mode 100644 packages/kbn-apm-synthtrace-client/src/lib/otel/metric.ts create mode 100644 packages/kbn-apm-synthtrace-client/src/lib/otel/transaction.ts create mode 100644 packages/kbn-apm-synthtrace/src/cli/utils/get_otel_es_client.ts create mode 100644 packages/kbn-apm-synthtrace/src/lib/otel/otel_synthtrace_es_client.ts create mode 100644 packages/kbn-apm-synthtrace/src/scenarios/otel_simple_trace.ts diff --git a/packages/kbn-apm-synthtrace-client/index.ts b/packages/kbn-apm-synthtrace-client/index.ts index 6ac3b6525ec000..d3d24a8940a3b6 100644 --- a/packages/kbn-apm-synthtrace-client/index.ts +++ b/packages/kbn-apm-synthtrace-client/index.ts @@ -37,3 +37,4 @@ export type { ESDocumentWithOperation, SynthtraceESAction, SynthtraceGenerator } export { log, type LogDocument, LONG_FIELD_NAME } from './src/lib/logs'; export { type AssetDocument } from './src/lib/assets'; export { syntheticsMonitor, type SyntheticsMonitorDocument } from './src/lib/synthetics'; +export { otel, type OtelDocument } from './src/lib/otel'; diff --git a/packages/kbn-apm-synthtrace-client/src/lib/otel/error.ts b/packages/kbn-apm-synthtrace-client/src/lib/otel/error.ts new file mode 100644 index 00000000000000..63265d45fe8862 --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/otel/error.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { OtelDocument } from '../../..'; +import { Serializable } from '../serializable'; + +export interface OtelErrorDocument extends OtelDocument { + 'event.name'?: string; + attributes?: { + 'exception.message'?: string; + 'error.stack_trace'?: string; + 'exception.handled'?: boolean; + 'exception.type'?: string; + 'processor.event'?: string; + 'timestamp.us'?: number; + 'event.name'?: string; + 'error.id'?: string; + }; +} + +export class OtelError extends Serializable { + constructor(fields: OtelErrorDocument) { + super({ + ...fields, + }); + } +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/otel/index.ts b/packages/kbn-apm-synthtrace-client/src/lib/otel/index.ts new file mode 100644 index 00000000000000..86bb74dd94ff4b --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/otel/index.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Fields } from '../entity'; +import { Serializable } from '../serializable'; +import { OtelError } from './error'; +import { OtelMetric } from './metric'; +import { OtelTransaction } from './transaction'; + +interface OtelSharedResourceAttributes { + 'service.name'?: string; + 'agent.name'?: string; + 'agent.version'?: string; + 'metricset.interval'?: string; + 'service.instance.id'?: string; + 'telemetry.sdk.language'?: string; + 'telemetry.sdk.name'?: string; + 'telemetry.sdk.version'?: string; + 'some.resource.attribute'?: string; +} + +export interface OtelDocument extends Fields { + data_stream?: { + dataset: string; + namespace: string; + type: string; + }; + attributes?: { + 'timestamp.us'?: number; + 'metricset.name'?: string; + [key: string]: any; + }; + resource?: { + attributes?: OtelSharedResourceAttributes; + dropped_attributes_count?: number; + schema_url?: string; + }; + scope?: { + attributes?: { + 'service.framework.name'?: string; + 'service.framework.version'?: string; + }; + dropped_attributes_count?: number; + name?: string; + }; + name?: string; + trace_id?: string; + trace?: { id: string }; + span_id?: string; + span?: { id: string }; + dropped_attributes_count?: number; + dropped_events_count?: number; + dropped_links_count?: number; + timestamp_us?: number; +} + +class Otel extends Serializable { + constructor(fields: OtelDocument) { + super({ + ...fields, + }); + } + + error(spanId: string) { + return new OtelError({ + ...this.fields, + attributes: { + 'exception.message': 'boom', + 'exception.handled': false, + 'exception.type': '*errors.errorString', + 'error.stack_trace': 'Error: INTERNAL: Boom', + 'processor.event': 'error', + 'timestamp.us': 1726580752010657, + 'event.name': 'exception', + 'error.id': `error-${spanId}`, + }, + data_stream: { + dataset: 'generic.otel', + namespace: 'default', + type: 'logs', + }, + 'event.name': 'exception', + dropped_attributes_count: 0, + resource: { + attributes: { + 'agent.name': 'opentelemetry/go', + 'agent.version': '1.28.0', + 'service.name': 'sendotlp-synth', + 'service.instance.id': '89117ac1-0dbf-4488-9e17-4c2c3b76943a', + }, + dropped_attributes_count: 0, + schema_url: 'https://opentelemetry.io/schemas/1.26.0', + }, + scope: { + attributes: { + 'service.framework.name': 'sendotlp-synth', + 'service.framework.version': '', + }, + dropped_attributes_count: 0, + name: 'sendotlp-synth', + }, + span_id: spanId, + }); + } + + metric() { + return new OtelMetric({ + ...this.fields, + attributes: { + 'metricset.name': 'service_destination', + 'processor.event': 'metric', + 'event.outcome': 'success', + 'service.target.name': 'foo_service', + 'service.target.type': 'http', + 'span.name': 'child1', + 'span.destination.service.resource': 'foo_service:8080', + }, + data_stream: { + dataset: 'service_destination.10m.otel', + namespace: 'default', + type: 'metrics', + }, + metrics: { + service_summary: 2, + }, + resource: { + attributes: { + 'agent.name': 'otlp', + 'agent.version': '1.28.0', + 'service.instance.id': '89117ac1-0dbf-4488-9e17-4c2c3b76943a', + 'service.name': 'sendotlp-synth', + 'metricset.interval': '10m', + }, + dropped_attributes_count: 0, + }, + scope: { + dropped_attributes_count: 0, + name: 'github.com/elastic/opentelemetry-collector-components/connector/spanmetricsconnectorv2', + }, + }); + } + + // In Otel we have only spans (https://opentelemetry.io/docs/concepts/signals/traces/#spans) + // we call the root span a transaction to match our data model + transaction(id: string) { + return new OtelTransaction({ + ...this.fields, + attributes: { + 'event.outcome': 'success', + 'event.success_count': 1, + 'processor.event': 'transaction', + 'timestamp.us': 1726580752010657, + 'transaction.duration.us': 15202, + 'transaction.id': id, + 'transaction.name': 'parent-synth', + 'transaction.representative_count': 1, + 'transaction.result': 'HTTP 2xx', + 'transaction.root': true, + 'transaction.sampled': true, + 'transaction.type': 'unknown', + }, + data_stream: { + dataset: 'generic.otel', + namespace: 'default', + type: 'traces', + }, + duration: 11742370, + kind: 'Internal', + name: 'parent-synth', + resource: { + attributes: { + 'agent.name': 'otlp', + 'agent.version': '1.28.0', + 'service.instance.id': '89117ac1-0dbf-4488-9e17-4c2c3b76943a', + 'service.name': 'sendotlp-synth', + }, + dropped_attributes_count: 0, + schema_url: 'https://opentelemetry.io/schemas/1.26.0', + }, + scope: { + attributes: { + 'service.framework.name': 'sendotlp-synth', + 'service.framework.version': '', + }, + dropped_attributes_count: 0, + name: 'sendotlp-synth', + }, + span_id: id, + status: { + code: 'Unset', + }, + }); + } +} + +export function create(id: string): Otel { + return new Otel({ + trace_id: id, + dropped_attributes_count: 0, + dropped_events_count: 0, + dropped_links_count: 0, + }); +} + +export const otel = { + create, +}; diff --git a/packages/kbn-apm-synthtrace-client/src/lib/otel/metric.ts b/packages/kbn-apm-synthtrace-client/src/lib/otel/metric.ts new file mode 100644 index 00000000000000..2f238b36c5acae --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/otel/metric.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { OtelDocument } from '.'; +import { Serializable } from '../serializable'; + +export interface OtelMetricDocument extends OtelDocument { + attributes?: { + 'metricset.name'?: string; + 'processor.event'?: string; + 'event.outcome'?: string; + 'service.target.name'?: string; + 'service.target.type'?: string; + 'span.name'?: string; + 'span.destination.service.resource'?: string; + }; + metrics?: { + service_summary?: number; + }; +} +export class OtelMetric extends Serializable { + constructor(fields: OtelMetricDocument) { + super({ + ...fields, + }); + } +} diff --git a/packages/kbn-apm-synthtrace-client/src/lib/otel/transaction.ts b/packages/kbn-apm-synthtrace-client/src/lib/otel/transaction.ts new file mode 100644 index 00000000000000..d6f7c7111b1872 --- /dev/null +++ b/packages/kbn-apm-synthtrace-client/src/lib/otel/transaction.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { OtelDocument } from '.'; +import { Serializable } from '../serializable'; + +export interface OtelTransactionDocument extends OtelDocument { + attributes?: { + 'event.outcome'?: string; + 'event.success_count'?: number; + 'processor.event'?: string; + 'timestamp.us'?: number; + 'transaction.duration.us'?: number; + 'transaction.id'?: string; + 'transaction.name'?: string; + 'transaction.representative_count'?: number; + 'transaction.result'?: string; + 'transaction.root'?: boolean; + 'transaction.sampled'?: boolean; + 'transaction.type'?: string; + }; + status?: { + code?: string; + }; + dropped_events_count?: number; + dropped_links_count?: number; + duration?: number; + kind?: string; + name?: string; +} + +export class OtelTransaction extends Serializable { + constructor(fields: OtelTransactionDocument) { + super({ + ...fields, + }); + } +} diff --git a/packages/kbn-apm-synthtrace/index.ts b/packages/kbn-apm-synthtrace/index.ts index b717b9a45af998..ebd35da3aa19e5 100644 --- a/packages/kbn-apm-synthtrace/index.ts +++ b/packages/kbn-apm-synthtrace/index.ts @@ -17,6 +17,7 @@ export { MonitoringSynthtraceEsClient } from './src/lib/monitoring/monitoring_sy export { LogsSynthtraceEsClient } from './src/lib/logs/logs_synthtrace_es_client'; export { AssetsSynthtraceEsClient } from './src/lib/assets/assets_synthtrace_es_client'; export { SyntheticsSynthtraceEsClient } from './src/lib/synthetics/synthetics_synthtrace_es_client'; +export { OtelSynthtraceEsClient } from './src/lib/otel/otel_synthtrace_es_client'; export { addObserverVersionTransform, deleteSummaryFieldTransform, diff --git a/packages/kbn-apm-synthtrace/src/cli/scenario.ts b/packages/kbn-apm-synthtrace/src/cli/scenario.ts index 85b0ee56fc9e80..4f1550b8bdbc8a 100644 --- a/packages/kbn-apm-synthtrace/src/cli/scenario.ts +++ b/packages/kbn-apm-synthtrace/src/cli/scenario.ts @@ -13,6 +13,7 @@ import { InfraSynthtraceEsClient, LogsSynthtraceEsClient, SyntheticsSynthtraceEsClient, + OtelSynthtraceEsClient, } from '../..'; import { AssetsSynthtraceEsClient } from '../lib/assets/assets_synthtrace_es_client'; import { Logger } from '../lib/utils/create_logger'; @@ -25,6 +26,7 @@ interface EsClients { infraEsClient: InfraSynthtraceEsClient; assetsEsClient: AssetsSynthtraceEsClient; syntheticsEsClient: SyntheticsSynthtraceEsClient; + otelEsClient: OtelSynthtraceEsClient; } type Generate = (options: { diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/bootstrap.ts b/packages/kbn-apm-synthtrace/src/cli/utils/bootstrap.ts index 6c6b065dabfc7c..22d07f73c56cbc 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/bootstrap.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/bootstrap.ts @@ -16,6 +16,7 @@ import { getServiceUrls } from './get_service_urls'; import { RunOptions } from './parse_run_cli_flags'; import { getAssetsEsClient } from './get_assets_es_client'; import { getSyntheticsEsClient } from './get_synthetics_es_client'; +import { getOtelSynthtraceEsClient } from './get_otel_es_client'; export async function bootstrap(runOptions: RunOptions) { const logger = createLogger(runOptions.logLevel); @@ -68,6 +69,11 @@ export async function bootstrap(runOptions: RunOptions) { logger, concurrency: runOptions.concurrency, }); + const otelEsClient = getOtelSynthtraceEsClient({ + target: esUrl, + logger, + concurrency: runOptions.concurrency, + }); if (runOptions.clean) { await apmEsClient.clean(); @@ -75,6 +81,7 @@ export async function bootstrap(runOptions: RunOptions) { await infraEsClient.clean(); await assetsEsClient.clean(); await syntheticsEsClient.clean(); + await otelEsClient.clean(); } return { @@ -84,6 +91,7 @@ export async function bootstrap(runOptions: RunOptions) { infraEsClient, assetsEsClient, syntheticsEsClient, + otelEsClient, version, kibanaUrl, esUrl, diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/get_otel_es_client.ts b/packages/kbn-apm-synthtrace/src/cli/utils/get_otel_es_client.ts new file mode 100644 index 00000000000000..0671ea66c472f7 --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/cli/utils/get_otel_es_client.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Client } from '@elastic/elasticsearch'; +import { Logger } from '../../lib/utils/create_logger'; +import { RunOptions } from './parse_run_cli_flags'; +import { getEsClientTlsSettings } from './ssl'; +import { OtelSynthtraceEsClient } from '../../lib/otel/otel_synthtrace_es_client'; + +export function getOtelSynthtraceEsClient({ + target, + logger, + concurrency, +}: Pick & { + target: string; + logger: Logger; +}) { + const client = new Client({ + node: target, + tls: getEsClientTlsSettings(target), + }); + + return new OtelSynthtraceEsClient({ + client, + logger, + concurrency, + }); +} diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/start_live_data_upload.ts b/packages/kbn-apm-synthtrace/src/cli/utils/start_live_data_upload.ts index 90fa0189469add..79c9907dc13d12 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/start_live_data_upload.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/start_live_data_upload.ts @@ -26,8 +26,15 @@ export async function startLiveDataUpload({ }) { const file = runOptions.file; - const { logger, apmEsClient, logsEsClient, infraEsClient, assetsEsClient, syntheticsEsClient } = - await bootstrap(runOptions); + const { + logger, + apmEsClient, + logsEsClient, + infraEsClient, + assetsEsClient, + syntheticsEsClient, + otelEsClient, + } = await bootstrap(runOptions); const scenario = await getScenario({ file, logger }); const { generate } = await scenario({ ...runOptions, logger }); @@ -65,7 +72,14 @@ export async function startLiveDataUpload({ const generatorsAndClients = generate({ range: timerange(bucketFrom.getTime(), bucketTo.getTime()), - clients: { logsEsClient, apmEsClient, infraEsClient, assetsEsClient, syntheticsEsClient }, + clients: { + logsEsClient, + apmEsClient, + infraEsClient, + assetsEsClient, + syntheticsEsClient, + otelEsClient, + }, }); const generatorsAndClientsArray = castArray(generatorsAndClients); diff --git a/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts b/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts index a5defe4b6e1b48..78c89d110c8921 100644 --- a/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts +++ b/packages/kbn-apm-synthtrace/src/cli/utils/synthtrace_worker.ts @@ -20,6 +20,7 @@ import { getLogsEsClient } from './get_logs_es_client'; import { getInfraEsClient } from './get_infra_es_client'; import { getAssetsEsClient } from './get_assets_es_client'; import { getSyntheticsEsClient } from './get_synthetics_es_client'; +import { getOtelSynthtraceEsClient } from './get_otel_es_client'; export interface WorkerData { bucketFrom: Date; @@ -65,6 +66,12 @@ async function start() { logger, }); + const otelEsClient = getOtelSynthtraceEsClient({ + concurrency: runOptions.concurrency, + target: esUrl, + logger, + }); + const file = runOptions.file; const scenario = await logger.perf('get_scenario', () => getScenario({ file, logger })); @@ -80,6 +87,7 @@ async function start() { infraEsClient, assetsEsClient, syntheticsEsClient, + otelEsClient, }); } @@ -88,7 +96,14 @@ async function start() { const generatorsAndClients = logger.perf('generate_scenario', () => generate({ range: timerange(bucketFrom, bucketTo), - clients: { logsEsClient, apmEsClient, infraEsClient, assetsEsClient, syntheticsEsClient }, + clients: { + logsEsClient, + apmEsClient, + infraEsClient, + assetsEsClient, + syntheticsEsClient, + otelEsClient, + }, }) ); diff --git a/packages/kbn-apm-synthtrace/src/lib/otel/otel_synthtrace_es_client.ts b/packages/kbn-apm-synthtrace/src/lib/otel/otel_synthtrace_es_client.ts new file mode 100644 index 00000000000000..e2162925e3c72c --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/lib/otel/otel_synthtrace_es_client.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { Client } from '@elastic/elasticsearch'; +import { ESDocumentWithOperation } from '@kbn/apm-synthtrace-client'; +import { OtelDocument } from '@kbn/apm-synthtrace-client'; +import { pipeline, Readable, Transform } from 'stream'; +import { SynthtraceEsClient, SynthtraceEsClientOptions } from '../shared/base_client'; +import { getDedotTransform } from '../shared/get_dedot_transform'; +import { getSerializeTransform } from '../shared/get_serialize_transform'; +import { Logger } from '../utils/create_logger'; + +export type OtelSynthtraceEsClientOptions = Omit; + +export class OtelSynthtraceEsClient extends SynthtraceEsClient { + constructor(options: { client: Client; logger: Logger } & OtelSynthtraceEsClientOptions) { + super({ + ...options, + pipeline: otelPipeline(), + }); + this.dataStreams = ['metrics-generic.otel*', 'traces-generic.otel*', 'logs-generic.otel*']; + } +} + +function otelPipeline() { + return (base: Readable) => { + return pipeline( + base, + getSerializeTransform(), + getRoutingTransform(), + getDedotTransform(), + (err: unknown) => { + if (err) { + throw err; + } + } + ); + }; +} + +export function getRoutingTransform() { + return new Transform({ + objectMode: true, + transform(document: ESDocumentWithOperation, encoding, callback) { + const namespace = 'default'; + let index: string | undefined; + + switch (document?.attributes?.['processor.event']) { + case 'transaction': + case 'span': + index = `traces-generic.otel-${namespace}-synth`; + break; + + case 'error': + index = `logs-generic.otel-${namespace}-synth`; + break; + + case 'metric': + const metricsetName = document?.attributes?.['metricset.name']; + if ( + metricsetName === 'transaction' || + metricsetName === 'service_transaction' || + metricsetName === 'service_destination' || + metricsetName === 'service_summary' + ) { + index = `metrics-generic.otel.${metricsetName}.${document.attributes[ + 'metricset.interval' + ]!}-${namespace}-synth`; + } else { + index = `metrics-generic.otel.internal-${namespace}-synth`; + } + break; + default: + if (document?.attributes?.['event.action'] != null) { + index = `logs-generic.otel-${namespace}-synth`; + } + break; + } + + if (!index) { + const error = new Error('Cannot determine index for event'); + Object.assign(error, { document }); + } + + document._index = index; + + callback(null, document); + }, + }); +} diff --git a/packages/kbn-apm-synthtrace/src/scenarios/otel_simple_trace.ts b/packages/kbn-apm-synthtrace/src/scenarios/otel_simple_trace.ts new file mode 100644 index 00000000000000..7721a8651905fa --- /dev/null +++ b/packages/kbn-apm-synthtrace/src/scenarios/otel_simple_trace.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { otel, generateShortId, OtelDocument } from '@kbn/apm-synthtrace-client'; +import { times } from 'lodash'; +import { Scenario } from '../cli/scenario'; +import { withClient } from '../lib/utils/with_client'; + +const scenario: Scenario = async (runOptions) => { + return { + generate: ({ range, clients: { otelEsClient } }) => { + const { numOtelTraces = 5 } = runOptions.scenarioOpts || {}; + const { logger } = runOptions; + const traceId = generateShortId(); + const spanId = generateShortId(); + + const otelDocs = times(numOtelTraces / 2).map((index) => otel.create(traceId)); + + const otelWithMetricsAndErrors = range + .interval('30s') + .rate(1) + .generator((timestamp) => + otelDocs.flatMap((oteld) => { + return [ + oteld.metric().timestamp(timestamp), + oteld.transaction(spanId).timestamp(timestamp), + oteld.error(spanId).timestamp(timestamp), + ]; + }) + ); + + return [ + withClient( + otelEsClient, + logger.perf('generating_otel_trace', () => otelWithMetricsAndErrors) + ), + ]; + }, + }; +}; + +export default scenario; From ebe16fa467d6e35e2398ad724780db05c27779cf Mon Sep 17 00:00:00 2001 From: Jordan <51442161+JordanSh@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:35:25 +0300 Subject: [PATCH 32/38] [Cloud Posture] Adding CSP 3P support callout for Wiz integration (#196025) --- .github/CODEOWNERS | 1 + ...sture_third_party_support_callout.test.tsx | 48 +++++++++++++++++++ ...ud_posture_third_party_support_callout.tsx | 42 ++++++++++++++++ .../epm/screens/detail/overview/overview.tsx | 3 ++ 4 files changed, 94 insertions(+) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.test.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.tsx diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a8cc39b7b072b9..bf39e9ea13567f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1842,6 +1842,7 @@ x-pack/plugins/osquery @elastic/security-defend-workflows /x-pack/plugins/fleet/public/components/cloud_security_posture @elastic/fleet @elastic/kibana-cloud-security-posture /x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture @elastic/fleet @elastic/kibana-cloud-security-posture /x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.* @elastic/fleet @elastic/kibana-cloud-security-posture +/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.* @elastic/fleet @elastic/kibana-cloud-security-posture /x-pack/plugins/security_solution/public/cloud_security_posture @elastic/kibana-cloud-security-posture /x-pack/test/security_solution_cypress/cypress/e2e/explore/hosts/vulnerabilities_contextual_flyout.cy.ts @elastic/kibana-cloud-security-posture diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.test.tsx new file mode 100644 index 00000000000000..7b238ef49fa2e5 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.test.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 { render, screen } from '@testing-library/react'; + +import useLocalStorage from 'react-use/lib/useLocalStorage'; + +import type { PackageInfo } from '../../../../../../../../common'; + +import { CloudPostureThirdPartySupportCallout } from './cloud_posture_third_party_support_callout'; + +jest.mock('react-use/lib/useLocalStorage'); + +describe('CloudPostureThirdPartySupportCallout', () => { + const mockPackageInfo = { name: 'wiz' } as PackageInfo; + + beforeEach(() => { + (useLocalStorage as jest.Mock).mockClear(); + }); + + it('renders callout when package is wiz and callout is not dismissed', () => { + (useLocalStorage as jest.Mock).mockReturnValue([false, jest.fn()]); + + render(); + + expect(screen.getByText(/New! Starting from version 1.9/)).toBeInTheDocument(); + }); + + it('does not render callout when package is not wiz', () => { + const nonWizPackageInfo = { name: 'other' } as PackageInfo; + render(); + + expect(screen.queryByText(/New! Starting from version 1.9/)).not.toBeInTheDocument(); + }); + + it('does not render callout when it has been dismissed', () => { + (useLocalStorage as jest.Mock).mockReturnValue([true, jest.fn()]); + + render(); + + expect(screen.queryByText(/New! Starting from version 1.9/)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.tsx new file mode 100644 index 00000000000000..6bd4197dc267e3 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/components/cloud_posture_third_party_support_callout.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 useLocalStorage from 'react-use/lib/useLocalStorage'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import type { PackageInfo } from '../../../../../../../../common'; + +const LS_CLOUD_POSTURE_3P_SUPPORT_WIZ_INTEGRATIONS_CALLOUT_KEY = + 'fleet:cloudSecurityPosture:thirdPartySupport:wizIntegrationsCallout'; + +export const CloudPostureThirdPartySupportCallout = ({ + packageInfo, +}: { + packageInfo: PackageInfo; +}) => { + const [userHasDismissedWizCallout, setUserHasDismissedWizCallout] = useLocalStorage( + LS_CLOUD_POSTURE_3P_SUPPORT_WIZ_INTEGRATIONS_CALLOUT_KEY + ); + + if (packageInfo.name !== 'wiz' || userHasDismissedWizCallout) return null; + + return ( + <> + setUserHasDismissedWizCallout(true)} + iconType="cheer" + title={i18n.translate('xpack.fleet.epm.wizIntegration.newFeaturesCallout', { + defaultMessage: + 'New! Starting from version 1.9, ingest vulnerability and misconfiguration findings from Wiz into Elastic. Leverage out-of-the-box contextual investigation and threat-hunting workflows.', + })} + /> + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx index 9fa6df1e9f8ca1..e96e74b1bb96c8 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/overview/overview.tsx @@ -42,6 +42,8 @@ import { SideBarColumn } from '../../../components/side_bar_column'; import type { FleetStartServices } from '../../../../../../../plugin'; +import { CloudPostureThirdPartySupportCallout } from '../components/cloud_posture_third_party_support_callout'; + import { Screenshots } from './screenshots'; import { Readme } from './readme'; import { Details } from './details'; @@ -319,6 +321,7 @@ export const OverviewPage: React.FC = memo( )} + {isPrerelease && ( Date: Mon, 14 Oct 2024 14:58:15 +0200 Subject: [PATCH 33/38] [EDR Workflows] Fix navigation not opening on Response Console view (#195907) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Response Console is a full-size overlay rather than a modal or page. In the Serverless environment, we need to account for the space taken up by the navigation on the left when it’s open as well as closed. To accommodate this, we’ll subtract the navigation width (defined by the `--euiCollapsibleNavOffset` variable) from the overlay width and apply it as a left offset. The `--euiCollapsibleNavOffset` variable is only declared for Serverless, so these changes will not affect ESS, where navigation is displayed as a popup rather than being integrated into the page layout. With these changes: Serverless: https://github.com/user-attachments/assets/81955598-6ff1-4856-af08-ca5e9548a85c ESS: https://github.com/user-attachments/assets/0bec1e24-58f6-45be-b846-feece89ecb63 --- .../management/components/page_overlay/page_overlay.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx b/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx index 966e7aab6d833b..570c250057f34f 100644 --- a/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx +++ b/x-pack/plugins/security_solution/public/management/components/page_overlay/page_overlay.tsx @@ -27,9 +27,12 @@ const OverlayRootContainer = styled.div` top: var(--euiFixedHeadersOffset, 0); bottom: 0; right: 0; + left: var(--euiCollapsibleNavOffset, 0); + width: calc(100% - var(--euiCollapsibleNavOffset, 0)); height: calc(100% - var(--euiFixedHeadersOffset, 0)); - width: 100%; + + border-left: 1px solid ${({ theme: { eui } }) => eui.euiColorLightestShade}; z-index: ${({ theme: { eui } }) => eui.euiZFlyout + From 500ac5b195b3972be465b9a8f9fa68c7054b5b54 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 14 Oct 2024 06:28:32 -0700 Subject: [PATCH 34/38] [dependency update] update reflect-metadata version requirment (#196020) Updates dependency from `^0.2.1` to `^0.2.2` --------- Co-authored-by: Elastic Machine --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 22a3f88bfe03e0..339468af2dfcc0 100644 --- a/package.json +++ b/package.json @@ -1240,7 +1240,7 @@ "redux-saga-testing": "^2.0.2", "redux-thunk": "^2.4.2", "redux-thunks": "^1.0.0", - "reflect-metadata": "^0.2.1", + "reflect-metadata": "^0.2.2", "remark-gfm": "1.0.0", "remark-parse-no-trim": "^8.0.4", "remark-stringify": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index 4f2c4b5435749e..56007a1094ecd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27531,10 +27531,10 @@ redux@^4.0.0, redux@^4.0.4, redux@^4.2.1: dependencies: "@babel/runtime" "^7.9.2" -reflect-metadata@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.1.tgz#8d5513c0f5ef2b4b9c3865287f3c0940c1f67f74" - integrity sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw== +reflect-metadata@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== refractor@^3.2.0, refractor@^3.6.0: version "3.6.0" From 4ef2a3f1c0c5fb64c765dd41fbb7caef799ebdff Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 14 Oct 2024 16:48:34 +0300 Subject: [PATCH 35/38] fix: (Accessibility) Map visualization Layers palette has duplicated IDs (#195577) Closes: #151925 ## Description Map visualization Layers palette has duplicated IDs ## Steps to reproduce 1. Load the Flights sample data set into Kibana 2. Navigate to the Dashboard view 3. Run an axe scan using the [Axe browser plugin](https://deque.com/axe/) (Chrome, Edge, or Firefox required) 4. Under the `Minor` issue type there should be 3 "ID attribute values must be unique" errors ## What was changed?: 1. Removed unnecessary id's attributes. ## Screen: image --- .../toc_entry/__snapshots__/toc_entry.test.tsx.snap | 6 ------ .../layer_control/layer_toc/toc_entry/toc_entry.tsx | 7 +------ .../__snapshots__/toc_entry_actions_popover.test.tsx.snap | 6 ------ .../toc_entry_actions_popover.tsx | 1 - 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap index 63edb4bff381c8..daceee4ac6aabc 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap @@ -4,7 +4,6 @@ exports[`TOCEntry is rendered 1`] = `
{ this.props.depth > 0 ? { paddingLeft: `${8 + this.props.depth * 24}px` } : {}; return ( -
+
{this._renderLayerHeader()} {this.props.isLegendDetailsOpen && diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap index 47171e8bbe871b..56ecd504f74657 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap @@ -39,7 +39,6 @@ exports[`TOCEntryActionsPopover is rendered 1`] = ` closePopover={[Function]} display="inline-block" hasArrow={true} - id="testLayer" isOpen={false} ownFocus={true} panelPaddingSize="none" @@ -154,7 +153,6 @@ exports[`TOCEntryActionsPopover should disable Edit features when edit mode acti closePopover={[Function]} display="inline-block" hasArrow={true} - id="testLayer" isOpen={false} ownFocus={true} panelPaddingSize="none" @@ -269,7 +267,6 @@ exports[`TOCEntryActionsPopover should disable fit to data when supportsFitToBou closePopover={[Function]} display="inline-block" hasArrow={true} - id="testLayer" isOpen={false} ownFocus={true} panelPaddingSize="none" @@ -385,7 +382,6 @@ exports[`TOCEntryActionsPopover should have "show layer" action when layer is no closePopover={[Function]} display="inline-block" hasArrow={true} - id="testLayer" isOpen={false} ownFocus={true} panelPaddingSize="none" @@ -500,7 +496,6 @@ exports[`TOCEntryActionsPopover should not show edit actions in read only mode 1 closePopover={[Function]} display="inline-block" hasArrow={true} - id="testLayer" isOpen={false} ownFocus={true} panelPaddingSize="none" @@ -584,7 +579,6 @@ exports[`TOCEntryActionsPopover should show "show this layer only" action when t closePopover={[Function]} display="inline-block" hasArrow={true} - id="testLayer" isOpen={false} ownFocus={true} panelPaddingSize="none" diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx index a6d83f809653d2..7e928fa9ce7b10 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx @@ -269,7 +269,6 @@ export class TOCEntryActionsPopover extends Component { <> {removeModal} Date: Mon, 14 Oct 2024 15:53:21 +0200 Subject: [PATCH 36/38] [DOCS] Search landing page updates (#196131) --- docs/landing-page.asciidoc | 4 ++-- docs/search/index.asciidoc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/landing-page.asciidoc b/docs/landing-page.asciidoc index 8d806d574129a5..b7fb7435017a80 100644 --- a/docs/landing-page.asciidoc +++ b/docs/landing-page.asciidoc @@ -248,11 +248,11 @@
- +

- Enterprise Search + Search

Create search experiences for your content, wherever it lives.

diff --git a/docs/search/index.asciidoc b/docs/search/index.asciidoc index ab4b007800da43..4d2ee436327b66 100644 --- a/docs/search/index.asciidoc +++ b/docs/search/index.asciidoc @@ -2,7 +2,7 @@ [[search-space]] = Search -The *Search* space in {kib} comprises the following features: +The *Search* space in the {kib} UI contains the following GUI features: * https://www.elastic.co/guide/en/enterprise-search/current/connectors.html[Connectors] * https://www.elastic.co/guide/en/enterprise-search/current/crawler.html[Web crawler] @@ -11,7 +11,7 @@ The *Search* space in {kib} comprises the following features: * https://www.elastic.co/guide/en/elasticsearch/reference/current/behavioral-analytics-overview.html[Behavioral Analytics] * <> * <> -* Persistent Dev Tools <> +* Dev Tools <> [float] [[search-release-notes]] From 8fe292f3ca143266309fd7acda47d58ea367a07c Mon Sep 17 00:00:00 2001 From: Ido Cohen <90558359+CohenIdo@users.noreply.github.com> Date: Mon, 14 Oct 2024 17:04:17 +0300 Subject: [PATCH 37/38] Fix vuln flyout when score is zero --- .../vulnerability_overview_tab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx index ed9d7f985f357f..327ce2f94759d9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_finding_flyout/vulnerability_overview_tab.tsx @@ -161,7 +161,7 @@ const VulnerabilityOverviewTiles = ({ vulnerabilityRecord }: VulnerabilityTabPro return ( - {vulnerability?.score?.base && ( + {!!vulnerability?.score?.base && ( Date: Mon, 14 Oct 2024 15:06:33 +0100 Subject: [PATCH 38/38] [ObsUX][Synthtrace] Fallback to latest GA package version if latest prerelease fetch fails (#195889) Closes https://github.com/elastic/kibana/issues/195436 --- .../client/apm_synthtrace_kibana_client.ts | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts index 9533ded24dec54..307f43932e94b4 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts @@ -32,24 +32,40 @@ export class ApmSynthtraceKibanaClient { async fetchLatestApmPackageVersion() { this.logger.debug(`Fetching latest APM package version`); - const url = `${this.getFleetApmPackagePath()}?prerelease=true`; - const response = await fetch(url, { - method: 'GET', - headers: kibanaHeaders(), - agent: getFetchAgent(url), - }); - const responseJson = await response.json(); + const fetchPackageVersion = async ({ prerelease }: { prerelease: boolean }) => { + const url = `${this.getFleetApmPackagePath()}?prerelease=${prerelease}`; + this.logger.debug(`Fetching from URL: ${url}`); - if (response.status !== 200) { - throw new Error( - `Failed to fetch latest APM package version, received HTTP ${response.status} and message: ${responseJson.message}` - ); - } + const response = await fetch(url, { + method: 'GET', + headers: kibanaHeaders(), + agent: getFetchAgent(url), + }); + + const responseJson = await response.json(); - const { latestVersion } = responseJson.item; + if (response.status !== 200) { + throw new Error( + `Failed to fetch APM package version, received HTTP ${response.status} and message: ${responseJson.message}` + ); + } + + return responseJson.item.latestVersion as string; + }; - return latestVersion as string; + try { + return await fetchPackageVersion({ prerelease: true }); + } catch (error) { + this.logger.debug( + 'Fetching latestes prerelease version failed, retrying with latest GA version' + ); + const retryResult = await fetchPackageVersion({ prerelease: false }).catch((retryError) => { + throw retryError; + }); + + return retryResult; + } } async installApmPackage(packageVersion?: string) {
; + return
; }; diff --git a/x-pack/plugins/ml/public/application/styles.ts b/x-pack/plugins/ml/public/application/styles.ts new file mode 100644 index 00000000000000..86f4dbe3bbcd46 --- /dev/null +++ b/x-pack/plugins/ml/public/application/styles.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. + */ + +// Replacement for ./_variables.scss as we aim to remove the scss files + +export const mlColors = { + critical: '#FE5050', + major: '#FBA740', + minor: '#FDEC25', + warning: '#8BC8FB', + lowWarning: '#D2E9F7', + unknown: '#C0C0C0', +}; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_index.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_index.scss deleted file mode 100644 index 236e50dc747bf9..00000000000000 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'timeseriesexplorer'; -@import 'timeseriesexplorer_annotations'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss deleted file mode 100644 index e47e69c741a90a..00000000000000 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer.scss +++ /dev/null @@ -1,267 +0,0 @@ -// stylelint-disable selector-no-qualifying-type -.ml-time-series-explorer { - color: $euiColorDarkShade; - - .forecast-controls { - float: right; - } - - .ml-timeseries-chart { - svg { - font-size: $euiFontSizeXS; - font-family: $euiFontFamily; - } - - .axis path, - .axis line { - fill: none; - stroke: $euiBorderColor; - shape-rendering: crispEdges; - pointer-events: none; - } - - .axis text { - fill: $euiTextColor; - } - - .axis .tick line { - stroke: $euiColorLightShade; - } - - .chart-border { - stroke: $euiBorderColor; - fill: none; - stroke-width: 1; - shape-rendering: crispEdges; - } - - .chart-border-highlight { - stroke: $euiColorDarkShade; - stroke-width: 2; - } - - .chart-border-highlight:hover { - opacity: 1; - } - - .area { - stroke-width: 1; - } - - .area.bounds { - fill: transparentize($euiColorPrimary, .8); - pointer-events: none; - } - - .values-line { - fill: none; - stroke: $euiColorPrimary; - stroke-width: 2; - pointer-events: none; - } - - .values-line.forecast { - stroke: $euiColorVis5; - pointer-events: none; - } - - .hidden { - visibility: hidden; - } - - .area.forecast { - fill: transparentize($euiColorVis5, .7); - pointer-events: none; - } - - .values-dots circle { - fill: $euiColorPrimary; - stroke-width: 0; - } - - .metric-value { - opacity: 1; - fill: transparent; - stroke: $euiColorPrimary; - stroke-width: 0; - } - - .anomaly-marker { - stroke-width: 1px; - stroke: $euiColorMediumShade; - } - - .anomaly-marker.critical { - fill: $mlColorCritical; - } - - .anomaly-marker.major { - fill: $mlColorMajor; - } - - .anomaly-marker.minor { - fill: $mlColorMinor; - } - - .anomaly-marker.warning { - fill: $mlColorWarning; - } - - .anomaly-marker.low { - fill: $mlColorLowWarning; - } - - .metric-value:hover, - .anomaly-marker:hover, - .anomaly-marker.highlighted { - stroke-width: 6px; - stroke-opacity: .65; - stroke: $euiColorPrimary; - } - - rect.scheduled-event-marker { - stroke-width: 1px; - stroke: $euiColorDarkShade; - fill: $euiColorLightShade; - } - - .forecast { - .metric-value, - .metric-value:hover { - stroke: $euiColorVis5; - } - } - - .focus-chart { - .x-axis-background { - line { - fill: none; - shape-rendering: crispEdges; - stroke: $euiColorLightestShade; - } - - rect { - fill: $euiColorLightestShade; - } - } - - .focus-zoom { - fill: $euiColorDarkShade; - - a { - text { - fill: $euiColorPrimary; - cursor: pointer; - } - } - - a:hover, - a:active, - a:focus { - text-decoration: underline; - } - } - } - - .context-chart { - .x.axis path { - display: none; - } - - .axis text { - font-size: 10px; - fill: $euiTextColor; - } - - .values-line { - stroke-width: 1; - } - - .mask { - polygon { - fill-opacity: .1; - } - - .area.bounds { - fill: $euiColorLightShade; - } - - .values-line { - stroke-width: 1; - stroke: $euiColorMediumShade; - } - } - } - - .swimlane .axis text { - display: none; - } - - .swimlane rect.swimlane-cell-hidden { - display: none; - } - - .brush .extent { - fill-opacity: 0; - shape-rendering: crispEdges; - stroke: $euiColorDarkShade; - stroke-width: 2; - cursor: move; - } - - .brush .extent:hover { - opacity: 1; - } - - .top-border { - fill: $euiColorEmptyShade; - } - - foreignObject.brush-handle { - pointer-events: none; - padding-top: 1px; - } - - div.brush-handle-inner { - border: 1px solid $euiColorDarkShade; - background-color: $euiColorLightShade; - height: 70px; - width: 10px; - text-align: center; - cursor: ew-resize; - margin-top: 9px; - font-size: $euiFontSizeS; - fill: $euiColorDarkShade; - } - - div.brush-handle-inner-left { - border-radius: $euiBorderRadius 0 0 $euiBorderRadius; - } - - div.brush-handle-inner-right { - border-radius: 0 $euiBorderRadius $euiBorderRadius 0; - } - - rect.brush-handle { - stroke-width: 1; - stroke: $euiColorDarkShade; - fill: $euiColorLightShade; - pointer-events: none; - } - - rect.brush-handle:hover { - opacity: 1; - } - } -} - -/* Hides the progress bar's background so it doesn't look like it increases the thickness - of the horizontal bar below the tab menu elements in its inactive state. */ -.mlTimeSeriesExplorerProgress { - background-color: $euiColorEmptyShade; - - &::-moz-progress-bar, - &::-webkit-progress-bar { - background-color: $euiColorEmptyShade; - } -} diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss b/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss deleted file mode 100644 index 656f38590d3a5e..00000000000000 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/_timeseriesexplorer_annotations.scss +++ /dev/null @@ -1,103 +0,0 @@ -// SASS TODO: This uses non-BEM styles to be in line with the existing -// legacy Time Series Viewer style. Where applicable it tries to avoid -// overrides. The one override with `.extent` is because of d3. - -$mlAnnotationBorderWidth: 2px; - -// Replicates $euiBorderEditable for SVG -.mlAnnotationBrush .extent { - stroke: $euiColorLightShade; - stroke-width: $mlAnnotationBorderWidth; - stroke-dasharray: 2 2; - fill: $euiColorLightestShade; - shape-rendering: geometricPrecision; -} - -// Instead of different EUI colors we use opacity settings -// here to avoid opaque layers on top of existing chart elements. -$mlAnnotationRectDefaultStrokeOpacity: .2; -$mlAnnotationRectDefaultFillOpacity: .05; - -.mlAnnotationRect { - stroke: $euiColorFullShade; - stroke-width: $mlAnnotationBorderWidth; - stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity; - transition: stroke-opacity $euiAnimSpeedFast; - - fill: $euiColorFullShade; - fill-opacity: $mlAnnotationRectDefaultFillOpacity; - transition: fill-opacity $euiAnimSpeedFast; - - shape-rendering: geometricPrecision; -} - -.mlAnnotationRect-isHighlight { - stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity * 2; - transition: stroke-opacity $euiAnimSpeedFast; - - fill-opacity: $mlAnnotationRectDefaultFillOpacity * 2; - transition: fill-opacity $euiAnimSpeedFast; -} - -.mlAnnotationRect-isBlur { - stroke-opacity: calc($mlAnnotationRectDefaultStrokeOpacity / 2); - transition: stroke-opacity $euiAnimSpeedFast; - - fill-opacity: calc($mlAnnotationRectDefaultFillOpacity / 2); - transition: fill-opacity $euiAnimSpeedFast; -} - -// Replace the EuiBadge text style for SVG -.mlAnnotationText { - text-anchor: middle; - font-size: $euiFontSizeXS; - font-family: $euiFontFamily; - font-weight: $euiFontWeightMedium; - - fill: $euiColorFullShade; - transition: fill $euiAnimSpeedFast; - - user-select: none; - -} - -.mlAnnotationText-isBlur { - fill: $euiColorMediumShade; - transition: fill $euiAnimSpeedFast; -} - -.mlAnnotationTextRect { - fill: $euiColorLightShade; - transition: fill $euiAnimSpeedFast; -} - -.mlAnnotationTextRect-isBlur { - fill: $euiColorLightestShade; - transition: fill $euiAnimSpeedFast; -} - -.mlAnnotationHidden { - display: none; -} - -// context annotation marker -.mlContextAnnotationRect { - stroke: $euiColorFullShade; - stroke-width: $mlAnnotationBorderWidth; - stroke-opacity: $mlAnnotationRectDefaultStrokeOpacity; - transition: stroke-opacity $euiAnimSpeedFast; - - fill: $euiColorFullShade; - fill-opacity: $mlAnnotationRectDefaultFillOpacity; - transition: fill-opacity $euiAnimSpeedFast; - - shape-rendering: geometricPrecision; -} - -.mlContextAnnotationRect-isBlur { - stroke-opacity: calc($mlAnnotationRectDefaultStrokeOpacity / 2); - transition: stroke-opacity $euiAnimSpeedFast; - - fill-opacity: calc($mlAnnotationRectDefaultFillOpacity / 2); - transition: fill-opacity $euiAnimSpeedFast; -} diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 51abbf77dbebaf..d78ed1ed6e7fc0 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -307,7 +307,7 @@ class TimeseriesChartIntl extends Component { if (this.props.annotation === null) { const chartElement = d3.select(this.rootNode); - chartElement.select('g.mlAnnotationBrush').call(this.annotateBrush.extent([0, 0])); + chartElement.select('g.ml-annotation__brush').call(this.annotateBrush.extent([0, 0])); } } @@ -560,7 +560,7 @@ class TimeseriesChartIntl extends Component { fcsGroup .append('g') - .attr('class', 'mlAnnotationBrush') + .attr('class', 'ml-annotation__brush') .call(annotateBrush) .selectAll('rect') .attr('x', brushX) @@ -568,7 +568,7 @@ class TimeseriesChartIntl extends Component { .attr('width', brushWidth) .attr('height', focusChartIncoming ?? focusChartHeight); - fcsGroup.append('g').classed('mlAnnotations', true); + fcsGroup.append('g').classed('ml-annotations', true); // Add border round plot area. fcsGroup @@ -828,7 +828,7 @@ class TimeseriesChartIntl extends Component { // disable brushing (creation of annotations) when annotations aren't shown or when in embeddable mode focusChart - .select('.mlAnnotationBrush') + .select('.ml-annotation__brush') .style('display', !showAnnotations || embeddableMode ? 'none' : null); focusChart.select('.values-line').attr('d', this.focusValuesLine(data)); @@ -1245,18 +1245,18 @@ class TimeseriesChartIntl extends Component { drawLineChartDots(data, cxtGroup, contextValuesLine, 1); // Add annotation markers to the context area - cxtGroup.append('g').classed('mlContextAnnotations', true); + cxtGroup.append('g').classed('ml-annotation__context', true); const [contextXRangeStart, contextXRangeEnd] = this.contextXScale.range(); const ctxAnnotations = cxtGroup - .select('.mlContextAnnotations') - .selectAll('g.mlContextAnnotation') + .select('.ml-annotation__context') + .selectAll('g.ml-annotation__context-item') .data(mergedAnnotations, (d) => `${d.start}-${d.end}` || ''); - ctxAnnotations.enter().append('g').classed('mlContextAnnotation', true); + ctxAnnotations.enter().append('g').classed('ml-annotation__context-item', true); const ctxAnnotationRects = ctxAnnotations - .selectAll('.mlContextAnnotationRect') + .selectAll('.ml-annotation__context-rect') .data((d) => [d]); ctxAnnotationRects @@ -1266,7 +1266,7 @@ class TimeseriesChartIntl extends Component { showFocusChartTooltip(d.annotations.length === 1 ? d.annotations[0] : d, this); }) .on('mouseout', () => hideFocusChartTooltip()) - .classed('mlContextAnnotationRect', true); + .classed('ml-annotation__context-rect', true); ctxAnnotationRects .attr('x', (item) => { diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.ts index 2a104eadad1177..58dc3e84f27947 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart_annotations.ts @@ -145,18 +145,18 @@ export function renderAnnotations( }; const annotations = focusChart - .select('.mlAnnotations') - .selectAll('g.mlAnnotation') + .select('.ml-annotations') + .selectAll('g.ml-annotation') .data(focusAnnotationData || [], (d: Annotation) => d._id || ''); - annotations.enter().append('g').classed('mlAnnotation', true); + annotations.enter().append('g').classed('ml-annotation', true); - const rects = annotations.selectAll('.mlAnnotationRect').data((d: Annotation) => [d]); + const rects = annotations.selectAll('.ml-annotation__rect').data((d: Annotation) => [d]); rects .enter() .append('rect') - .classed('mlAnnotationRect', true) + .classed('ml-annotation__rect', true) .attr('mask', `url(#${ANNOTATION_MASK_ID})`) .on('mouseover', onAnnotationMouseOver) .on('mouseout', hideFocusChartTooltip) @@ -187,13 +187,13 @@ export function renderAnnotations( rects.exit().remove(); - const textRects = annotations.selectAll('.mlAnnotationTextRect').data((d) => [d]); - const texts = annotations.selectAll('.mlAnnotationText').data((d) => [d]); + const textRects = annotations.selectAll('.ml-annotation__text-rect').data((d) => [d]); + const texts = annotations.selectAll('.ml-annotation__text').data((d) => [d]); textRects .enter() .append('rect') - .classed('mlAnnotationTextRect', true) + .classed('ml-annotation__text-rect', true) .attr('width', ANNOTATION_TEXT_RECT_WIDTH) .attr('height', ANNOTATION_TEXT_RECT_HEIGHT) .on('mouseover', onAnnotationMouseOver) @@ -203,7 +203,7 @@ export function renderAnnotations( texts .enter() .append('text') - .classed('mlAnnotationText', true) + .classed('ml-annotation__text', true) .on('mouseover', onAnnotationMouseOver) .on('mouseout', hideFocusChartTooltip) .on('click', onAnnotationClick); @@ -253,7 +253,7 @@ export function renderAnnotations( textRects.exit().remove(); texts.exit().remove(); - annotations.classed('mlAnnotationHidden', !showAnnotations); + annotations.classed('ml-annotation--hidden', !showAnnotations); annotations.exit().remove(); } @@ -271,34 +271,36 @@ export function getAnnotationWidth( } export function highlightFocusChartAnnotation(annotation: Annotation) { - const annotations = d3.selectAll('.mlAnnotation'); + const annotations = d3.selectAll('.ml-annotation'); annotations.each(function (d) { // @ts-ignore const element = d3.select(this); if (d._id === annotation._id) { - element.selectAll('.mlAnnotationRect').classed('mlAnnotationRect-isHighlight', true); + element.selectAll('.ml-annotation__rect').classed('ml-annotation__rect--highlight', true); } else { - element.selectAll('.mlAnnotationTextRect').classed('mlAnnotationTextRect-isBlur', true); - element.selectAll('.mlAnnotationText').classed('mlAnnotationText-isBlur', true); - element.selectAll('.mlAnnotationRect').classed('mlAnnotationRect-isBlur', true); + element + .selectAll('.ml-annotation__text-rect') + .classed('ml-annotation__text-rect--blur', true); + element.selectAll('.ml-annotation__text').classed('ml-annotation__text--blur', true); + element.selectAll('.ml-annotation__rect').classed('ml-annotation__rect--blur', true); } }); } export function unhighlightFocusChartAnnotation() { - const annotations = d3.selectAll('.mlAnnotation'); + const annotations = d3.selectAll('.ml-annotation'); annotations.each(function () { // @ts-ignore const element = d3.select(this); - element.selectAll('.mlAnnotationTextRect').classed('mlAnnotationTextRect-isBlur', false); + element.selectAll('.ml-annotation__text-rect').classed('ml-annotation__text-rect--blur', false); element - .selectAll('.mlAnnotationRect') - .classed('mlAnnotationRect-isHighlight', false) - .classed('mlAnnotationRect-isBlur', false); - element.selectAll('.mlAnnotationText').classed('mlAnnotationText-isBlur', false); + .selectAll('.ml-annotation__rect') + .classed('ml-annotation__rect--highlight', false) + .classed('ml-annotation__rect--blur', false); + element.selectAll('.ml-annotation__text').classed('ml-annotation__text--blur', false); }); } diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/styles.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/styles.ts new file mode 100644 index 00000000000000..74ddf085038039 --- /dev/null +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/styles.ts @@ -0,0 +1,332 @@ +/* + * 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 { css } from '@emotion/react'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { transparentize } from '@elastic/eui'; +import { mlColors } from '../styles'; + +// Annotations constants +const mlAnnotationBorderWidth = '2px'; +const mlAnnotationRectDefaultStrokeOpacity = 0.2; +const mlAnnotationRectDefaultFillOpacity = 0.05; + +export const getTimeseriesExplorerStyles = () => + css({ + color: euiThemeVars.euiColorDarkShade, + + '.ml-timeseries-chart': { + svg: { + fontSize: euiThemeVars.euiFontSizeXS, + fontFamily: euiThemeVars.euiFontFamily, + }, + + '.axis': { + 'path, line': { + fill: 'none', + stroke: euiThemeVars.euiBorderColor, + shapeRendering: 'crispEdges', + pointerEvents: 'none', + }, + + text: { + fill: euiThemeVars.euiTextColor, + }, + + '.tick line': { + stroke: euiThemeVars.euiColorLightShade, + }, + }, + + '.chart-border': { + stroke: euiThemeVars.euiBorderColor, + fill: 'none', + strokeWidth: 1, + shapeRendering: 'crispEdges', + }, + + '.chart-border-highlight': { + stroke: euiThemeVars.euiColorDarkShade, + strokeWidth: 2, + + '&:hover': { + opacity: 1, + }, + }, + + '.area': { + strokeWidth: 1, + + '&.bounds': { + fill: transparentize(euiThemeVars.euiColorPrimary, 0.2), + pointerEvents: 'none', + }, + + '&.forecast': { + fill: transparentize(euiThemeVars.euiColorVis5, 0.3), + pointerEvents: 'none', + }, + }, + + '.values-line': { + fill: 'none', + stroke: euiThemeVars.euiColorPrimary, + strokeWidth: 2, + pointerEvents: 'none', + + '&.forecast': { + stroke: euiThemeVars.euiColorVis5, + pointerEvents: 'none', + }, + }, + + '.hidden': { + visibility: 'hidden', + }, + + '.values-dots circle': { + fill: euiThemeVars.euiColorPrimary, + strokeWidth: 0, + }, + + '.metric-value': { + opacity: 1, + fill: 'transparent', + stroke: euiThemeVars.euiColorPrimary, + strokeWidth: 0, + }, + + '.anomaly-marker': { + strokeWidth: 1, + stroke: euiThemeVars.euiColorMediumShade, + + '&.critical': { + fill: mlColors.critical, + }, + + '&.major': { + fill: mlColors.major, + }, + + '&.minor': { + fill: mlColors.minor, + }, + + '&.warning': { + fill: mlColors.warning, + }, + + '&.low': { + fill: mlColors.lowWarning, + }, + }, + + '.metric-value:hover, .anomaly-marker:hover, .anomaly-marker.highlighted': { + strokeWidth: 6, + strokeOpacity: 0.65, + stroke: euiThemeVars.euiColorPrimary, + }, + + 'rect.scheduled-event-marker': { + strokeWidth: 1, + stroke: euiThemeVars.euiColorDarkShade, + fill: euiThemeVars.euiColorLightShade, + }, + + '.forecast': { + '.metric-value, .metric-value:hover': { + stroke: euiThemeVars.euiColorVis5, + }, + }, + + '.focus-chart': { + '.x-axis-background': { + line: { + fill: 'none', + shapeRendering: 'crispEdges', + stroke: euiThemeVars.euiColorLightestShade, + }, + rect: { + fill: euiThemeVars.euiColorLightestShade, + }, + }, + '.focus-zoom': { + fill: euiThemeVars.euiColorDarkShade, + a: { + text: { + fill: euiThemeVars.euiColorPrimary, + cursor: 'pointer', + }, + '&:hover, &:active, &:focus': { + textDecoration: 'underline', + fill: euiThemeVars.euiColorPrimary, + }, + }, + }, + }, + + '.context-chart': { + '.x.axis path': { + display: 'none', + }, + '.axis text': { + fontSize: '10px', + fill: euiThemeVars.euiTextColor, + }, + '.values-line': { + strokeWidth: 1, + }, + '.mask': { + polygon: { + fillOpacity: 0.1, + }, + '.area.bounds': { + fill: euiThemeVars.euiColorLightShade, + }, + '.values-line': { + strokeWidth: 1, + stroke: euiThemeVars.euiColorMediumShade, + }, + }, + }, + + '.swimlane .axis text': { + display: 'none', + }, + + '.swimlane rect.swimlane-cell-hidden': { + display: 'none', + }, + + '.brush .extent': { + fillOpacity: 0, + shapeRendering: 'crispEdges', + stroke: euiThemeVars.euiColorDarkShade, + strokeWidth: 2, + cursor: 'move', + '&:hover': { + opacity: 1, + }, + }, + + '.top-border': { + fill: euiThemeVars.euiColorEmptyShade, + }, + + 'foreignObject.brush-handle': { + pointerEvents: 'none', + paddingTop: '1px', + }, + + 'div.brush-handle-inner': { + border: `1px solid ${euiThemeVars.euiColorDarkShade}`, + backgroundColor: euiThemeVars.euiColorLightShade, + height: '70px', + width: '10px', + textAlign: 'center', + cursor: 'ew-resize', + marginTop: '9px', + fontSize: euiThemeVars.euiFontSizeS, + fill: euiThemeVars.euiColorDarkShade, + }, + + 'div.brush-handle-inner-left': { + borderRadius: `${euiThemeVars.euiBorderRadius} 0 0 ${euiThemeVars.euiBorderRadius}`, + }, + + 'div.brush-handle-inner-right': { + borderRadius: `0 ${euiThemeVars.euiBorderRadius} ${euiThemeVars.euiBorderRadius} 0`, + }, + + 'rect.brush-handle': { + strokeWidth: 1, + stroke: euiThemeVars.euiColorDarkShade, + fill: euiThemeVars.euiColorLightShade, + pointerEvents: 'none', + '&:hover': { + opacity: 1, + }, + }, + }, + }); + +export const getAnnotationStyles = () => + css({ + '.ml-annotation': { + '&__brush': { + '.extent': { + stroke: euiThemeVars.euiColorLightShade, + strokeWidth: mlAnnotationBorderWidth, + strokeDasharray: '2 2', + fill: euiThemeVars.euiColorLightestShade, + shapeRendering: 'geometricPrecision', + }, + }, + + '&__rect': { + stroke: euiThemeVars.euiColorFullShade, + strokeWidth: mlAnnotationBorderWidth, + strokeOpacity: mlAnnotationRectDefaultStrokeOpacity, + fill: euiThemeVars.euiColorFullShade, + fillOpacity: mlAnnotationRectDefaultFillOpacity, + shapeRendering: 'geometricPrecision', + transition: `stroke-opacity ${euiThemeVars.euiAnimSpeedFast}, fill-opacity ${euiThemeVars.euiAnimSpeedFast}`, + + '&--highlight': { + strokeOpacity: mlAnnotationRectDefaultStrokeOpacity * 2, + fillOpacity: mlAnnotationRectDefaultFillOpacity * 2, + }, + + '&--blur': { + strokeOpacity: mlAnnotationRectDefaultStrokeOpacity / 2, + fillOpacity: mlAnnotationRectDefaultFillOpacity / 2, + }, + }, + + '&__text': { + textAnchor: 'middle', + fontSize: euiThemeVars.euiFontSizeXS, + fontFamily: euiThemeVars.euiFontFamily, + fontWeight: euiThemeVars.euiFontWeightMedium, + fill: euiThemeVars.euiColorFullShade, + transition: `fill ${euiThemeVars.euiAnimSpeedFast}`, + userSelect: 'none', + + '&--blur': { + fill: euiThemeVars.euiColorMediumShade, + }, + }, + + '&__text-rect': { + fill: euiThemeVars.euiColorLightShade, + transition: `fill ${euiThemeVars.euiAnimSpeedFast}`, + + '&--blur': { + fill: euiThemeVars.euiColorLightestShade, + }, + }, + + '&--hidden': { + display: 'none', + }, + + '&__context-rect': { + stroke: euiThemeVars.euiColorFullShade, + strokeWidth: mlAnnotationBorderWidth, + strokeOpacity: mlAnnotationRectDefaultStrokeOpacity, + fill: euiThemeVars.euiColorFullShade, + fillOpacity: mlAnnotationRectDefaultFillOpacity, + transition: `stroke-opacity ${euiThemeVars.euiAnimSpeedFast}, fill-opacity ${euiThemeVars.euiAnimSpeedFast}`, + shapeRendering: 'geometricPrecision', + + '&--blur': { + strokeOpacity: mlAnnotationRectDefaultStrokeOpacity / 2, + fillOpacity: mlAnnotationRectDefaultFillOpacity / 2, + }, + }, + }, + }); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx index d673aca1e74e3d..dc7cacdba5d0a3 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_page.tsx @@ -19,6 +19,7 @@ import { HelpMenu } from '../components/help_menu'; import { useMlKibana } from '../contexts/kibana'; import { MlPageHeader } from '../components/page_header'; import { PageTitle } from '../components/page_title'; +import { getAnnotationStyles, getTimeseriesExplorerStyles } from './styles'; interface TimeSeriesExplorerPageProps { dateFormatTz?: string; @@ -26,6 +27,9 @@ interface TimeSeriesExplorerPageProps { noSingleMetricJobsFound?: boolean; } +const timeseriesExplorerStyles = getTimeseriesExplorerStyles(); +const annotationStyles = getAnnotationStyles(); + export const TimeSeriesExplorerPage: FC> = ({ children, dateFormatTz, @@ -38,10 +42,11 @@ export const TimeSeriesExplorerPage: FC
diff --git a/x-pack/plugins/ml/public/shared_components/single_metric_viewer/_index.scss b/x-pack/plugins/ml/public/shared_components/single_metric_viewer/_index.scss deleted file mode 100644 index b6f91cc749dcc6..00000000000000 --- a/x-pack/plugins/ml/public/shared_components/single_metric_viewer/_index.scss +++ /dev/null @@ -1,6 +0,0 @@ -// ML has it's own variables for coloring -@import '../../application/variables'; - -// Protect the rest of Kibana from ML generic namespacing -@import '../../application/timeseriesexplorer/timeseriesexplorer'; -@import '../../application/timeseriesexplorer/timeseriesexplorer_annotations'; \ No newline at end of file diff --git a/x-pack/plugins/ml/public/shared_components/single_metric_viewer/single_metric_viewer.tsx b/x-pack/plugins/ml/public/shared_components/single_metric_viewer/single_metric_viewer.tsx index 96e678407f6260..27ed864fbd012a 100644 --- a/x-pack/plugins/ml/public/shared_components/single_metric_viewer/single_metric_viewer.tsx +++ b/x-pack/plugins/ml/public/shared_components/single_metric_viewer/single_metric_viewer.tsx @@ -26,7 +26,10 @@ import type { MlDependencies } from '../../application/app'; import { TimeSeriesExplorerEmbeddableChart } from '../../application/timeseriesexplorer/timeseriesexplorer_embeddable_chart'; import { APP_STATE_ACTION } from '../../application/timeseriesexplorer/timeseriesexplorer_constants'; import type { SingleMetricViewerServices, MlEntity } from '../../embeddables/types'; -import './_index.scss'; +import { + getTimeseriesExplorerStyles, + getAnnotationStyles, +} from '../../application/timeseriesexplorer/styles'; const containerPadding = 20; const minElemAndChartDiff = 20; @@ -72,6 +75,9 @@ export interface SingleMetricViewerProps { type Zoom = AppStateZoom | undefined; type ForecastId = string | undefined; +const timeseriesExplorerStyles = getTimeseriesExplorerStyles(); +const annotationStyles = getAnnotationStyles(); + const SingleMetricViewerWrapper: FC = ({ // Component dependencies coreStart, @@ -217,7 +223,7 @@ const SingleMetricViewerWrapper: FC = ({ }} data-test-subj={`mlSingleMetricViewer_${uuid}`} ref={resizeRef} - className="ml-time-series-explorer" + css={[timeseriesExplorerStyles, annotationStyles]} data-shared-item="" // TODO: Remove data-shared-item as part of https://github.com/elastic/kibana/issues/179376 data-rendering-count={1} > diff --git a/x-pack/test/functional/services/ml/job_annotations_table.ts b/x-pack/test/functional/services/ml/job_annotations_table.ts index 7945abd7e9608a..0dc477113f1ea8 100644 --- a/x-pack/test/functional/services/ml/job_annotations_table.ts +++ b/x-pack/test/functional/services/ml/job_annotations_table.ts @@ -285,7 +285,7 @@ export function MachineLearningJobAnnotationsProvider({ getService }: FtrProvide public async openCreateAnnotationFlyout() { await retry.tryForTime(30 * 1000, async () => { - const el = await find.byClassName('mlAnnotationBrush'); + const el = await find.byClassName('ml-annotation__brush'); // simulate click and drag on the focus chart // to generate annotation From 2b7c72c6193cf46c5cf883dafb8521f4a6805cd4 Mon Sep 17 00:00:00 2001 From: Marco Antonio Ghiani Date: Mon, 14 Oct 2024 12:15:36 +0200 Subject: [PATCH 15/38] [Fields Metadata] Improve integration fields resolution and caching (#195405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📓 Summary Browsing fields from the Discover sidebar, I noticed integration fields never show a related description even if they exist. The same is happening in the fields table for the document detail flyout. This happens due to `integration` and `dataset` parameters not being passed to the service. https://github.com/user-attachments/assets/0946cc71-44fb-4fc7-8e9d-b146bdd811f2 These changes improve the resolution of the integration field metadata: - The `integration` and `dataset` params are no longer required to attempt resolving and integration field metadata. They are still accepted as an explicit hint in case we cannot infer correctly some integration packages from the field name. - The above change enables querying fields from different integrations and datasets at once, enabling metadata retrieval for mixed data sources. - The integration retrieved from the EPR is now cached with its relevant version, solving a potential corner case as explained [here](https://github.com/elastic/kibana/pull/183806#pullrequestreview-2088102130). https://github.com/user-attachments/assets/ae9cafd8-2581-4ce0-9242-cbb4e37c7702 --------- Co-authored-by: Marco Antonio Ghiani --- x-pack/plugins/fields_metadata/README.md | 2 + .../plugins/fields_metadata/server/mocks.ts | 2 + .../plugins/fields_metadata/server/plugin.ts | 1 + .../fields_metadata_client.test.ts | 56 +++++++++++++-- .../fields_metadata/fields_metadata_client.ts | 2 +- .../fields_metadata_service.mock.ts | 1 + .../fields_metadata_service.ts | 9 ++- .../integration_fields_repository.ts | 71 +++++++++++++++++-- .../fields_metadata/repositories/types.ts | 8 +++ .../server/services/fields_metadata/types.ts | 7 +- .../plugins/fields_metadata/server/types.ts | 1 + x-pack/plugins/fleet/server/plugin.ts | 6 +- ...=> register_fields_metadata_extractors.ts} | 14 +++- 13 files changed, 162 insertions(+), 18 deletions(-) rename x-pack/plugins/fleet/server/services/{register_integration_fields_extractor.ts => register_fields_metadata_extractors.ts} (66%) diff --git a/x-pack/plugins/fields_metadata/README.md b/x-pack/plugins/fields_metadata/README.md index ea561c52febcef..dacd6d8acabd36 100755 --- a/x-pack/plugins/fields_metadata/README.md +++ b/x-pack/plugins/fields_metadata/README.md @@ -55,6 +55,8 @@ const fields = await client.find({ */ ``` +> The service will try to extract the integration and dataset name as they are conventionally named in their static definition, providing a much simpler usage of this API for integration fields. + > N.B. Passing the `dataset` name parameter to `.find` helps narrowing the scope of the integration assets that need to be fetched, increasing the performance of the request. In case the exact dataset for a field is unknown, is it still possible to pass a `*` value as `dataset` parameter to access all the integration datasets' fields. Still, is recommended always passing the `dataset` as well if known or unless the required fields come from different datasets of the same integration. diff --git a/x-pack/plugins/fields_metadata/server/mocks.ts b/x-pack/plugins/fields_metadata/server/mocks.ts index b46ca661b6210b..03415fcc6ddda1 100644 --- a/x-pack/plugins/fields_metadata/server/mocks.ts +++ b/x-pack/plugins/fields_metadata/server/mocks.ts @@ -14,6 +14,8 @@ import { FieldsMetadataServerSetup, FieldsMetadataServerStart } from './types'; const createFieldsMetadataServerSetupMock = (): jest.Mocked => ({ registerIntegrationFieldsExtractor: createFieldsMetadataServiceSetupMock().registerIntegrationFieldsExtractor, + registerIntegrationListExtractor: + createFieldsMetadataServiceSetupMock().registerIntegrationListExtractor, }); const createFieldsMetadataServerStartMock = (): jest.Mocked => ({ diff --git a/x-pack/plugins/fields_metadata/server/plugin.ts b/x-pack/plugins/fields_metadata/server/plugin.ts index da7ede5efba3f9..70ccb7b5d30a34 100644 --- a/x-pack/plugins/fields_metadata/server/plugin.ts +++ b/x-pack/plugins/fields_metadata/server/plugin.ts @@ -52,6 +52,7 @@ export class FieldsMetadataPlugin return { registerIntegrationFieldsExtractor: fieldsMetadata.registerIntegrationFieldsExtractor, + registerIntegrationListExtractor: fieldsMetadata.registerIntegrationListExtractor, }; } diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.test.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.test.ts index 93c43fa69e5c87..4ef2e3c693fb54 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.test.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.test.ts @@ -57,6 +57,18 @@ const integrationFields = { 'The version of the browser or computer where the 1Password app is installed, or the CPU of the machine where the 1Password command-line tool is installed', }, }, + 'mysql.slowlog': { + 'mysql.slowlog.filesort': { + name: 'filesort', + type: 'boolean', + description: 'Whether filesort optimization was used.', + flat_name: 'mysql.slowlog.filesort', + source: 'integration', + dashed_name: 'mysql-slowlog-filesort', + normalize: [], + short: 'Whether filesort optimization was used.', + }, + }, }; describe('FieldsMetadataClient class', () => { @@ -64,7 +76,22 @@ describe('FieldsMetadataClient class', () => { const ecsFieldsRepository = EcsFieldsRepository.create({ ecsFields }); const metadataFieldsRepository = MetadataFieldsRepository.create({ metadataFields }); const integrationFieldsExtractor = jest.fn(); + const integrationListExtractor = jest.fn(); integrationFieldsExtractor.mockImplementation(() => Promise.resolve(integrationFields)); + integrationListExtractor.mockImplementation(() => + Promise.resolve([ + { + id: '1password', + name: '1password', + version: '1.0.0', + }, + { + id: 'mysql', + name: 'mysql', + version: '1.0.0', + }, + ]) + ); let integrationFieldsRepository: IntegrationFieldsRepository; let fieldsMetadataClient: FieldsMetadataClient; @@ -73,6 +100,7 @@ describe('FieldsMetadataClient class', () => { integrationFieldsExtractor.mockClear(); integrationFieldsRepository = IntegrationFieldsRepository.create({ integrationFieldsExtractor, + integrationListExtractor, }); fieldsMetadataClient = FieldsMetadataClient.create({ ecsFieldsRepository, @@ -105,6 +133,26 @@ describe('FieldsMetadataClient class', () => { expect(Object.hasOwn(timestampField, 'type')).toBeTruthy(); }); + it('should attempt resolving the field from an integration if it does not exist in ECS/Metadata by inferring the integration from the field name', async () => { + const mysqlFieldInstance = await fieldsMetadataClient.getByName('mysql.slowlog.filesort'); + + expect(integrationFieldsExtractor).toHaveBeenCalled(); + + expectToBeDefined(mysqlFieldInstance); + expect(mysqlFieldInstance).toBeInstanceOf(FieldMetadata); + + const mysqlField = mysqlFieldInstance.toPlain(); + + expect(Object.hasOwn(mysqlField, 'name')).toBeTruthy(); + expect(Object.hasOwn(mysqlField, 'type')).toBeTruthy(); + expect(Object.hasOwn(mysqlField, 'description')).toBeTruthy(); + expect(Object.hasOwn(mysqlField, 'flat_name')).toBeTruthy(); + expect(Object.hasOwn(mysqlField, 'source')).toBeTruthy(); + expect(Object.hasOwn(mysqlField, 'dashed_name')).toBeTruthy(); + expect(Object.hasOwn(mysqlField, 'normalize')).toBeTruthy(); + expect(Object.hasOwn(mysqlField, 'short')).toBeTruthy(); + }); + it('should attempt resolving the field from an integration if it does not exist in ECS/Metadata and the integration and dataset params are provided', async () => { const onePasswordFieldInstance = await fieldsMetadataClient.getByName( 'onepassword.client.platform_version', @@ -128,13 +176,13 @@ describe('FieldsMetadataClient class', () => { expect(Object.hasOwn(onePasswordField, 'short')).toBeTruthy(); }); - it('should not resolve the field from an integration if the integration and dataset params are not provided', async () => { - const onePasswordFieldInstance = await fieldsMetadataClient.getByName( - 'onepassword.client.platform_version' + it('should not resolve the field from an integration if the integration name cannot be inferred from the field name and integration and dataset params are not provided', async () => { + const unknownFieldInstance = await fieldsMetadataClient.getByName( + 'customField.duration.milliseconds' ); expect(integrationFieldsExtractor).not.toHaveBeenCalled(); - expect(onePasswordFieldInstance).toBeUndefined(); + expect(unknownFieldInstance).toBeUndefined(); }); }); diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.ts index 87c9b6547f4f58..baaac903a7b3a7 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.ts @@ -43,7 +43,7 @@ export class FieldsMetadataClient implements IFieldsMetadataClient { } // 2. Try searching for the fiels in the Elastic Package Registry - if (!field && integration) { + if (!field) { field = await this.integrationFieldsRepository.getByName(fieldName, { integration, dataset }); } diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.mock.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.mock.ts index 6fab587c9ca7aa..b6395d4c96f6be 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.mock.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.mock.ts @@ -11,6 +11,7 @@ import { FieldsMetadataServiceSetup, FieldsMetadataServiceStart } from './types' export const createFieldsMetadataServiceSetupMock = (): jest.Mocked => ({ registerIntegrationFieldsExtractor: jest.fn(), + registerIntegrationListExtractor: jest.fn(), }); export const createFieldsMetadataServiceStartMock = diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.ts index 8313f0337d7695..dc8aa976e34bee 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.ts @@ -11,12 +11,13 @@ import { FieldsMetadataClient } from './fields_metadata_client'; import { EcsFieldsRepository } from './repositories/ecs_fields_repository'; import { IntegrationFieldsRepository } from './repositories/integration_fields_repository'; import { MetadataFieldsRepository } from './repositories/metadata_fields_repository'; -import { IntegrationFieldsExtractor } from './repositories/types'; +import { IntegrationFieldsExtractor, IntegrationListExtractor } from './repositories/types'; import { FieldsMetadataServiceSetup, FieldsMetadataServiceStart } from './types'; import { MetadataFields as metadataFields } from '../../../common/metadata_fields'; export class FieldsMetadataService { private integrationFieldsExtractor: IntegrationFieldsExtractor = () => Promise.resolve({}); + private integrationListExtractor: IntegrationListExtractor = () => Promise.resolve([]); constructor(private readonly logger: Logger) {} @@ -25,16 +26,20 @@ export class FieldsMetadataService { registerIntegrationFieldsExtractor: (extractor: IntegrationFieldsExtractor) => { this.integrationFieldsExtractor = extractor; }, + registerIntegrationListExtractor: (extractor: IntegrationListExtractor) => { + this.integrationListExtractor = extractor; + }, }; } public start(): FieldsMetadataServiceStart { - const { logger, integrationFieldsExtractor } = this; + const { logger, integrationFieldsExtractor, integrationListExtractor } = this; const ecsFieldsRepository = EcsFieldsRepository.create({ ecsFields }); const metadataFieldsRepository = MetadataFieldsRepository.create({ metadataFields }); const integrationFieldsRepository = IntegrationFieldsRepository.create({ integrationFieldsExtractor, + integrationListExtractor, }); return { diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/repositories/integration_fields_repository.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/repositories/integration_fields_repository.ts index cf3c2b0454c7de..7bf3ed871d1c55 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/repositories/integration_fields_repository.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/repositories/integration_fields_repository.ts @@ -9,14 +9,17 @@ import { ANY_DATASET } from '../../../../common/fields_metadata'; import { HashedCache } from '../../../../common/hashed_cache'; import { FieldMetadata, IntegrationFieldName } from '../../../../common'; import { + ExtractedIntegration, ExtractedIntegrationFields, IntegrationFieldsExtractor, IntegrationFieldsSearchParams, + IntegrationListExtractor, IntegrationName, } from './types'; import { PackageNotFoundError } from '../errors'; interface IntegrationFieldsRepositoryDeps { integrationFieldsExtractor: IntegrationFieldsExtractor; + integrationListExtractor: IntegrationListExtractor; } type DatasetFieldsMetadata = Record; @@ -24,15 +27,28 @@ type IntegrationFieldsMetadataTree = Record; + private integrationsMap: Map; - private constructor(private readonly fieldsExtractor: IntegrationFieldsExtractor) { + private constructor( + private readonly integrationFieldsExtractor: IntegrationFieldsExtractor, + private readonly integrationListExtractor: IntegrationListExtractor + ) { this.cache = new HashedCache(); + this.integrationsMap = new Map(); + + this.extractIntegrationList(); } async getByName( fieldName: IntegrationFieldName, - { integration, dataset }: IntegrationFieldsSearchParams + params: Partial ): Promise { + const { integration, dataset } = this.extractIntegrationFieldsSearchParams(fieldName, params); + + if (!integration || !this.integrationsMap.has(integration)) { + return undefined; + } + let field = this.getCachedField(fieldName, { integration, dataset }); if (!field) { @@ -48,8 +64,29 @@ export class IntegrationFieldsRepository { return field; } - public static create({ integrationFieldsExtractor }: IntegrationFieldsRepositoryDeps) { - return new IntegrationFieldsRepository(integrationFieldsExtractor); + public static create({ + integrationFieldsExtractor, + integrationListExtractor, + }: IntegrationFieldsRepositoryDeps) { + return new IntegrationFieldsRepository(integrationFieldsExtractor, integrationListExtractor); + } + + private extractIntegrationFieldsSearchParams( + fieldName: IntegrationFieldName, + params: Partial + ) { + const parts = fieldName.split('.'); + + if (parts.length < 3) { + return params; + } + + const [extractedIntegration, extractedDataset] = parts; + + return { + integration: params.integration ?? extractedIntegration, + dataset: params.dataset ?? [extractedIntegration, extractedDataset].join('.'), + }; } private async extractFields({ @@ -63,11 +100,17 @@ export class IntegrationFieldsRepository { return undefined; } - return this.fieldsExtractor({ integration, dataset }) + return this.integrationFieldsExtractor({ integration, dataset }) .then(this.mapExtractedFieldsToFieldMetadataTree) .then((fieldMetadataTree) => this.storeFieldsInCache(cacheKey, fieldMetadataTree)); } + private extractIntegrationList(): void { + void this.integrationListExtractor() + .then(this.mapExtractedIntegrationListToMap) + .then((integrationsMap) => (this.integrationsMap = integrationsMap)); + } + private getCachedField( fieldName: IntegrationFieldName, { integration, dataset }: IntegrationFieldsSearchParams @@ -113,7 +156,19 @@ export class IntegrationFieldsRepository { } }; - private getCacheKey = (params: IntegrationFieldsSearchParams) => params; + private getCacheKey = ({ integration, dataset }: IntegrationFieldsSearchParams) => { + const integrationDetails = this.integrationsMap.get(integration); + + if (integrationDetails) { + return { + dataset, + integration, + version: integrationDetails.version, + }; + } + + return { integration, dataset }; + }; private mapExtractedFieldsToFieldMetadataTree = (extractedFields: ExtractedIntegrationFields) => { const datasetGroups = Object.entries(extractedFields); @@ -132,4 +187,8 @@ export class IntegrationFieldsRepository { return integrationGroup; }, {} as IntegrationFieldsMetadataTree); }; + + private mapExtractedIntegrationListToMap = (extractedIntegrations: ExtractedIntegration[]) => { + return new Map(extractedIntegrations.map((integration) => [integration.name, integration])); + }; } diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/repositories/types.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/repositories/types.ts index e258c46b569b49..a9fff964f2b194 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/repositories/types.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/repositories/types.ts @@ -20,3 +20,11 @@ export type ExtractedDatasetFields = Record; export type IntegrationFieldsExtractor = ( params: IntegrationFieldsSearchParams ) => Promise; + +export interface ExtractedIntegration { + id: string; + name: string; + version: string; +} + +export type IntegrationListExtractor = () => Promise; diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/types.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/types.ts index 5b87f3299d61b4..533b4fd0bb2c2c 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/types.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/types.ts @@ -6,7 +6,11 @@ */ import { FieldName, FieldMetadata, FieldsMetadataDictionary } from '../../../common'; -import { IntegrationFieldsExtractor, IntegrationFieldsSearchParams } from './repositories/types'; +import { + IntegrationFieldsExtractor, + IntegrationFieldsSearchParams, + IntegrationListExtractor, +} from './repositories/types'; export * from './repositories/types'; @@ -15,6 +19,7 @@ export interface FieldsMetadataServiceStartDeps {} export interface FieldsMetadataServiceSetup { registerIntegrationFieldsExtractor: (extractor: IntegrationFieldsExtractor) => void; + registerIntegrationListExtractor: (extractor: IntegrationListExtractor) => void; } export interface FieldsMetadataServiceStart { diff --git a/x-pack/plugins/fields_metadata/server/types.ts b/x-pack/plugins/fields_metadata/server/types.ts index 4e2bf7ce2c0b31..dd84bc2c24559b 100644 --- a/x-pack/plugins/fields_metadata/server/types.ts +++ b/x-pack/plugins/fields_metadata/server/types.ts @@ -21,6 +21,7 @@ export type FieldsMetadataPluginStartServicesAccessor = export interface FieldsMetadataServerSetup { registerIntegrationFieldsExtractor: FieldsMetadataServiceSetup['registerIntegrationFieldsExtractor']; + registerIntegrationListExtractor: FieldsMetadataServiceSetup['registerIntegrationListExtractor']; } export interface FieldsMetadataServerStart { diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index ced8da63e97030..4c5cdf70705307 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -139,7 +139,7 @@ import { PolicyWatcher } from './services/agent_policy_watch'; import { getPackageSpecTagId } from './services/epm/kibana/assets/tag_assets'; import { FleetMetricsTask } from './services/metrics/fleet_metrics_task'; import { fetchAgentMetrics } from './services/metrics/fetch_agent_metrics'; -import { registerIntegrationFieldsExtractor } from './services/register_integration_fields_extractor'; +import { registerFieldsMetadataExtractors } from './services/register_fields_metadata_extractors'; import { registerUpgradeManagedPackagePoliciesTask } from './services/setup/managed_package_policies'; import { registerDeployAgentPoliciesTask } from './services/agent_policies/deploy_agent_policies_task'; import { DeleteUnenrolledAgentsTask } from './tasks/delete_unenrolled_agents_task'; @@ -637,8 +637,8 @@ export class FleetPlugin logFactory: this.initializerContext.logger, }); - // Register fields metadata extractor - registerIntegrationFieldsExtractor({ core, fieldsMetadata: deps.fieldsMetadata }); + // Register fields metadata extractors + registerFieldsMetadataExtractors({ core, fieldsMetadata: deps.fieldsMetadata }); } public start(core: CoreStart, plugins: FleetStartDeps): FleetStartContract { diff --git a/x-pack/plugins/fleet/server/services/register_integration_fields_extractor.ts b/x-pack/plugins/fleet/server/services/register_fields_metadata_extractors.ts similarity index 66% rename from x-pack/plugins/fleet/server/services/register_integration_fields_extractor.ts rename to x-pack/plugins/fleet/server/services/register_fields_metadata_extractors.ts index d06c1e528469de..9a53c235622bfc 100644 --- a/x-pack/plugins/fleet/server/services/register_integration_fields_extractor.ts +++ b/x-pack/plugins/fleet/server/services/register_fields_metadata_extractors.ts @@ -15,7 +15,7 @@ interface RegistrationDeps { fieldsMetadata: FieldsMetadataServerSetup; } -export const registerIntegrationFieldsExtractor = ({ core, fieldsMetadata }: RegistrationDeps) => { +export const registerFieldsMetadataExtractors = ({ core, fieldsMetadata }: RegistrationDeps) => { fieldsMetadata.registerIntegrationFieldsExtractor(async ({ integration, dataset }) => { const [_core, _startDeps, { packageService }] = await core.getStartServices(); @@ -24,4 +24,16 @@ export const registerIntegrationFieldsExtractor = ({ core, fieldsMetadata }: Reg datasetName: dataset, }); }); + + fieldsMetadata.registerIntegrationListExtractor(async () => { + const [_core, _startDeps, { packageService }] = await core.getStartServices(); + + try { + const packages = await packageService.asInternalUser.getPackages(); + + return packages.map(({ id, name, version }) => ({ id, name, version })); + } catch (error) { + return []; + } + }); }; From 6a72037007d8f71504f444911c9fa25adfb1bb89 Mon Sep 17 00:00:00 2001 From: Ilya Nikokoshev Date: Mon, 14 Oct 2024 13:24:58 +0300 Subject: [PATCH 16/38] [Auto Import] CSV format support (#194386) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Release Notes Automatic Import can now create integrations for logs in the CSV format. Owing to the maturity of log format support, we thus remove the verbiage about requiring the JSON/NDJSON format. ## Summary **Added: the CSV feature** The issue is https://github.com/elastic/kibana/issues/194342 When the user adds a log sample whose format is recognized as CSV by the LLM, we now parse the samples and insert the [csv](https://www.elastic.co/guide/en/elasticsearch/reference/current/csv-processor.html) processor into the generated pipeline. If the header is present, we use it for the field names and add a [drop](https://www.elastic.co/guide/en/elasticsearch/reference/current/drop-processor.html) processor that removes a header from the document stream by comparing the values to the header values. If the header is missing, we ask the LLM to generate a list of column names, providing some context like package and data stream title. Should the header or LLM suggestion provide unsuitable for a specific column, we use `column1`, `column2` and so on as a fallback. To avoid duplicate column names, we can add postfixes like `_2` as necessary. If the format appears to be CSV, but the `csv` processor returns fails, we bubble up an error using the recently introduced `ErrorThatHandlesItsOwnResponse` class. We also provide the first example of passing the additional attributes of an error (in this case, the original CSV error) back to the client. The error message is composed on the client side. **Removed: supported formats message** The message that asks the user to upload the logs in `JSON/NDJSON format` is removed in this PR: image **Refactoring** The refactoring makes the "→JSON" conversion process more uniform across different chains and centralizes processor definitions in `.../server/util/processors.ts`. Log format chain now expects the LLM to follow the `SamplesFormat` when providing the information rather than an ad-hoc format. When testing, the `fail` method is [not supported in `jest`](https://stackoverflow.com/a/54244479/23968144), so it is removed. See the PR for examples and follow-up. --------- Co-authored-by: Elastic Machine --- .../__jest__/fixtures/log_type_detection.ts | 2 + .../analyze_logs/analyze_logs_route.gen.ts | 4 + .../analyze_logs_route.schema.yaml | 6 + .../common/api/generation_error.ts | 41 +++ .../common/api/model/api_test.mock.ts | 2 + .../common/api/model/common_attributes.gen.ts | 20 ++ .../api/model/common_attributes.schema.yaml | 18 ++ .../integration_assistant/common/constants.ts | 3 +- .../integration_assistant/common/index.ts | 3 +- .../generation_modal.test.tsx | 2 + .../data_stream_step/generation_modal.tsx | 2 +- .../data_stream_step/sample_logs_input.tsx | 3 - .../steps/data_stream_step/translations.ts | 39 ++- .../steps/data_stream_step/use_generation.tsx | 24 +- .../server/graphs/csv/columns.test.ts | 243 ++++++++++++++++++ .../server/graphs/csv/columns.ts | 115 +++++++++ .../server/graphs/csv/csv.ts | 100 +++++++ .../server/graphs/kv/constants.ts | 10 - .../server/graphs/kv/graph.test.ts | 6 +- .../server/graphs/kv/validate.ts | 19 +- .../graphs/log_type_detection/constants.ts | 9 +- .../log_type_detection/detection.test.ts | 15 +- .../graphs/log_type_detection/detection.ts | 19 +- .../graphs/log_type_detection/graph.test.ts | 6 +- .../server/graphs/log_type_detection/graph.ts | 22 +- .../graphs/log_type_detection/prompts.ts | 39 +-- .../server/graphs/log_type_detection/types.ts | 1 + .../server/graphs/unstructured/constants.ts | 8 - .../server/graphs/unstructured/graph.test.ts | 6 +- .../server/graphs/unstructured/validate.ts | 7 +- .../lib/errors/unparseable_csv_error.ts | 43 ++++ .../server/lib/errors/unsupported_error.ts | 4 +- .../server/routes/analyze_logs_routes.ts | 19 +- .../server/routes/build_integration_routes.ts | 4 +- .../server/routes/categorization_routes.ts | 4 +- .../server/routes/ecs_routes.ts | 4 +- .../server/routes/pipeline_routes.ts | 4 +- .../server/routes/related_routes.ts | 4 +- .../server/routes/routes_util.test.ts | 6 +- .../server/routes/routes_util.ts | 6 +- .../integration_assistant/server/types.ts | 4 +- .../server/util/index.ts | 2 +- .../server/util/pipeline.ts | 23 +- .../server/util/processors.ts | 61 +++++ .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 47 files changed, 853 insertions(+), 132 deletions(-) create mode 100644 x-pack/plugins/integration_assistant/common/api/generation_error.ts create mode 100644 x-pack/plugins/integration_assistant/server/graphs/csv/columns.test.ts create mode 100644 x-pack/plugins/integration_assistant/server/graphs/csv/columns.ts create mode 100644 x-pack/plugins/integration_assistant/server/graphs/csv/csv.ts create mode 100644 x-pack/plugins/integration_assistant/server/lib/errors/unparseable_csv_error.ts diff --git a/x-pack/plugins/integration_assistant/__jest__/fixtures/log_type_detection.ts b/x-pack/plugins/integration_assistant/__jest__/fixtures/log_type_detection.ts index b3c1e4c05ebd96..799dc45f8aadc2 100644 --- a/x-pack/plugins/integration_assistant/__jest__/fixtures/log_type_detection.ts +++ b/x-pack/plugins/integration_assistant/__jest__/fixtures/log_type_detection.ts @@ -14,6 +14,8 @@ export const logFormatDetectionTestState = { exAnswer: 'testanswer', packageName: 'testPackage', dataStreamName: 'testDatastream', + packageTitle: 'Test Title', + dataStreamTitle: 'Test Datastream Title', finalized: false, samplesFormat: { name: SamplesFormatName.Values.structured }, header: true, diff --git a/x-pack/plugins/integration_assistant/common/api/analyze_logs/analyze_logs_route.gen.ts b/x-pack/plugins/integration_assistant/common/api/analyze_logs/analyze_logs_route.gen.ts index a224bb3cbe241f..5e3c09c5fc74e8 100644 --- a/x-pack/plugins/integration_assistant/common/api/analyze_logs/analyze_logs_route.gen.ts +++ b/x-pack/plugins/integration_assistant/common/api/analyze_logs/analyze_logs_route.gen.ts @@ -19,6 +19,8 @@ import { z } from '@kbn/zod'; import { PackageName, DataStreamName, + PackageTitle, + DataStreamTitle, LogSamples, Connector, LangSmithOptions, @@ -29,6 +31,8 @@ export type AnalyzeLogsRequestBody = z.infer; export const AnalyzeLogsRequestBody = z.object({ packageName: PackageName, dataStreamName: DataStreamName, + packageTitle: PackageTitle, + dataStreamTitle: DataStreamTitle, logSamples: LogSamples, connectorId: Connector, langSmithOptions: LangSmithOptions.optional(), diff --git a/x-pack/plugins/integration_assistant/common/api/analyze_logs/analyze_logs_route.schema.yaml b/x-pack/plugins/integration_assistant/common/api/analyze_logs/analyze_logs_route.schema.yaml index 165a2cff91a062..298abbc1201eae 100644 --- a/x-pack/plugins/integration_assistant/common/api/analyze_logs/analyze_logs_route.schema.yaml +++ b/x-pack/plugins/integration_assistant/common/api/analyze_logs/analyze_logs_route.schema.yaml @@ -22,11 +22,17 @@ paths: - connectorId - packageName - dataStreamName + - packageTitle + - dataStreamTitle properties: packageName: $ref: "../model/common_attributes.schema.yaml#/components/schemas/PackageName" dataStreamName: $ref: "../model/common_attributes.schema.yaml#/components/schemas/DataStreamName" + packageTitle: + $ref: "../model/common_attributes.schema.yaml#/components/schemas/PackageTitle" + dataStreamTitle: + $ref: "../model/common_attributes.schema.yaml#/components/schemas/DataStreamTitle" logSamples: $ref: "../model/common_attributes.schema.yaml#/components/schemas/LogSamples" connectorId: diff --git a/x-pack/plugins/integration_assistant/common/api/generation_error.ts b/x-pack/plugins/integration_assistant/common/api/generation_error.ts new file mode 100644 index 00000000000000..e96ad8bff9b593 --- /dev/null +++ b/x-pack/plugins/integration_assistant/common/api/generation_error.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GenerationErrorCode } from '../constants'; + +// Errors raised by the generation process should provide information through this interface. +export interface GenerationErrorBody { + message: string; + attributes: GenerationErrorAttributes; +} + +export function isGenerationErrorBody(obj: unknown | undefined): obj is GenerationErrorBody { + return ( + typeof obj === 'object' && + obj !== null && + 'message' in obj && + typeof obj.message === 'string' && + 'attributes' in obj && + obj.attributes !== undefined && + isGenerationErrorAttributes(obj.attributes) + ); +} + +export interface GenerationErrorAttributes { + errorCode: GenerationErrorCode; + underlyingMessages: string[] | undefined; +} + +export function isGenerationErrorAttributes(obj: unknown): obj is GenerationErrorAttributes { + return ( + typeof obj === 'object' && + obj !== null && + 'errorCode' in obj && + typeof obj.errorCode === 'string' && + (!('underlyingMessages' in obj) || Array.isArray(obj.underlyingMessages)) + ); +} diff --git a/x-pack/plugins/integration_assistant/common/api/model/api_test.mock.ts b/x-pack/plugins/integration_assistant/common/api/model/api_test.mock.ts index ea2aa614175262..c8f6967503ac3b 100644 --- a/x-pack/plugins/integration_assistant/common/api/model/api_test.mock.ts +++ b/x-pack/plugins/integration_assistant/common/api/model/api_test.mock.ts @@ -96,6 +96,8 @@ export const getRelatedRequestMock = (): RelatedRequestBody => ({ export const getAnalyzeLogsRequestBody = (): AnalyzeLogsRequestBody => ({ dataStreamName: 'test-data-stream-name', packageName: 'test-package-name', + packageTitle: 'Test package title', + dataStreamTitle: 'Test data stream title', connectorId: 'test-connector-id', logSamples: rawSamples, }); diff --git a/x-pack/plugins/integration_assistant/common/api/model/common_attributes.gen.ts b/x-pack/plugins/integration_assistant/common/api/model/common_attributes.gen.ts index 7b64b4f8a88d81..803f9b8a6c3af8 100644 --- a/x-pack/plugins/integration_assistant/common/api/model/common_attributes.gen.ts +++ b/x-pack/plugins/integration_assistant/common/api/model/common_attributes.gen.ts @@ -31,6 +31,18 @@ export const PackageName = z.string().min(1); export type DataStreamName = z.infer; export const DataStreamName = z.string().min(1); +/** + * Package title for the integration to be built. + */ +export type PackageTitle = z.infer; +export const PackageTitle = z.string().min(1); + +/** + * DataStream title for the integration to be built. + */ +export type DataStreamTitle = z.infer; +export const DataStreamTitle = z.string().min(1); + /** * String form of the input logsamples. */ @@ -86,6 +98,14 @@ export const SamplesFormat = z.object({ * For some formats, specifies whether the samples can be multiline. */ multiline: z.boolean().optional(), + /** + * For CSV format, specifies whether the samples have a header row. For other formats, specifies the presence of header in each row. + */ + header: z.boolean().optional(), + /** + * For CSV format, specifies the column names proposed by the LLM. + */ + columns: z.array(z.string()).optional(), /** * For a JSON format, describes how to get to the sample array from the root of the JSON. */ diff --git a/x-pack/plugins/integration_assistant/common/api/model/common_attributes.schema.yaml b/x-pack/plugins/integration_assistant/common/api/model/common_attributes.schema.yaml index aba43d0174bb8e..900b6e362a754f 100644 --- a/x-pack/plugins/integration_assistant/common/api/model/common_attributes.schema.yaml +++ b/x-pack/plugins/integration_assistant/common/api/model/common_attributes.schema.yaml @@ -16,6 +16,16 @@ components: minLength: 1 description: DataStream name for the integration to be built. + PackageTitle: + type: string + minLength: 1 + description: Package title for the integration to be built. + + DataStreamTitle: + type: string + minLength: 1 + description: DataStream title for the integration to be built. + LogSamples: type: array items: @@ -66,6 +76,14 @@ components: multiline: type: boolean description: For some formats, specifies whether the samples can be multiline. + header: + type: boolean + description: For CSV format, specifies whether the samples have a header row. For other formats, specifies the presence of header in each row. + columns: + type: array + description: For CSV format, specifies the column names proposed by the LLM. + items: + type: string json_path: type: array description: For a JSON format, describes how to get to the sample array from the root of the JSON. diff --git a/x-pack/plugins/integration_assistant/common/constants.ts b/x-pack/plugins/integration_assistant/common/constants.ts index 1472a260fadf09..d652f661f10bbc 100644 --- a/x-pack/plugins/integration_assistant/common/constants.ts +++ b/x-pack/plugins/integration_assistant/common/constants.ts @@ -30,8 +30,9 @@ export const MINIMUM_LICENSE_TYPE: LicenseType = 'enterprise'; // ErrorCodes -export enum ErrorCode { +export enum GenerationErrorCode { RECURSION_LIMIT = 'recursion-limit', RECURSION_LIMIT_ANALYZE_LOGS = 'recursion-limit-analyze-logs', UNSUPPORTED_LOG_SAMPLES_FORMAT = 'unsupported-log-samples-format', + UNPARSEABLE_CSV_DATA = 'unparseable-csv-data', } diff --git a/x-pack/plugins/integration_assistant/common/index.ts b/x-pack/plugins/integration_assistant/common/index.ts index 5e90e6e6a6217d..b16254f9e11e28 100644 --- a/x-pack/plugins/integration_assistant/common/index.ts +++ b/x-pack/plugins/integration_assistant/common/index.ts @@ -27,10 +27,9 @@ export type { Integration, Pipeline, Docs, - SamplesFormat, LangSmithOptions, } from './api/model/common_attributes.gen'; -export { SamplesFormatName } from './api/model/common_attributes.gen'; +export { SamplesFormat, SamplesFormatName } from './api/model/common_attributes.gen'; export type { ESProcessorItem } from './api/model/processor_attributes.gen'; export type { CelInput } from './api/model/cel_input_attributes.gen'; diff --git a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/generation_modal.test.tsx b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/generation_modal.test.tsx index a8e6a30ca5dfa4..25ed0311297779 100644 --- a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/generation_modal.test.tsx +++ b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/generation_modal.test.tsx @@ -105,6 +105,8 @@ describe('GenerationModal', () => { it('should call runAnalyzeLogsGraph with correct parameters', () => { expect(mockRunAnalyzeLogsGraph).toHaveBeenCalledWith({ ...defaultRequest, + packageTitle: 'Mocked Integration title', + dataStreamTitle: 'Mocked Data Stream Title', logSamples: integrationSettingsNonJSON.logSamples ?? [], }); }); diff --git a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/generation_modal.tsx b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/generation_modal.tsx index 21f82532dc21cb..ba57d83618c13c 100644 --- a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/generation_modal.tsx +++ b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/generation_modal.tsx @@ -82,7 +82,7 @@ export const GenerationModal = React.memo( {error ? ( (({ integrationSe {i18n.LOGS_SAMPLE_DESCRIPTION} - - {i18n.LOGS_SAMPLE_DESCRIPTION_2} - } onChange={onChangeLogsSample} diff --git a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/translations.ts b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/translations.ts index 017a1a9c29caa1..48793d20496d61 100644 --- a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/translations.ts +++ b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/translations.ts @@ -6,7 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { ErrorCode } from '../../../../../../common/constants'; +import { GenerationErrorCode } from '../../../../../../common/constants'; +import type { GenerationErrorAttributes } from '../../../../../../common/api/generation_error'; export const INTEGRATION_NAME_TITLE = i18n.translate( 'xpack.integrationAssistant.step.dataStream.integrationNameTitle', @@ -109,12 +110,6 @@ export const LOGS_SAMPLE_DESCRIPTION = i18n.translate( defaultMessage: 'Drag and drop a file or Browse files.', } ); -export const LOGS_SAMPLE_DESCRIPTION_2 = i18n.translate( - 'xpack.integrationAssistant.step.dataStream.logsSample.description2', - { - defaultMessage: 'JSON/NDJSON format', - } -); export const LOGS_SAMPLE_TRUNCATED = (maxRows: number) => i18n.translate('xpack.integrationAssistant.step.dataStream.logsSample.truncatedWarning', { values: { maxRows }, @@ -188,7 +183,7 @@ export const PROGRESS_RELATED_GRAPH = i18n.translate( defaultMessage: 'Generating related fields', } ); -export const GENERATION_ERROR = (progressStep: string) => +export const GENERATION_ERROR_TITLE = (progressStep: string) => i18n.translate('xpack.integrationAssistant.step.dataStream.generationError', { values: { progressStep }, defaultMessage: 'An error occurred during: {progressStep}', @@ -198,24 +193,44 @@ export const RETRY = i18n.translate('xpack.integrationAssistant.step.dataStream. defaultMessage: 'Retry', }); -export const ERROR_TRANSLATION: Record = { - [ErrorCode.RECURSION_LIMIT_ANALYZE_LOGS]: i18n.translate( +export const GENERATION_ERROR_TRANSLATION: Record< + GenerationErrorCode, + string | ((attributes: GenerationErrorAttributes) => string) +> = { + [GenerationErrorCode.RECURSION_LIMIT_ANALYZE_LOGS]: i18n.translate( 'xpack.integrationAssistant.errors.recursionLimitAnalyzeLogsErrorMessage', { defaultMessage: 'Please verify the format of log samples is correct and try again. Try with a fewer samples if error persists.', } ), - [ErrorCode.RECURSION_LIMIT]: i18n.translate( + [GenerationErrorCode.RECURSION_LIMIT]: i18n.translate( 'xpack.integrationAssistant.errors.recursionLimitReached', { defaultMessage: 'Max attempts exceeded. Please try again.', } ), - [ErrorCode.UNSUPPORTED_LOG_SAMPLES_FORMAT]: i18n.translate( + [GenerationErrorCode.UNSUPPORTED_LOG_SAMPLES_FORMAT]: i18n.translate( 'xpack.integrationAssistant.errors.unsupportedLogSamples', { defaultMessage: 'Unsupported log format in the samples.', } ), + [GenerationErrorCode.UNPARSEABLE_CSV_DATA]: (attributes) => { + if ( + attributes.underlyingMessages !== undefined && + attributes.underlyingMessages?.length !== 0 + ) { + return i18n.translate('xpack.integrationAssistant.errors.uparseableCSV.withReason', { + values: { + reason: attributes.underlyingMessages[0], + }, + defaultMessage: `Cannot parse the samples as the CSV data (reason: {reason}). Please check the provided samples.`, + }); + } else { + return i18n.translate('xpack.integrationAssistant.errors.uparseableCSV.withoutReason', { + defaultMessage: `Cannot parse the samples as the CSV data. Please check the provided samples.`, + }); + } + }, }; diff --git a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/use_generation.tsx b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/use_generation.tsx index d062a0ff8b836b..566451d624c5ed 100644 --- a/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/use_generation.tsx +++ b/x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/steps/data_stream_step/use_generation.tsx @@ -16,6 +16,7 @@ import { type EcsMappingRequestBody, type RelatedRequestBody, } from '../../../../../../common'; +import { isGenerationErrorBody } from '../../../../../../common/api/generation_error'; import { runCategorizationGraph, runEcsGraph, @@ -26,7 +27,6 @@ import { useKibana } from '../../../../../common/hooks/use_kibana'; import type { State } from '../../state'; import * as i18n from './translations'; import { useTelemetry } from '../../../telemetry'; -import type { ErrorCode } from '../../../../../../common/constants'; import type { AIConnector, IntegrationSettings } from '../../types'; export type OnComplete = (result: State['result']) => void; @@ -46,6 +46,18 @@ interface RunGenerationProps { setProgress: (progress: ProgressItem) => void; } +// If the result is classified as a generation error, produce an error message +// as defined in the i18n file. Otherwise, return undefined. +function generationErrorMessage(body: unknown | undefined): string | undefined { + if (!isGenerationErrorBody(body)) { + return; + } + + const errorCode = body.attributes.errorCode; + const translation = i18n.GENERATION_ERROR_TRANSLATION[errorCode]; + return typeof translation === 'function' ? translation(body.attributes) : translation; +} + interface GenerationResults { pipeline: Pipeline; docs: Docs; @@ -96,12 +108,7 @@ export const useGeneration = ({ error: originalErrorMessage, }); - let errorMessage = originalErrorMessage; - const errorCode = e.body?.attributes?.errorCode as ErrorCode | undefined; - if (errorCode != null) { - errorMessage = i18n.ERROR_TRANSLATION[errorCode]; - } - setError(errorMessage); + setError(generationErrorMessage(e.body) ?? originalErrorMessage); } finally { setIsRequesting(false); } @@ -145,6 +152,9 @@ async function runGeneration({ const analyzeLogsRequest: AnalyzeLogsRequestBody = { packageName: integrationSettings.name ?? '', dataStreamName: integrationSettings.dataStreamName ?? '', + packageTitle: integrationSettings.title ?? integrationSettings.name ?? '', + dataStreamTitle: + integrationSettings.dataStreamTitle ?? integrationSettings.dataStreamName ?? '', logSamples: integrationSettings.logSamples ?? [], connectorId: connector.id, langSmithOptions: getLangSmithOptions(), diff --git a/x-pack/plugins/integration_assistant/server/graphs/csv/columns.test.ts b/x-pack/plugins/integration_assistant/server/graphs/csv/columns.test.ts new file mode 100644 index 00000000000000..4e84fb9f00af0b --- /dev/null +++ b/x-pack/plugins/integration_assistant/server/graphs/csv/columns.test.ts @@ -0,0 +1,243 @@ +/* + * 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 { + upperBoundForColumnCount, + generateColumnNames, + columnsFromHeader, + totalColumnCount, + toSafeColumnName, + yieldUniqueColumnNames, +} from './columns'; + +describe('upperBoundForColumnCount', () => { + it('should return the correct number of columns for a simple CSV', () => { + const samples = ['name,age,location', 'john,30,new york', 'jane,25,los angeles']; + expect(upperBoundForColumnCount(samples)).toBe(3); + }); + + it('should handle samples with varying column counts', () => { + const samples = ['name,age,location', 'john,30', 'jane,25,los angeles,usa']; + expect(upperBoundForColumnCount(samples)).toBe(4); + }); + + it('should return 0 for empty samples', () => { + const samples: string[] = []; + expect(upperBoundForColumnCount(samples)).toBe(0); + }); + + it('should handle samples with empty strings', () => { + const samples = ['', 'john,30,new york', 'jane,25,los angeles']; + expect(upperBoundForColumnCount(samples)).toBe(3); + }); + + it('should handle samples with only one column', () => { + const samples = ['name', 'john', 'jane']; + expect(upperBoundForColumnCount(samples)).toBe(1); + }); + + it('should handle samples with extra commas', () => { + const samples = ['name,age,location', 'john,30', 'jane,25,"los angeles,usa"']; + expect(upperBoundForColumnCount(samples)).toBeGreaterThanOrEqual(3); + }); +}); + +describe('generateColumnNames', () => { + it('should generate the correct number of column names', () => { + const count = 5; + const expected = ['column1', 'column2', 'column3', 'column4', 'column5']; + expect(generateColumnNames(count)).toEqual(expected); + }); + + it('should return an empty array when count is 0', () => { + const count = 0; + const expected: string[] = []; + expect(generateColumnNames(count)).toEqual(expected); + }); + + it('should handle large counts correctly', () => { + const count = 100; + const result = generateColumnNames(count); + expect(result.length).toBe(count); + expect(result[0]).toBe('column1'); + expect(result[count - 1]).toBe('column100'); + }); +}); + +describe('columnsFromHeader', () => { + it('should return the correct columns from the header object', () => { + const tempColumnNames = ['column1', 'column2', 'column3']; + const headerObject = { column1: 'name', column2: 'age', column3: 'location' }; + expect(columnsFromHeader(tempColumnNames, headerObject)).toEqual(['name', 'age', 'location']); + }); + + it('should return an empty array if no columns match', () => { + const tempColumnNames = ['column1', 'column2', 'column3']; + const headerObject = { column4: 'name', column5: 'age', column6: 'location' }; + expect(columnsFromHeader(tempColumnNames, headerObject)).toEqual([]); + }); + + it('should handle missing columns in the header object', () => { + const tempColumnNames = ['column1', 'column2', 'column3', 'column4']; + const headerObject = { column1: 'name', column3: 'location' }; + expect(columnsFromHeader(tempColumnNames, headerObject)).toEqual([ + 'name', + undefined, + 'location', + ]); + }); + + it('should handle an empty header object', () => { + const tempColumnNames = ['column1', 'column2', 'column3']; + const headerObject = {}; + expect(columnsFromHeader(tempColumnNames, headerObject)).toEqual([]); + }); + + it('should handle an empty tempColumnNames array', () => { + const tempColumnNames: string[] = []; + const headerObject = { column1: 'name', column2: 'age', column3: 'location' }; + expect(columnsFromHeader(tempColumnNames, headerObject)).toEqual([]); + }); +}); + +describe('totalColumnCount', () => { + it('should return the correct total column count for a simple CSV', () => { + const tempColumnNames = ['column1', 'column2', 'column3']; + const csvRows = [ + { column1: 'john', column2: '30', column3: 'new york' }, + { column1: 'jane', column3: '25', column4: 'los angeles' }, + ]; + expect(totalColumnCount(tempColumnNames, csvRows)).toBe(3); + }); + + it('should handle rows with varying column counts', () => { + const tempColumnNames = ['column1', 'column2', 'column3', 'column4']; + const csvRows = [ + { column1: 'john', column2: '30' }, + { column1: 'jane', column3: 'los angeles', column4: 'usa' }, + ]; + expect(totalColumnCount(tempColumnNames, csvRows)).toBe(4); + }); + + it('should return 0 for empty rows', () => { + const tempColumnNames = ['column1', 'column2', 'column3']; + expect(totalColumnCount(tempColumnNames, [])).toBe(0); + }); + + it('should handle rows with empty objects', () => { + const tempColumnNames = ['column1', 'column2', 'column3']; + const csvRows = [ + {}, + { column1: 'john', column2: '30', column3: 'new york' }, + { column1: 'jane', column2: '25', column3: 'los angeles' }, + ]; + expect(totalColumnCount(tempColumnNames, csvRows)).toBe(3); + }); + + it('should handle rows with only one column', () => { + const tempColumnNames = ['column1']; + const csvRows = [{ column1: 'john' }, { column1: 'jane' }]; + expect(totalColumnCount(tempColumnNames, csvRows)).toBe(1); + }); + + it('should handle rows with extra columns', () => { + const tempColumnNames = ['column1', 'column2', 'column3']; + const csvRows = [ + { column1: 'john', column2: '30' }, + { column1: 'jane', column2: '25', column3: 'los angeles', column4: 'usa' }, + ]; + expect(totalColumnCount(tempColumnNames, csvRows)).toBe(3); + }); + + describe('toSafeColumnName', () => { + it('should return undefined for non-string and non-number inputs', () => { + expect(toSafeColumnName(null)).toBeUndefined(); + expect(toSafeColumnName(undefined)).toBeUndefined(); + expect(toSafeColumnName({})).toBeUndefined(); + expect(toSafeColumnName([1, 2])).toBeUndefined(); + }); + + it('should replace non-alphanumeric characters with underscores', () => { + expect(toSafeColumnName('name@age!location')).toBe('name_age_location'); + expect(toSafeColumnName('column#1')).toBe('column_1'); + }); + + it('should return the same string if it contains only alphanumeric characters and underscores', () => { + expect(toSafeColumnName('Column1')).toBe('Column1'); + expect(toSafeColumnName('Location')).toBe('Location'); + }); + + it('should handle empty strings', () => { + expect(toSafeColumnName('')).toBeUndefined(); + }); + + it('should handle strings starting from a digit or numbers', () => { + expect(toSafeColumnName('1ABC')).toBe('Column1ABC'); + expect(toSafeColumnName(123)).toBe('Column123'); + }); + }); +}); + +describe('yieldUniqueColumnNames', () => { + it('should yield unique column names based on preferred and fallback names', () => { + const count = 5; + const preferredNames = [ + ['name1', 'name2', undefined, 'name4', undefined], + [undefined, 'altName2', 'altName3', undefined, 'altName5'], + ]; + const fallbackNames = ['fallback1', 'fallback2', 'fallback3', 'fallback4', 'fallback5']; + + const result = Array.from(yieldUniqueColumnNames(count, preferredNames, fallbackNames)); + expect(result).toEqual(['name1', 'name2', 'altName3', 'name4', 'altName5']); + }); + + it('should use fallback names when preferred names are not provided', () => { + const count = 3; + const preferredNames = [['name1', undefined, 'name3']]; + const fallbackNames = ['fallback1', 'fallback2', 'fallback3']; + + const result = Array.from(yieldUniqueColumnNames(count, preferredNames, fallbackNames)); + expect(result).toEqual(['name1', 'fallback2', 'name3']); + }); + + it('should append postfix to duplicate names to ensure uniqueness', () => { + const count = 4; + const preferredNames = [['name', 'name', 'name', 'name']]; + const fallbackNames = ['fallback1', 'fallback2', 'fallback3', 'fallback4']; + + const result = Array.from(yieldUniqueColumnNames(count, preferredNames, fallbackNames)); + expect(result).toEqual(['name', 'name_2', 'name_3', 'name_4']); + }); + + it('should handle mixed preferred and fallback names with duplicates', () => { + const count = 6; + const preferredNames = [ + ['name', undefined, 'name', undefined, undefined, undefined], + [undefined, 'altName', undefined, 'altName', undefined, 'altName'], + ]; + const fallbackNames = [ + 'fallback1', + 'fallback2', + 'fallback3', + 'fallback4', + 'fallback5', + 'fallback6', + ]; + + const result = Array.from(yieldUniqueColumnNames(count, preferredNames, fallbackNames)); + expect(result).toEqual(['name', 'altName', 'name_2', 'altName_2', 'fallback5', 'altName_3']); + }); + + it('should handle empty preferred names', () => { + const count = 3; + const preferredNames: Array> = []; + const fallbackNames: string[] = ['fallback1', 'fallback2', 'fallback3']; + + const result = Array.from(yieldUniqueColumnNames(count, preferredNames, fallbackNames)); + expect(result).toEqual(['fallback1', 'fallback2', 'fallback3']); + }); +}); diff --git a/x-pack/plugins/integration_assistant/server/graphs/csv/columns.ts b/x-pack/plugins/integration_assistant/server/graphs/csv/columns.ts new file mode 100644 index 00000000000000..108c4cec75bf6e --- /dev/null +++ b/x-pack/plugins/integration_assistant/server/graphs/csv/columns.ts @@ -0,0 +1,115 @@ +/* + * 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. + */ + +// Estimates from above the number of columns in the CSV samples. +export function upperBoundForColumnCount(csvSamples: string[]): number { + return Math.max(0, ...csvSamples.map((sample) => sample.split(',').length)); +} + +// Generates a list of temporary column names. +export function generateColumnNames(count: number): string[] { + return Array.from({ length: count }).map((_, i) => `column${i + 1}`); +} + +// Converts a column name into a safe one to use in the `if ctx...` clause. +// Result must pass rules at https://www.elastic.co/guide/en/elasticsearch/painless/8.15/painless-identifiers.html +export function toSafeColumnName(columnName: unknown): string | undefined { + if (typeof columnName === 'number') { + return `Column${columnName}`; + } + if (typeof columnName !== 'string') { + return undefined; + } + if (columnName.length === 0) { + return undefined; + } + const safeName = columnName.replace(/[^a-zA-Z0-9_]/g, '_'); + return /^[0-9]/.test(safeName) ? `Column${safeName}` : safeName; +} +// Returns the column list from a header row. We skip values that are not strings. + +export function columnsFromHeader( + tempColumnNames: string[], + headerObject: { [key: string]: unknown } +): Array { + const maxIndex = tempColumnNames.findLastIndex( + (columnName) => headerObject[columnName] !== undefined + ); + return tempColumnNames + .slice(0, maxIndex + 1) + .map((columnName) => headerObject[columnName]) + .map(toSafeColumnName); +} +// Count the number of columns actually present in the rows. + +export function totalColumnCount( + tempColumnNames: string[], + csvRows: Array<{ [key: string]: unknown }> +): number { + return ( + Math.max( + -1, + ...csvRows.map((row) => + tempColumnNames.findLastIndex((columnName) => row[columnName] !== undefined) + ) + ) + 1 + ); +} +// Prefixes each column with the provided prefixes, separated by a period. +export function prefixColumns(columns: string[], prefixes: string[]): string[] { + return columns.map((column) => [...prefixes, column].join('.')); +} +/** + * Generates a list of unique column names based on preferred and fallback names. + * + * The preferred names are used first, followed by the fallback names. It is required that + * there are enough fallback names to cover the number of unique column names needed. + * + * The resulting column names are guaranteed to be unique. If a column name is already in use, + * a postfix like _2, _3 and so on is added to the name to make it unique. + * + * @generator + * @param {number} count - The number of unique column names to generate. + * @param {Array>} preferredNames - A 2D array where each sub-array contains a list of names. + * @param {string[]} fallbackNames - An array of fallback names to use if no preferred name is defined. + * @yields {string} - A sequence of column names, such that no two are the same. + */ + +export function* yieldUniqueColumnNames( + count: number, + preferredNames: Array>, + fallbackNames: string[] +): Generator { + const knownNames = new Set(); + + for (let i = 0; i < count; i++) { + let selectedName: string = fallbackNames[i]; + + for (const nameList of preferredNames) { + const name = nameList[i]; + if (name) { + selectedName = name; + break; + } + } + + let postfixString = ''; + + if (knownNames.has(selectedName)) { + for (let postfix = 2; ; postfix++) { + postfixString = `_${postfix}`; + if (!knownNames.has(selectedName + postfixString)) { + break; + } + } + } + + selectedName += postfixString; + knownNames.add(selectedName); + yield selectedName; + } +} diff --git a/x-pack/plugins/integration_assistant/server/graphs/csv/csv.ts b/x-pack/plugins/integration_assistant/server/graphs/csv/csv.ts new file mode 100644 index 00000000000000..d753fd79956886 --- /dev/null +++ b/x-pack/plugins/integration_assistant/server/graphs/csv/csv.ts @@ -0,0 +1,100 @@ +/* + * 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 { LogFormatDetectionState } from '../../types'; +import type { LogDetectionNodeParams } from '../log_type_detection/types'; +import { createJSONInput } from '../../util'; +import { createCSVProcessor, createDropProcessor } from '../../util/processors'; +import { CSVParseError, UnparseableCSVFormatError } from '../../lib/errors/unparseable_csv_error'; +import { + generateColumnNames, + upperBoundForColumnCount, + columnsFromHeader, + toSafeColumnName, + totalColumnCount, + yieldUniqueColumnNames, + prefixColumns, +} from './columns'; + +// We will only create the processor for the first MAX_CSV_COLUMNS columns. +const MAX_CSV_COLUMNS = 100; + +// Converts CSV samples into JSON samples. +export async function handleCSV({ + state, + client, +}: LogDetectionNodeParams): Promise> { + const packageName = state.packageName; + const dataStreamName = state.dataStreamName; + + const samples = state.logSamples; + const temporaryColumns = generateColumnNames( + Math.min(upperBoundForColumnCount(samples), MAX_CSV_COLUMNS) + ); + const temporaryProcessor = createCSVProcessor('message', temporaryColumns); + + const { pipelineResults: tempResults, errors: tempErrors } = await createJSONInput( + [temporaryProcessor], + samples, + client + ); + + if (tempErrors.length > 0) { + throw new UnparseableCSVFormatError(tempErrors as CSVParseError[]); + } + + const headerColumns = state.samplesFormat.header + ? columnsFromHeader(temporaryColumns, tempResults[0]) + : []; + const llmProvidedColumns = (state.samplesFormat.columns || []).map(toSafeColumnName); + const needColumns = totalColumnCount(temporaryColumns, tempResults); + const columns: string[] = Array.from( + yieldUniqueColumnNames(needColumns, [llmProvidedColumns, headerColumns], temporaryColumns) + ); + + const prefix = [packageName, dataStreamName]; + const prefixedColumns = prefixColumns(columns, prefix); + const csvProcessor = createCSVProcessor('message', prefixedColumns); + const csvHandlingProcessors = [csvProcessor]; + + if (headerColumns.length > 0) { + const dropValues = columns.reduce((acc, column, index) => { + if (headerColumns[index] !== undefined) { + acc[column] = String(headerColumns[index]); + } + return acc; + }, {} as Record); + const dropProcessor = createDropProcessor( + dropValues, + prefix, + 'remove_csv_header', + 'Remove the CSV header line by comparing the values' + ); + csvHandlingProcessors.push(dropProcessor); + } + + const { pipelineResults: finalResults, errors: finalErrors } = await createJSONInput( + csvHandlingProcessors, + samples, + client + ); + + if (finalErrors.length > 0) { + throw new UnparseableCSVFormatError(finalErrors as CSVParseError[]); + } + + // Converts JSON Object into a string and parses it as a array of JSON strings + const jsonSamples = finalResults + .map((log) => log[packageName]) + .map((log) => (log as Record)[dataStreamName]) + .map((log) => JSON.stringify(log)); + + return { + jsonSamples, + additionalProcessors: [...state.additionalProcessors, ...csvHandlingProcessors], + lastExecutedChain: 'handleCSV', + }; +} diff --git a/x-pack/plugins/integration_assistant/server/graphs/kv/constants.ts b/x-pack/plugins/integration_assistant/server/graphs/kv/constants.ts index 183898ec313548..46ed44db01db4a 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/kv/constants.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/kv/constants.ts @@ -26,16 +26,6 @@ export const KV_HEADER_ERROR_EXAMPLE_ANSWER = { '%{TIMESTAMP:cisco.audit.timestamp}:%{WORD:cisco.audit.value1};%{WORD:cisco.audit.key2}:%{WORD:cisco.audit.value2}:%{GREEDYDATA:message}', }; -export const onFailure = { - append: { - field: 'error.message', - value: - '{% raw %}Processor {{{_ingest.on_failure_processor_type}}} with tag {{{_ingest.on_failure_processor_tag}}} in pipeline {{{_ingest.on_failure_pipeline}}} failed with message: {{{_ingest.on_failure_message}}}{% endraw %}', - }, -}; - -export const removeProcessor = { remove: { field: 'message', ignore_missing: true } }; - export const COMMON_ERRORS = [ { error: 'field [message] does not contain value_split [=]', diff --git a/x-pack/plugins/integration_assistant/server/graphs/kv/graph.test.ts b/x-pack/plugins/integration_assistant/server/graphs/kv/graph.test.ts index 4b995c9b8f31f4..9c16cf2fb78647 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/kv/graph.test.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/kv/graph.test.ts @@ -29,11 +29,7 @@ describe('KVGraph', () => { it('Ensures that the graph compiles', async () => { // When getKVGraph runs, langgraph compiles the graph it will error if the graph has any issues. // Common issues for example detecting a node has no next step, or there is a infinite loop between them. - try { - await getKVGraph({ model, client }); - } catch (error) { - fail(`getKVGraph threw an error: ${error}`); - } + await getKVGraph({ model, client }); }); }); }); diff --git a/x-pack/plugins/integration_assistant/server/graphs/kv/validate.ts b/x-pack/plugins/integration_assistant/server/graphs/kv/validate.ts index e130a69910076f..6781f5cfa46d9b 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/kv/validate.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/kv/validate.ts @@ -10,8 +10,11 @@ import { ESProcessorItem } from '../../../common'; import type { KVState } from '../../types'; import type { HandleKVNodeParams } from './types'; import { testPipeline } from '../../util'; -import { onFailure, removeProcessor } from './constants'; -import { createGrokProcessor } from '../../util/processors'; +import { + createGrokProcessor, + createPassthroughFailureProcessor, + createRemoveProcessor, +} from '../../util/processors'; interface StructuredLogResult { [packageName: string]: { [dataStreamName: string]: unknown }; @@ -65,7 +68,7 @@ export async function handleHeaderValidate({ }: HandleKVNodeParams): Promise> { const grokPattern = state.grokPattern; const grokProcessor = createGrokProcessor([grokPattern]); - const pipeline = { processors: grokProcessor, on_failure: [onFailure] }; + const pipeline = { processors: grokProcessor, on_failure: [createPassthroughFailureProcessor()] }; const { pipelineResults, errors } = (await testPipeline(state.logSamples, pipeline, client)) as { pipelineResults: GrokResult[]; @@ -94,7 +97,10 @@ async function verifyKVProcessor( client: IScopedClusterClient ): Promise<{ errors: object[] }> { // This processor removes the original message field in the output - const pipeline = { processors: [kvProcessor[0], removeProcessor], on_failure: [onFailure] }; + const pipeline = { + processors: [kvProcessor[0], createRemoveProcessor()], + on_failure: [createPassthroughFailureProcessor()], + }; const { errors } = await testPipeline(formattedSamples, pipeline, client); return { errors }; } @@ -104,7 +110,10 @@ async function buildJSONSamples( processors: object[], client: IScopedClusterClient ): Promise { - const pipeline = { processors: [...processors, removeProcessor], on_failure: [onFailure] }; + const pipeline = { + processors: [...processors, createRemoveProcessor()], + on_failure: [createPassthroughFailureProcessor()], + }; const { pipelineResults } = (await testPipeline(samples, pipeline, client)) as { pipelineResults: StructuredLogResult[]; }; diff --git a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/constants.ts b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/constants.ts index ca29ba284fc06d..065daa5268cb66 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/constants.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/constants.ts @@ -5,7 +5,10 @@ * 2.0. */ -export const EX_ANSWER_LOG_TYPE = { - log_type: 'structured', - header: true, +import { SamplesFormat } from '../../../common'; + +export const EX_ANSWER_LOG_TYPE: SamplesFormat = { + name: 'csv', + header: false, + columns: ['ip', 'timestamp', 'request', 'status', '', 'bytes'], }; diff --git a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/detection.test.ts b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/detection.test.ts index df78f1b9a04891..a0230e0347af8e 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/detection.test.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/detection.test.ts @@ -13,17 +13,26 @@ import { FakeLLM } from '@langchain/core/utils/testing'; import { logFormatDetectionTestState } from '../../../__jest__/fixtures/log_type_detection'; import type { LogFormatDetectionState } from '../../types'; import { handleLogFormatDetection } from './detection'; +import { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; const model = new FakeLLM({ - response: '{ "log_type": "structured"}', + response: '{ "name": "structured"}', }) as unknown as ActionsClientChatOpenAI | ActionsClientSimpleChatModel; const state: LogFormatDetectionState = logFormatDetectionTestState; describe('Testing log type detection handler', () => { it('handleLogFormatDetection()', async () => { - const response = await handleLogFormatDetection({ state, model }); - expect(response.samplesFormat).toStrictEqual({ name: 'structured' }); + const client = { + asCurrentUser: { + ingest: { + simulate: jest.fn(), + }, + }, + } as unknown as IScopedClusterClient; + + const response = await handleLogFormatDetection({ state, model, client }); + expect(response.samplesFormat).toStrictEqual({ name: 'structured', header: false }); expect(response.lastExecutedChain).toBe('logFormatDetection'); }); }); diff --git a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/detection.ts b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/detection.ts index 4920adb609967a..a8334432a0211f 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/detection.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/detection.ts @@ -8,6 +8,7 @@ import { JsonOutputParser } from '@langchain/core/output_parsers'; import type { LogFormatDetectionState } from '../../types'; import { LOG_FORMAT_DETECTION_PROMPT } from './prompts'; import type { LogDetectionNodeParams } from './types'; +import { SamplesFormat } from '../../../common'; const MaxLogSamplesInPrompt = 5; @@ -23,13 +24,23 @@ export async function handleLogFormatDetection({ ? state.logSamples.slice(0, MaxLogSamplesInPrompt) : state.logSamples; - const detectedLogFormatAnswer = await logFormatDetectionNode.invoke({ + const logFormatDetectionResult = await logFormatDetectionNode.invoke({ ex_answer: state.exAnswer, log_samples: samples, + package_title: state.packageTitle, + datastream_title: state.dataStreamTitle, }); - const logFormat = detectedLogFormatAnswer.log_type; - const header = detectedLogFormatAnswer.header; + let samplesFormat: SamplesFormat = { name: 'unsupported' }; - return { samplesFormat: { name: logFormat }, header, lastExecutedChain: 'logFormatDetection' }; + try { + samplesFormat = SamplesFormat.parse(logFormatDetectionResult); + if (samplesFormat.header === undefined) { + samplesFormat.header = false; + } + } catch (error) { + // If the LLM fails to produce the output of specified format, we will default to unsupported. + } + + return { samplesFormat, header: samplesFormat.header, lastExecutedChain: 'logFormatDetection' }; } diff --git a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.test.ts b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.test.ts index 361852f051e502..a8a8494de76266 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.test.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.test.ts @@ -29,11 +29,7 @@ describe('LogFormatDetectionGraph', () => { it('Ensures that the graph compiles', async () => { // When getLogFormatDetectionGraph runs, langgraph compiles the graph it will error if the graph has any issues. // Common issues for example detecting a node has no next step, or there is a infinite loop between them. - try { - await getLogFormatDetectionGraph({ model, client }); - } catch (error) { - fail(`getLogFormatDetectionGraph threw an error: ${error}`); - } + await getLogFormatDetectionGraph({ model, client }); }); }); }); diff --git a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.ts b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.ts index 4a3f2e2536266d..95d624a7436c72 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/graph.ts @@ -10,6 +10,7 @@ import { END, START, StateGraph } from '@langchain/langgraph'; import type { LogFormatDetectionState } from '../../types'; import { EX_ANSWER_LOG_TYPE } from './constants'; import { handleLogFormatDetection } from './detection'; +import { handleCSV } from '../csv/csv'; import { ESProcessorItem, SamplesFormat } from '../../../common'; import { getKVGraph } from '../kv/graph'; import { LogDetectionGraphParams, LogDetectionBaseNodeParams } from './types'; @@ -29,6 +30,14 @@ const graphState: StateGraphArgs['channels'] = { value: (x: string, y?: string) => y ?? x, default: () => '', }, + packageTitle: { + value: (x: string, y?: string) => y ?? x, + default: () => '', + }, + dataStreamTitle: { + value: (x: string, y?: string) => y ?? x, + default: () => '', + }, logSamples: { value: (x: string[], y?: string[]) => y ?? x, default: () => [], @@ -94,9 +103,9 @@ function logFormatRouter({ state }: LogDetectionBaseNodeParams): string { if (state.samplesFormat.name === LogFormat.UNSTRUCTURED) { return 'unstructured'; } - // if (state.samplesFormat === LogFormat.CSV) { - // return 'csv'; - // } + if (state.samplesFormat.name === LogFormat.CSV) { + return 'csv'; + } return 'unsupported'; } @@ -107,15 +116,16 @@ export async function getLogFormatDetectionGraph({ model, client }: LogDetection .addNode('modelInput', (state: LogFormatDetectionState) => modelInput({ state })) .addNode('modelOutput', (state: LogFormatDetectionState) => modelOutput({ state })) .addNode('handleLogFormatDetection', (state: LogFormatDetectionState) => - handleLogFormatDetection({ state, model }) + handleLogFormatDetection({ state, model, client }) ) .addNode('handleKVGraph', await getKVGraph({ model, client })) .addNode('handleUnstructuredGraph', await getUnstructuredGraph({ model, client })) - // .addNode('handleCsvGraph', (state: LogFormatDetectionState) => getCompiledCsvGraph({state, model})) + .addNode('handleCSV', (state: LogFormatDetectionState) => handleCSV({ state, model, client })) .addEdge(START, 'modelInput') .addEdge('modelInput', 'handleLogFormatDetection') .addEdge('handleKVGraph', 'modelOutput') .addEdge('handleUnstructuredGraph', 'modelOutput') + .addEdge('handleCSV', 'modelOutput') .addEdge('modelOutput', END) .addConditionalEdges( 'handleLogFormatDetection', @@ -123,7 +133,7 @@ export async function getLogFormatDetectionGraph({ model, client }: LogDetection { structured: 'handleKVGraph', unstructured: 'handleUnstructuredGraph', - // csv: 'handleCsvGraph', + csv: 'handleCSV', unsupported: 'modelOutput', } ); diff --git a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/prompts.ts b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/prompts.ts index 74ba8f719f8752..71246d46363cb9 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/prompts.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/prompts.ts @@ -8,30 +8,27 @@ import { ChatPromptTemplate } from '@langchain/core/prompts'; export const LOG_FORMAT_DETECTION_PROMPT = ChatPromptTemplate.fromMessages([ [ 'system', - `You are a helpful, expert assistant in identifying different log types based on the format. - -Here is some context for you to reference for your task, read it carefully as you will get questions about it later: - - -{log_samples} - -`, + `You are a helpful, expert assistant specializing in all things logs. You're great at analyzing log samples.`, ], [ 'human', - `Looking at the log samples , our goal is to identify the syslog type based on the guidelines below. -Follow these steps to identify the log format type: -1. Go through each log sample and identify the log format type. + `The current task is to identify the log format from the provided samples based on the guidelines below. + +The samples apply to the data stream {datastream_title} inside the integration package {package_title}. + +Follow these steps to do this: +1. Go through each log sample and identify the log format. Output this as "name: ". 2. If the samples have any or all of priority, timestamp, loglevel, hostname, ipAddress, messageId in the beginning information then set "header: true". 3. If the samples have a syslog header then set "header: true" , else set "header: false". If you are unable to determine the syslog header presence then set "header: false". -4. If the log samples have structured message body with key-value pairs then classify it as "log_type: structured". Look for a flat list of key-value pairs, often separated by spaces, commas, or other delimiters. +4. If the log samples have structured message body with key-value pairs then classify it as "name: structured". Look for a flat list of key-value pairs, often separated by spaces, commas, or other delimiters. 5. Consider variations in formatting, such as quotes around values ("key=value", key="value"), special characters in keys or values, or escape sequences. -6. If the log samples have unstructured body like a free-form text then classify it as "log_type: unstructured". -7. If the log samples follow a csv format then classify it as "log_type: csv". -8. If the samples are identified as "csv" and there is a csv header then set "header: true" , else set "header: false". -9. If you do not find the log format in any of the above categories then classify it as "log_type: unsupported". +6. If the log samples have unstructured body like a free-form text then classify it as "name: unstructured". +7. If the log samples follow a csv format then classify it with "name: csv". There are two sub-cases for csv: + a. If there is a csv header then set "header: true". + b. If there is no csv header then set "header: false" and try to find good names for the columns in the "columns" array by looking into the values of data in those columns. For each column, if you are unable to find good name candidate for it then output an empty string, like in the example. +8. If you cannot put the format into any of the above categories then classify it with "name: unsupported". - You ALWAYS follow these guidelines when writing your response: +You ALWAYS follow these guidelines when writing your response: - Do not respond with anything except the updated current mapping JSON object enclosed with 3 backticks (\`). See example response below. @@ -42,7 +39,13 @@ A: Please find the JSON object below: \`\`\`json {ex_answer} \`\`\` -`, + + +Please process these log samples: + +{log_samples} + +`, ], ['ai', 'Please find the JSON object below:'], ]); diff --git a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/types.ts b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/types.ts index 8f73988ec30cc4..a076b17a160f15 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/types.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/log_type_detection/types.ts @@ -14,6 +14,7 @@ export interface LogDetectionBaseNodeParams { export interface LogDetectionNodeParams extends LogDetectionBaseNodeParams { model: ChatModels; + client: IScopedClusterClient; } export interface LogDetectionGraphParams { diff --git a/x-pack/plugins/integration_assistant/server/graphs/unstructured/constants.ts b/x-pack/plugins/integration_assistant/server/graphs/unstructured/constants.ts index b0e36de9be85d3..beba111e39a188 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/unstructured/constants.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/unstructured/constants.ts @@ -17,11 +17,3 @@ export const GROK_ERROR_EXAMPLE_ANSWER = { '%{TIMESTAMP:timestamp}:%{WORD:value1};%{WORD:key2}:%{WORD:value2}:%{GREEDYDATA:message}', ], }; - -export const onFailure = { - append: { - field: 'error.message', - value: - '{% raw %}Processor {{{_ingest.on_failure_processor_type}}} with tag {{{_ingest.on_failure_processor_tag}}} in pipeline {{{_ingest.on_failure_pipeline}}} failed with message: {{{_ingest.on_failure_message}}}{% endraw %}', - }, -}; diff --git a/x-pack/plugins/integration_assistant/server/graphs/unstructured/graph.test.ts b/x-pack/plugins/integration_assistant/server/graphs/unstructured/graph.test.ts index 60a9bdc4329de0..456adb8eebcc60 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/unstructured/graph.test.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/unstructured/graph.test.ts @@ -29,11 +29,7 @@ describe('UnstructuredGraph', () => { it('Ensures that the graph compiles', async () => { // When getUnstructuredGraph runs, langgraph compiles the graph it will error if the graph has any issues. // Common issues for example detecting a node has no next step, or there is a infinite loop between them. - try { - await getUnstructuredGraph({ model, client }); - } catch (error) { - fail(`getUnstructuredGraph threw an error: ${error}`); - } + await getUnstructuredGraph({ model, client }); }); }); }); diff --git a/x-pack/plugins/integration_assistant/server/graphs/unstructured/validate.ts b/x-pack/plugins/integration_assistant/server/graphs/unstructured/validate.ts index eea7602b641d62..b616f60080150e 100644 --- a/x-pack/plugins/integration_assistant/server/graphs/unstructured/validate.ts +++ b/x-pack/plugins/integration_assistant/server/graphs/unstructured/validate.ts @@ -8,8 +8,7 @@ import type { UnstructuredLogState } from '../../types'; import type { HandleUnstructuredNodeParams, LogResult } from './types'; import { testPipeline } from '../../util'; -import { onFailure } from './constants'; -import { createGrokProcessor } from '../../util/processors'; +import { createGrokProcessor, createPassthroughFailureProcessor } from '../../util/processors'; export async function handleUnstructuredValidate({ state, @@ -17,10 +16,10 @@ export async function handleUnstructuredValidate({ }: HandleUnstructuredNodeParams): Promise> { const grokPatterns = state.grokPatterns; const grokProcessor = createGrokProcessor(grokPatterns); - const pipeline = { processors: grokProcessor, on_failure: [onFailure] }; + const pipeline = { processors: grokProcessor, on_failure: [createPassthroughFailureProcessor()] }; + const packageName = state.packageName; const dataStreamName = state.dataStreamName; - const { pipelineResults, errors } = (await testPipeline(state.logSamples, pipeline, client)) as { pipelineResults: LogResult[]; errors: object[]; diff --git a/x-pack/plugins/integration_assistant/server/lib/errors/unparseable_csv_error.ts b/x-pack/plugins/integration_assistant/server/lib/errors/unparseable_csv_error.ts new file mode 100644 index 00000000000000..ab4010707d6649 --- /dev/null +++ b/x-pack/plugins/integration_assistant/server/lib/errors/unparseable_csv_error.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { KibanaResponseFactory } from '@kbn/core/server'; +import { ErrorThatHandlesItsOwnResponse } from './types'; +import { GenerationErrorCode } from '../../../common/constants'; +import { + GenerationErrorAttributes, + GenerationErrorBody, +} from '../../../common/api/generation_error'; + +const errorCode = GenerationErrorCode.UNPARSEABLE_CSV_DATA; + +export interface CSVParseError { + message: string[]; +} + +export class UnparseableCSVFormatError extends Error implements ErrorThatHandlesItsOwnResponse { + attributes: GenerationErrorAttributes; + + constructor(csvParseErrors: CSVParseError[]) { + super(errorCode); + this.attributes = { + errorCode, + underlyingMessages: csvParseErrors.flatMap((error) => error.message), + }; + } + + public sendResponse(res: KibanaResponseFactory) { + const body: GenerationErrorBody = { + message: errorCode, + attributes: this.attributes, + }; + return res.customError({ + statusCode: 422, + body, + }); + } +} diff --git a/x-pack/plugins/integration_assistant/server/lib/errors/unsupported_error.ts b/x-pack/plugins/integration_assistant/server/lib/errors/unsupported_error.ts index 79c4f2ccf69a1e..7ab4e0569ca835 100644 --- a/x-pack/plugins/integration_assistant/server/lib/errors/unsupported_error.ts +++ b/x-pack/plugins/integration_assistant/server/lib/errors/unsupported_error.ts @@ -7,10 +7,10 @@ import { KibanaResponseFactory } from '@kbn/core/server'; import { ErrorThatHandlesItsOwnResponse } from './types'; -import { ErrorCode } from '../../../common/constants'; +import { GenerationErrorCode } from '../../../common/constants'; export class UnsupportedLogFormatError extends Error implements ErrorThatHandlesItsOwnResponse { - private readonly errorCode: string = ErrorCode.UNSUPPORTED_LOG_SAMPLES_FORMAT; + private readonly errorCode: string = GenerationErrorCode.UNSUPPORTED_LOG_SAMPLES_FORMAT; // eslint-disable-next-line @typescript-eslint/no-useless-constructor constructor(message: string) { diff --git a/x-pack/plugins/integration_assistant/server/routes/analyze_logs_routes.ts b/x-pack/plugins/integration_assistant/server/routes/analyze_logs_routes.ts index 2f0f3db47a7a97..639cd62f275b1b 100644 --- a/x-pack/plugins/integration_assistant/server/routes/analyze_logs_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/analyze_logs_routes.ts @@ -18,7 +18,7 @@ import { buildRouteValidationWithZod } from '../util/route_validation'; import { withAvailability } from './with_availability'; import { isErrorThatHandlesItsOwnResponse, UnsupportedLogFormatError } from '../lib/errors'; import { handleCustomErrors } from './routes_util'; -import { ErrorCode } from '../../common/constants'; +import { GenerationErrorCode } from '../../common/constants'; export function registerAnalyzeLogsRoutes( router: IRouter @@ -43,7 +43,14 @@ export function registerAnalyzeLogsRoutes( }, }, withAvailability(async (context, req, res): Promise> => { - const { packageName, dataStreamName, logSamples, langSmithOptions } = req.body; + const { + packageName, + dataStreamName, + packageTitle, + dataStreamTitle, + logSamples, + langSmithOptions, + } = req.body; const services = await context.resolve(['core']); const { client } = services.core.elasticsearch; const { getStartServices, logger } = await context.integrationAssistant; @@ -79,18 +86,20 @@ export function registerAnalyzeLogsRoutes( const logFormatParameters = { packageName, dataStreamName, + packageTitle, + dataStreamTitle, logSamples, }; const graph = await getLogFormatDetectionGraph({ model, client }); const graphResults = await graph.invoke(logFormatParameters, options); const graphLogFormat = graphResults.results.samplesFormat.name; - if (graphLogFormat === 'unsupported' || graphLogFormat === 'csv') { - throw new UnsupportedLogFormatError(ErrorCode.UNSUPPORTED_LOG_SAMPLES_FORMAT); + if (graphLogFormat === 'unsupported') { + throw new UnsupportedLogFormatError(GenerationErrorCode.UNSUPPORTED_LOG_SAMPLES_FORMAT); } return res.ok({ body: AnalyzeLogsResponse.parse(graphResults) }); } catch (err) { try { - handleCustomErrors(err, ErrorCode.RECURSION_LIMIT_ANALYZE_LOGS); + handleCustomErrors(err, GenerationErrorCode.RECURSION_LIMIT_ANALYZE_LOGS); } catch (e) { if (isErrorThatHandlesItsOwnResponse(e)) { return e.sendResponse(res); diff --git a/x-pack/plugins/integration_assistant/server/routes/build_integration_routes.ts b/x-pack/plugins/integration_assistant/server/routes/build_integration_routes.ts index 1a7ecb58a20622..6d7e5155a3d23a 100644 --- a/x-pack/plugins/integration_assistant/server/routes/build_integration_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/build_integration_routes.ts @@ -13,7 +13,7 @@ import { buildRouteValidationWithZod } from '../util/route_validation'; import { withAvailability } from './with_availability'; import { isErrorThatHandlesItsOwnResponse } from '../lib/errors'; import { handleCustomErrors } from './routes_util'; -import { ErrorCode } from '../../common/constants'; +import { GenerationErrorCode } from '../../common/constants'; export function registerIntegrationBuilderRoutes( router: IRouter ) { @@ -42,7 +42,7 @@ export function registerIntegrationBuilderRoutes( }); } catch (err) { try { - handleCustomErrors(err, ErrorCode.RECURSION_LIMIT); + handleCustomErrors(err, GenerationErrorCode.RECURSION_LIMIT); } catch (e) { if (isErrorThatHandlesItsOwnResponse(e)) { return e.sendResponse(response); diff --git a/x-pack/plugins/integration_assistant/server/routes/categorization_routes.ts b/x-pack/plugins/integration_assistant/server/routes/categorization_routes.ts index 635ef08dcdf9c0..c437f6fc35546b 100644 --- a/x-pack/plugins/integration_assistant/server/routes/categorization_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/categorization_routes.ts @@ -22,7 +22,7 @@ import { buildRouteValidationWithZod } from '../util/route_validation'; import { withAvailability } from './with_availability'; import { isErrorThatHandlesItsOwnResponse } from '../lib/errors'; import { handleCustomErrors } from './routes_util'; -import { ErrorCode } from '../../common/constants'; +import { GenerationErrorCode } from '../../common/constants'; export function registerCategorizationRoutes( router: IRouter @@ -103,7 +103,7 @@ export function registerCategorizationRoutes( return res.ok({ body: CategorizationResponse.parse(results) }); } catch (err) { try { - handleCustomErrors(err, ErrorCode.RECURSION_LIMIT); + handleCustomErrors(err, GenerationErrorCode.RECURSION_LIMIT); } catch (e) { if (isErrorThatHandlesItsOwnResponse(e)) { return e.sendResponse(res); diff --git a/x-pack/plugins/integration_assistant/server/routes/ecs_routes.ts b/x-pack/plugins/integration_assistant/server/routes/ecs_routes.ts index 12d77c66a11322..43ca0fe396cae5 100644 --- a/x-pack/plugins/integration_assistant/server/routes/ecs_routes.ts +++ b/x-pack/plugins/integration_assistant/server/routes/ecs_routes.ts @@ -18,7 +18,7 @@ import { buildRouteValidationWithZod } from '../util/route_validation'; import { withAvailability } from './with_availability'; import { isErrorThatHandlesItsOwnResponse } from '../lib/errors'; import { handleCustomErrors } from './routes_util'; -import { ErrorCode } from '../../common/constants'; +import { GenerationErrorCode } from '../../common/constants'; export function registerEcsRoutes(router: IRouter) { router.versioned @@ -97,7 +97,7 @@ export function registerEcsRoutes(router: IRouter) { router.versioned @@ -51,7 +51,7 @@ export function registerPipelineRoutes(router: IRouter) { router.versioned @@ -94,7 +94,7 @@ export function registerRelatedRoutes(router: IRouter { it('should throw a RecursionLimitError when given a GraphRecursionError', () => { const errorMessage = 'Recursion limit exceeded'; - const errorCode = ErrorCode.RECURSION_LIMIT; + const errorCode = GenerationErrorCode.RECURSION_LIMIT; const recursionError = new GraphRecursionError(errorMessage); expect(() => { @@ -26,7 +26,7 @@ describe('handleError', () => { it('should rethrow the error when given an error that is not a GraphRecursionError', () => { const errorMessage = 'Some other error'; - const errorCode = ErrorCode.RECURSION_LIMIT; + const errorCode = GenerationErrorCode.RECURSION_LIMIT; const otherError = new Error(errorMessage); expect(() => { diff --git a/x-pack/plugins/integration_assistant/server/routes/routes_util.ts b/x-pack/plugins/integration_assistant/server/routes/routes_util.ts index 5622392cd06a92..9773bb42bba6c1 100644 --- a/x-pack/plugins/integration_assistant/server/routes/routes_util.ts +++ b/x-pack/plugins/integration_assistant/server/routes/routes_util.ts @@ -6,7 +6,7 @@ */ import { GraphRecursionError } from '@langchain/langgraph'; -import { ErrorCode } from '../../common/constants'; +import { GenerationErrorCode } from '../../common/constants'; import { RecursionLimitError } from '../lib/errors'; /** @@ -21,7 +21,9 @@ import { RecursionLimitError } from '../lib/errors'; */ export function handleCustomErrors( err: Error, - recursionErrorCode: ErrorCode.RECURSION_LIMIT | ErrorCode.RECURSION_LIMIT_ANALYZE_LOGS + recursionErrorCode: + | GenerationErrorCode.RECURSION_LIMIT + | GenerationErrorCode.RECURSION_LIMIT_ANALYZE_LOGS ) { if (err instanceof GraphRecursionError) { throw new RecursionLimitError(err.message, recursionErrorCode); diff --git a/x-pack/plugins/integration_assistant/server/types.ts b/x-pack/plugins/integration_assistant/server/types.ts index 3c17761c495b06..a8f0d86a925ba7 100644 --- a/x-pack/plugins/integration_assistant/server/types.ts +++ b/x-pack/plugins/integration_assistant/server/types.ts @@ -109,6 +109,8 @@ export interface LogFormatDetectionState { lastExecutedChain: string; packageName: string; dataStreamName: string; + packageTitle: string; + dataStreamTitle: string; logSamples: string[]; jsonSamples: string[]; exAnswer: string; @@ -117,7 +119,7 @@ export interface LogFormatDetectionState { header: boolean; ecsVersion: string; results: object; - additionalProcessors: ESProcessorItem[]; // # This will be generated in the sub-graphs + additionalProcessors: ESProcessorItem[]; // Generated in handleXXX nodes or subgraphs. } export interface KVState { diff --git a/x-pack/plugins/integration_assistant/server/util/index.ts b/x-pack/plugins/integration_assistant/server/util/index.ts index 9017f6e216ea84..019cea18885021 100644 --- a/x-pack/plugins/integration_assistant/server/util/index.ts +++ b/x-pack/plugins/integration_assistant/server/util/index.ts @@ -17,5 +17,5 @@ export { export { generateFields, mergeSamples } from './samples'; export { deepCopy, generateUniqueId } from './util'; -export { testPipeline } from './pipeline'; +export { testPipeline, createJSONInput } from './pipeline'; export { combineProcessors } from './processors'; diff --git a/x-pack/plugins/integration_assistant/server/util/pipeline.ts b/x-pack/plugins/integration_assistant/server/util/pipeline.ts index c9c58c78b6c9ab..5df0ad0ea49173 100644 --- a/x-pack/plugins/integration_assistant/server/util/pipeline.ts +++ b/x-pack/plugins/integration_assistant/server/util/pipeline.ts @@ -5,6 +5,8 @@ * 2.0. */ import type { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; +import { ESProcessorItem } from '../../common'; +import { createPassthroughFailureProcessor, createRemoveProcessor } from './processors'; interface DocTemplate { _index: string; @@ -29,15 +31,17 @@ export async function testPipeline( samples: string[], pipeline: object, client: IScopedClusterClient -): Promise<{ pipelineResults: object[]; errors: object[] }> { +): Promise<{ pipelineResults: Array<{ [key: string]: unknown }>; errors: object[] }> { const docs = samples.map((sample) => formatSample(sample)); - const pipelineResults: object[] = []; + const pipelineResults: Array<{ [key: string]: unknown }> = []; const errors: object[] = []; try { const output = await client.asCurrentUser.ingest.simulate({ docs, pipeline }); for (const doc of output.docs) { - if (doc.doc?._source?.error) { + if (!doc) { + // Nothing to do – the document was dropped. + } else if (doc.doc?._source?.error) { errors.push(doc.doc._source.error); } else if (doc.doc?._source) { pipelineResults.push(doc.doc._source); @@ -49,3 +53,16 @@ export async function testPipeline( return { pipelineResults, errors }; } + +export async function createJSONInput( + processors: ESProcessorItem[], + formattedSamples: string[], + client: IScopedClusterClient +): Promise<{ pipelineResults: Array<{ [key: string]: unknown }>; errors: object[] }> { + const pipeline = { + processors: [...processors, createRemoveProcessor()], + on_failure: [createPassthroughFailureProcessor()], + }; + const { pipelineResults, errors } = await testPipeline(formattedSamples, pipeline, client); + return { pipelineResults, errors }; +} diff --git a/x-pack/plugins/integration_assistant/server/util/processors.ts b/x-pack/plugins/integration_assistant/server/util/processors.ts index e77140b91a926b..10283bdeff9d88 100644 --- a/x-pack/plugins/integration_assistant/server/util/processors.ts +++ b/x-pack/plugins/integration_assistant/server/util/processors.ts @@ -77,3 +77,64 @@ export function createKVProcessor(kvInput: KVProcessor, state: KVState): ESProce const kvProcessor = load(renderedTemplate) as ESProcessorItem; return kvProcessor; } + +// Processor for the csv input to convert it to JSON. +export function createCSVProcessor(source: string, targets: string[]): ESProcessorItem { + return { + csv: { + field: source, + target_fields: targets, + description: 'Parse CSV input', + tag: 'parse_csv', + }, + }; +} + +// Trivial processor for the on_failure part of the pipeline. +// Use only if the source of error is not necessary. +export function createPassthroughFailureProcessor(): ESProcessorItem { + return { + append: { + field: 'error.message', + description: 'Append the error message as-is', + tag: 'append_error_message', + value: '{{{_ingest.on_failure_message}}}', + }, + }; +} + +// Processor to remove the message field. +export function createRemoveProcessor(): ESProcessorItem { + return { + remove: { + field: 'message', + ignore_missing: true, + description: 'Remove the message field', + tag: 'remove_message_field', + }, + }; +} + +// Processor to drop the specific values. +// values is a record of key value pairs to match against the fields +// root is the root of the fields to match against +export function createDropProcessor( + values: Record, + prefix: string[], + tag: string, + description: string +): ESProcessorItem { + const prefixExpression = prefix.join('?.'); + const conditions = Object.entries(values) + .map(([key, value]) => `ctx.${prefixExpression}?.${key} == '${String(value)}'`) + .join(' && '); + + return { + drop: { + if: conditions, + ignore_failure: true, + description, + tag, + }, + }; +} diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 0031fbbb5666f5..819a309616b52c 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -24773,7 +24773,6 @@ "xpack.integrationAssistant.step.dataStream.integrationNameDescription": "Le nom du package est utilisé pour faire référence à l'intégration dans le pipeline d'ingestion d'Elastic", "xpack.integrationAssistant.step.dataStream.integrationNameTitle": "Définir le nom du package", "xpack.integrationAssistant.step.dataStream.logsSample.description": "Glissez et déposez un fichier ou parcourez les fichiers.", - "xpack.integrationAssistant.step.dataStream.logsSample.description2": "Format JSON/NDJSON", "xpack.integrationAssistant.step.dataStream.logsSample.errorCanNotRead": "Impossible de lire le fichier de logs exemple", "xpack.integrationAssistant.step.dataStream.logsSample.errorEmpty": "Le fichier de logs exemple est vide", "xpack.integrationAssistant.step.dataStream.logsSample.errorNotArray": "Le fichier de logs exemple n'est pas un tableau", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 801f3aed69cd68..249c3a9e9caff2 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24520,7 +24520,6 @@ "xpack.integrationAssistant.step.dataStream.integrationNameDescription": "このパッケージ名は、Elasticのインジェストパイプラインの統合を参照するために使用されます", "xpack.integrationAssistant.step.dataStream.integrationNameTitle": "パッケージ名を定義", "xpack.integrationAssistant.step.dataStream.logsSample.description": "ファイルをドラッグアンドドロップするか、ファイルを参照します", - "xpack.integrationAssistant.step.dataStream.logsSample.description2": "JSON/NDJSON形式", "xpack.integrationAssistant.step.dataStream.logsSample.errorCanNotRead": "ログサンプルファイルを読み取れませんでした", "xpack.integrationAssistant.step.dataStream.logsSample.errorEmpty": "ログサンプルファイルが空です", "xpack.integrationAssistant.step.dataStream.logsSample.errorNotArray": "ログサンプルファイルは配列ではありません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 312eb075003c8e..7ca37ec4caef07 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24554,7 +24554,6 @@ "xpack.integrationAssistant.step.dataStream.integrationNameDescription": "软件包名称用于在 Elastic 采集管道中引用集成", "xpack.integrationAssistant.step.dataStream.integrationNameTitle": "定义软件包名称", "xpack.integrationAssistant.step.dataStream.logsSample.description": "拖放文件或浏览文件。", - "xpack.integrationAssistant.step.dataStream.logsSample.description2": "JSON/NDJSON 格式", "xpack.integrationAssistant.step.dataStream.logsSample.errorCanNotRead": "无法读取日志样例文件", "xpack.integrationAssistant.step.dataStream.logsSample.errorEmpty": "日志样例文件为空", "xpack.integrationAssistant.step.dataStream.logsSample.errorNotArray": "日志样例文件不是数组", From 0c5a94bb5738db01a08285dfdb589593bc81988e Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Mon, 14 Oct 2024 11:32:45 +0100 Subject: [PATCH 17/38] [Entity Store] Do not require full entity definition to execute enrich policy (remove magic number) (#195961) --- .../elasticsearch_assets/enrich_policy.ts | 8 +++++--- .../task/field_retention_enrichment_task.ts | 14 +++++++------- .../united_entity_definitions/entity_types/host.ts | 3 ++- .../entity_types/index.ts | 4 ++-- .../united_entity_definitions/entity_types/user.ts | 3 ++- .../get_united_definition.ts | 5 +++++ 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/enrich_policy.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/enrich_policy.ts index e849eb0b447a5c..7d6fc6fd8bc24c 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/enrich_policy.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/elasticsearch_assets/enrich_policy.ts @@ -10,11 +10,13 @@ import type { EnrichPutPolicyRequest } from '@elastic/elasticsearch/lib/api/type import { getEntitiesIndexName } from '../utils'; import type { UnitedEntityDefinition } from '../united_entity_definitions'; +type DefinitionMetadata = Pick; + export const getFieldRetentionEnrichPolicyName = ({ namespace, entityType, version, -}: Pick): string => { +}: DefinitionMetadata): string => { return `entity_store_field_retention_${entityType}_${namespace}_v${version}`; }; @@ -48,7 +50,7 @@ export const executeFieldRetentionEnrichPolicy = async ({ unitedDefinition, logger, }: { - unitedDefinition: UnitedEntityDefinition; + unitedDefinition: DefinitionMetadata; esClient: ElasticsearchClient; logger: Logger; }): Promise<{ executed: boolean }> => { @@ -72,7 +74,7 @@ export const deleteFieldRetentionEnrichPolicy = async ({ esClient, }: { esClient: ElasticsearchClient; - unitedDefinition: UnitedEntityDefinition; + unitedDefinition: DefinitionMetadata; }) => { const name = getFieldRetentionEnrichPolicyName(unitedDefinition); return esClient.enrich.deletePolicy({ name }, { ignore: [404] }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts index f7dac247dd9fd9..d008c3afe6f171 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/task/field_retention_enrichment_task.ts @@ -20,7 +20,10 @@ import { } from './state'; import { INTERVAL, SCOPE, TIMEOUT, TYPE, VERSION } from './constants'; import type { EntityAnalyticsRoutesDeps } from '../../types'; -import { getAvailableEntityTypes, getUnitedEntityDefinition } from '../united_entity_definitions'; +import { + getAvailableEntityTypes, + getUnitedEntityDefinitionVersion, +} from '../united_entity_definitions'; import { executeFieldRetentionEnrichPolicy } from '../elasticsearch_assets'; const logFactory = @@ -63,13 +66,10 @@ export const registerEntityStoreFieldRetentionEnrichTask = ({ const [coreStart, _] = await getStartServices(); const esClient = coreStart.elasticsearch.client.asInternalUser; - const unitedDefinition = getUnitedEntityDefinition({ - namespace, - entityType, - fieldHistoryLength: 10, // we are not using this value so it can be anything - }); + const unitedDefinitionVersion = getUnitedEntityDefinitionVersion(entityType); + return executeFieldRetentionEnrichPolicy({ - unitedDefinition, + unitedDefinition: { namespace, entityType, version: unitedDefinitionVersion }, esClient, logger, }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/host.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/host.ts index ee39ffff529ff6..e8d812d73ff272 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/host.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/host.ts @@ -8,11 +8,12 @@ import { collectValuesWithLength } from '../definition_utils'; import type { UnitedDefinitionBuilder } from '../types'; +export const HOST_DEFINITION_VERSION = '1.0.0'; export const getHostUnitedDefinition: UnitedDefinitionBuilder = (fieldHistoryLength: number) => { const collect = collectValuesWithLength(fieldHistoryLength); return { entityType: 'host', - version: '1.0.0', + version: HOST_DEFINITION_VERSION, fields: [ collect({ field: 'host.domain' }), collect({ field: 'host.hostname' }), diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/index.ts index c0600d45fa8b53..4193c07f308fb9 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/index.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/index.ts @@ -5,6 +5,6 @@ * 2.0. */ -export { getHostUnitedDefinition } from './host'; -export { getUserUnitedDefinition } from './user'; +export * from './host'; +export * from './user'; export { getCommonUnitedFieldDefinitions } from './common'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/user.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/user.ts index 8d99a6be119124..632d1a685b992f 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/user.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/entity_types/user.ts @@ -7,11 +7,12 @@ import { collectValuesWithLength } from '../definition_utils'; import type { UnitedDefinitionBuilder } from '../types'; +export const USER_DEFINITION_VERSION = '1.0.0'; export const getUserUnitedDefinition: UnitedDefinitionBuilder = (fieldHistoryLength: number) => { const collect = collectValuesWithLength(fieldHistoryLength); return { entityType: 'user', - version: '1.0.0', + version: USER_DEFINITION_VERSION, fields: [ collect({ field: 'user.domain' }), collect({ field: 'user.email' }), diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts index f6afcba8b35890..21214f1bf95fb6 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts @@ -10,6 +10,8 @@ import { getHostUnitedDefinition, getUserUnitedDefinition, getCommonUnitedFieldDefinitions, + USER_DEFINITION_VERSION, + HOST_DEFINITION_VERSION, } from './entity_types'; import type { UnitedDefinitionBuilder } from './types'; import { UnitedEntityDefinition } from './united_entity_definition'; @@ -44,5 +46,8 @@ export const getUnitedEntityDefinition = memoize( `${entityType}-${namespace}-${fieldHistoryLength}` ); +export const getUnitedEntityDefinitionVersion = (entityType: EntityType): string => + entityType === 'host' ? HOST_DEFINITION_VERSION : USER_DEFINITION_VERSION; + export const getAvailableEntityTypes = (): EntityType[] => Object.keys(unitedDefinitionBuilders) as EntityType[]; From a3289e440ad88825b1ae17495caf13ef5720ae7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Mon, 14 Oct 2024 11:37:15 +0100 Subject: [PATCH 18/38] [Stateful sidenav] Fix collapsed menu for panels with no landing pages (#195904) --- .../__jest__/build_nav_tree.test.tsx | 81 ++++++++- .../chrome/navigation/__jest__/utils.tsx | 1 - .../chrome/navigation/mocks/storybook.ts | 6 +- .../chrome/navigation/src/services.tsx | 1 - .../shared-ux/chrome/navigation/src/types.ts | 1 - .../ui/components/navigation_section_ui.tsx | 169 +++++++++++------- .../src/ui/hooks/use_accordion_state.ts | 1 + .../observability/public/navigation_tree.ts | 1 - 8 files changed, 189 insertions(+), 72 deletions(-) diff --git a/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx index c2458d8bf1a731..15baf09d04dc30 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx @@ -114,7 +114,7 @@ describe('builds navigation tree', () => { const accordionToggleButton = await findByTestId(/nav-item-group1\s/); accordionToggleButton.click(); - expect(navigateToUrl).not.toHaveBeenCalled(); + expect(navigateToUrl).not.toHaveBeenCalled(); // Should not navigate to the href unmount(); } @@ -138,6 +138,85 @@ describe('builds navigation tree', () => { } }); + test('should render panel opener groups as accordion when the sideNav is collapsed', async () => { + const panelOpenerNode: ChromeProjectNavigationNode = { + id: 'nestedGroup1', + title: 'Nested Group 1', + path: 'group1.nestedGroup1', + renderAs: 'panelOpener', // Should be converted to accordion when sideNav is collapsed + children: [ + { + id: 'item1', + title: 'Item 1', + href: 'https://foo', + path: 'group1.item1', + }, + ], + }; + + const nodes: ChromeProjectNavigationNode = { + id: 'group1', + title: 'Group 1', + path: 'group1', + children: [panelOpenerNode], + }; + + { + // Side nav is collapsed + const { queryAllByTestId, unmount } = renderNavigation({ + navTreeDef: of({ + body: [nodes], + }), + services: { isSideNavCollapsed: true }, + }); + + const accordionButtonLabel = queryAllByTestId('accordionToggleBtn').map((c) => c.textContent); + expect(accordionButtonLabel).toEqual(['Group 1', 'Nested Group 1']); // 2 accordion buttons + + unmount(); + } + + { + // Side nav is not collapsed + const { queryAllByTestId, unmount } = renderNavigation({ + navTreeDef: of({ + body: [nodes], + }), + services: { isSideNavCollapsed: false }, // No conversion to accordion + }); + + const accordionButtonLabel = queryAllByTestId('accordionToggleBtn').map((c) => c.textContent); + + expect(accordionButtonLabel).toEqual(['Group 1']); // Only 1 accordion button (top level) + unmount(); + } + + { + // Panel opener with a link + const { queryAllByTestId, unmount } = renderNavigation({ + navTreeDef: of({ + body: [ + { + ...nodes, + children: [ + { + ...panelOpenerNode, + href: '/foo/bar', // Panel opener with a link should not be converted to accordion + }, + ], + }, + ], + }), + services: { isSideNavCollapsed: true }, // SideNav is collapsed + }); + + const accordionButtonLabel = queryAllByTestId('accordionToggleBtn').map((c) => c.textContent); + + expect(accordionButtonLabel).toEqual(['Group 1']); // Only 1 accordion button (top level) + unmount(); + } + }); + test('should track click event', async () => { const navigateToUrl = jest.fn(); const reportEvent = jest.fn(); diff --git a/packages/shared-ux/chrome/navigation/__jest__/utils.tsx b/packages/shared-ux/chrome/navigation/__jest__/utils.tsx index 21ac68ae06c0f8..9f603709c8efaa 100644 --- a/packages/shared-ux/chrome/navigation/__jest__/utils.tsx +++ b/packages/shared-ux/chrome/navigation/__jest__/utils.tsx @@ -34,7 +34,6 @@ export const getServicesMock = (): NavigationServices => { return { basePath, recentlyAccessed$, - navIsOpen: true, navigateToUrl, activeNodes$: of(activeNodes), isSideNavCollapsed: false, diff --git a/packages/shared-ux/chrome/navigation/mocks/storybook.ts b/packages/shared-ux/chrome/navigation/mocks/storybook.ts index 6ae9fd24ef8f61..47d0bc342a7bdc 100644 --- a/packages/shared-ux/chrome/navigation/mocks/storybook.ts +++ b/packages/shared-ux/chrome/navigation/mocks/storybook.ts @@ -14,15 +14,15 @@ import { EventTracker } from '../src/analytics'; import { NavigationServices } from '../src/types'; type Arguments = NavigationServices; -export type Params = Pick; +export type Params = Pick; export class StorybookMock extends AbstractStorybookMock<{}, NavigationServices> { propArguments = {}; serviceArguments = { - navIsOpen: { + isSideNavCollapsed: { control: 'boolean', - defaultValue: true, + defaultValue: false, }, }; diff --git a/packages/shared-ux/chrome/navigation/src/services.tsx b/packages/shared-ux/chrome/navigation/src/services.tsx index 8f9a1915e3c4df..e7d7f30886b2ec 100644 --- a/packages/shared-ux/chrome/navigation/src/services.tsx +++ b/packages/shared-ux/chrome/navigation/src/services.tsx @@ -44,7 +44,6 @@ export const NavigationKibanaProvider: FC; - navIsOpen: boolean; navigateToUrl: NavigateToUrlFn; activeNodes$: Observable; isSideNavCollapsed: boolean; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx index fde5cf16d03155..6235d30bcf0a9e 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx @@ -55,12 +55,48 @@ const itemIsVisible = (item: ChromeProjectNavigationNode) => { return false; }; -const getRenderAs = (navNode: ChromeProjectNavigationNode): RenderAs => { +const getRenderAs = ( + navNode: ChromeProjectNavigationNode, + { isSideNavCollapsed }: { isSideNavCollapsed: boolean } +): RenderAs => { + if (isSideNavCollapsed && navNode.renderAs === 'panelOpener' && !nodeHasLink(navNode)) + return 'accordion'; // When the side nav is collapsed, we render panel openers as accordions if they don't have a landing page if (navNode.renderAs) return navNode.renderAs; if (!navNode.children) return 'item'; return DEFAULT_RENDER_AS; }; +const getSpaceBefore = ( + navNode: ChromeProjectNavigationNode, + { + isSideNavCollapsed, + treeDepth, + parentNode, + }: { isSideNavCollapsed: boolean; treeDepth: number; parentNode?: ChromeProjectNavigationNode } +): EuiThemeSize | null | undefined => { + const hasChildren = nodeHasChildren(navNode); + const isItem = navNode.renderAs === 'item'; + + if (navNode.spaceBefore === undefined && treeDepth === 1 && hasChildren && !isItem) { + // For groups at level 1 that don't have a space specified we default to add a "m" + // space. For all other groups, unless specified, there is no vertical space. + return DEFAULT_SPACE_BETWEEN_LEVEL_1_GROUPS; + } + + if ( + isSideNavCollapsed && + navNode.renderAs === 'block' && + !!navNode.title && + parentNode?.renderAs === 'accordion' + ) { + // When the side nav is collapsed we control the spacing between groups inside accordions + // for consistency and don't allow custom spacing to be set. + return DEFAULT_SPACE_BETWEEN_LEVEL_1_GROUPS; + } + + return navNode.spaceBefore; +}; + const getTestSubj = (navNode: ChromeProjectNavigationNode, isActive = false): string => { const { id, path, deepLink } = navNode; return classnames(`nav-item`, `nav-item-${path}`, { @@ -70,20 +106,33 @@ const getTestSubj = (navNode: ChromeProjectNavigationNode, isActive = false): st }); }; -const serializeNavNode = (navNode: ChromeProjectNavigationNode) => { +const serializeNavNode = ( + navNode: ChromeProjectNavigationNode, + { + isSideNavCollapsed, + treeDepth, + parentNode, + }: { isSideNavCollapsed: boolean; treeDepth: number; parentNode?: ChromeProjectNavigationNode } +) => { const serialized: ChromeProjectNavigationNode = { ...navNode, - children: navNode.children?.filter(itemIsVisible), }; - serialized.renderAs = getRenderAs(serialized); + serialized.renderAs = getRenderAs(serialized, { isSideNavCollapsed }); + serialized.spaceBefore = getSpaceBefore(serialized, { + isSideNavCollapsed, + treeDepth, + parentNode, + }); + serialized.children = navNode.children?.filter(itemIsVisible).map((child) => + serializeNavNode(child, { + isSideNavCollapsed, + treeDepth: treeDepth + 1, + parentNode: serialized, + }) + ); - return { - navNode: serialized, - hasChildren: nodeHasChildren(serialized), - hasLink: nodeHasLink(serialized), - isItem: serialized.renderAs === 'item', - }; + return serialized; }; const isEuiCollapsibleNavItemProps = ( @@ -95,41 +144,38 @@ const isEuiCollapsibleNavItemProps = ( }; const renderBlockTitle: ( - navNode: ChromeProjectNavigationNode, - { spaceBefore }: { spaceBefore: EuiThemeSize | null } -) => Required['renderItem'] = - (navNode, { spaceBefore }) => - () => { - const { title } = navNode; - const dataTestSubj = getTestSubj(navNode); - return ( - { - return { - marginTop: spaceBefore ? euiTheme.size[spaceBefore] : undefined, - paddingBlock: euiTheme.size.xs, - paddingInline: euiTheme.size.s, - }; - }} - > -
{title}
-
- ); - }; + navNode: ChromeProjectNavigationNode +) => Required['renderItem'] = (navNode) => () => { + const { title, spaceBefore } = navNode; + const dataTestSubj = getTestSubj(navNode); + return ( + { + return { + marginTop: spaceBefore ? euiTheme.size[spaceBefore] : undefined, + paddingBlock: euiTheme.size.xs, + paddingInline: euiTheme.size.s, + }; + }} + > +
{title}
+
+ ); +}; const renderGroup = ( navGroup: ChromeProjectNavigationNode, - groupItems: Array, - { spaceBefore = DEFAULT_SPACE_BETWEEN_LEVEL_1_GROUPS }: { spaceBefore?: EuiThemeSize | null } = {} + groupItems: Array ): Required['items'] => { let itemPrepend: EuiCollapsibleNavItemProps | EuiCollapsibleNavSubItemProps | null = null; + const { spaceBefore } = navGroup; if (!!navGroup.title) { itemPrepend = { - renderItem: renderBlockTitle(navGroup, { spaceBefore }), + renderItem: renderBlockTitle(navGroup), }; } else if (spaceBefore) { itemPrepend = { @@ -147,11 +193,9 @@ const renderGroup = ( const renderPanelOpener = ( navGroup: ChromeProjectNavigationNode, { - spaceBefore, navigateToUrl, activeNodes, }: { - spaceBefore?: EuiThemeSize | null; navigateToUrl: NavigateToUrlFn; activeNodes: ChromeProjectNavigationNode[][]; } @@ -168,9 +212,9 @@ const renderPanelOpener = ( }, ]; - if (spaceBefore) { + if (navGroup.spaceBefore) { items.unshift({ - renderItem: () => , + renderItem: () => , }); } @@ -178,7 +222,7 @@ const renderPanelOpener = ( }; const getEuiProps = ( - _navNode: ChromeProjectNavigationNode, + navNode: ChromeProjectNavigationNode, deps: { navigateToUrl: NavigateToUrlFn; closePanel: PanelContext['close']; @@ -194,7 +238,6 @@ const getEuiProps = ( isSelected: boolean; isItem: boolean; dataTestSubj: string; - spaceBefore?: EuiThemeSize | null; } & Pick => { const { navigateToUrl, @@ -205,7 +248,8 @@ const getEuiProps = ( eventTracker, basePath, } = deps; - const { navNode, isItem, hasChildren, hasLink } = serializeNavNode(_navNode); + const hasLink = nodeHasLink(navNode); + const isItem = navNode.renderAs === 'item'; const { path, href, onClick: customOnClick, isCollapsible = DEFAULT_IS_COLLAPSIBLE } = navNode; const isAccordion = isAccordionNode(navNode); @@ -225,13 +269,6 @@ const getEuiProps = ( const dataTestSubj = getTestSubj(navNode, isSelected); - let spaceBefore = navNode.spaceBefore; - if (spaceBefore === undefined && treeDepth === 1 && hasChildren && !isItem) { - // For groups at level 1 that don't have a space specified we default to add a "m" - // space. For all other groups, unless specified, there is no vertical space. - spaceBefore = DEFAULT_SPACE_BETWEEN_LEVEL_1_GROUPS; - } - const subItems: EuiCollapsibleNavItemProps['items'] | undefined = isItem ? undefined : navNode.children @@ -303,7 +340,6 @@ const getEuiProps = ( subItems, isSelected, isItem, - spaceBefore, dataTestSubj, linkProps, onClick, @@ -328,9 +364,11 @@ function nodeToEuiCollapsibleNavProps( items: Array; isVisible: boolean; } { - const { navNode, subItems, dataTestSubj, isSelected, isItem, spaceBefore, linkProps, onClick } = - getEuiProps(_navNode, deps); - const { id, path, href, renderAs, isCollapsible } = navNode; + const { navNode, subItems, dataTestSubj, isSelected, isItem, linkProps, onClick } = getEuiProps( + _navNode, + deps + ); + const { id, path, href, renderAs, isCollapsible, spaceBefore } = navNode; if (navNode.renderItem) { // Leave the rendering to the consumer @@ -343,7 +381,7 @@ function nodeToEuiCollapsibleNavProps( if (renderAs === 'panelOpener') { // Render as a panel opener (button to open a panel as a second navigation) return { - items: [...renderPanelOpener(navNode, { spaceBefore, ...deps })], + items: [...renderPanelOpener(navNode, deps)], isVisible: true, }; } @@ -351,7 +389,7 @@ function nodeToEuiCollapsibleNavProps( if (renderAs === 'block' && deps.treeDepth > 0 && subItems) { // Render as a group block (bold title + list of links underneath) return { - items: [...renderGroup(navNode, subItems, { spaceBefore: spaceBefore ?? null })], + items: [...renderGroup(navNode, subItems)], isVisible: subItems.length > 0, }; } @@ -399,16 +437,19 @@ interface Props { export const NavigationSectionUI: FC = React.memo(({ navNode: _navNode }) => { const { activeNodes } = useNavigation(); - const { navigateToUrl, eventTracker, basePath } = useServices(); + const { navigateToUrl, eventTracker, basePath, isSideNavCollapsed } = useServices(); const [items, setItems] = useState(); - const { navNode } = useMemo( + const navNode = useMemo( () => - serializeNavNode({ - renderAs: _navNode.children ? 'accordion' : 'item', // Top level nodes are either item or accordion - ..._navNode, - }), - [_navNode] + serializeNavNode( + { + renderAs: _navNode.children ? 'accordion' : 'item', // Top level nodes are either item or accordion + ..._navNode, + }, + { isSideNavCollapsed, treeDepth: 0 } + ), + [_navNode, isSideNavCollapsed] ); const { close: closePanel } = usePanel(); diff --git a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_accordion_state.ts b/packages/shared-ux/chrome/navigation/src/ui/hooks/use_accordion_state.ts index 03ded17ee40973..8ebf91a949db24 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_accordion_state.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/hooks/use_accordion_state.ts @@ -108,6 +108,7 @@ export const useAccordionState = ({ navNode }: { navNode: ChromeProjectNavigatio }; const updated: Partial = { + buttonProps: { 'data-test-subj': 'accordionToggleBtn' }, ..._accordionProps, arrowProps, isCollapsible, diff --git a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts index b2964648f8d022..e632cee3d732cc 100644 --- a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts +++ b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts @@ -356,7 +356,6 @@ export function createNavTree(pluginsStart: ObservabilityPublicPluginsStart) { defaultMessage: 'Other tools', }), renderAs: 'panelOpener', - icon: 'editorCodeBlock', children: [ { link: 'logs:stream', From 923c450c1b044a12dd938c0c5ea380a895eeaf88 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 14 Oct 2024 11:37:56 +0100 Subject: [PATCH 19/38] [ML] Adds ML tasks to the kibana audit log (#195120) Adds a new `MlAuditLogger` service for logging calls to elasticsearch in kibana's audit log. Not all calls are logged, only ones which make changes to ML jobs or trained models, e.g. creating, deleting, starting, stopping etc. Calls to the es client are wrapped in a logging function so successes and failures can be caught and logged. the audit log can be enabed by adding this to the kibana yml or dev.yml file `xpack.security.audit.enabled: true` An example log entry (NDJSON formatted to make it readable): ``` { "event": { "action": "ml_start_ad_datafeed", "type": [ "change" ], "category": [ "database" ], "outcome": "success" }, "labels": { "application": "elastic/ml" }, "user": { "id": "u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0", "name": "elastic", "roles": [ "superuser" ] }, "kibana": { "space_id": "default", "session_id": "U6HQCDkk+fAEUCXs7i4qM2/MZITPxE02pp8o7h09P68=" }, "trace": { "id": "4f1b616b-8535-43e1-8516-32ea9fe76d19" }, "client": { "ip": "127.0.0.1" }, "http": { "request": { "headers": { "x-forwarded-for": "127.0.0.1" } } }, "service": { "node": { "roles": [ "background_tasks", "ui" ] } }, "ecs": { "version": "8.11.0" }, "@timestamp": "2024-10-11T09:07:47.933+01:00", "message": "Starting anomaly detection datafeed datafeed-11aaaa", "log": { "level": "INFO", "logger": "plugins.security.audit.ecs" }, "process": { "pid": 58305, "uptime": 100.982390291 }, "transaction": { "id": "77c14aadc6901324" } } ``` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- docs/user/security/audit-logging.asciidoc | 158 +++++++- .../plugins/ml/server/lib/ml_client/index.ts | 1 + .../server/lib/ml_client/ml_audit_logger.ts | 383 ++++++++++++++++++ .../ml/server/lib/ml_client/ml_client.ts | 225 ++++++---- x-pack/plugins/ml/server/lib/route_guard.ts | 8 +- x-pack/plugins/ml/server/plugin.ts | 4 + .../server/shared_services/shared_services.ts | 21 +- x-pack/plugins/ml/tsconfig.json | 91 +++-- 8 files changed, 743 insertions(+), 148 deletions(-) create mode 100644 x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index 0ddc830e4ed49b..1ac40bcc7764a8 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -17,20 +17,20 @@ by cluster-wide privileges. For more information on enabling audit logging in Audit logs are **disabled** by default. To enable this functionality, you must set `xpack.security.audit.enabled` to `true` in `kibana.yml`. -You can optionally configure audit logs location, file/rolling file appenders and +You can optionally configure audit logs location, file/rolling file appenders and ignore filters using <>. ============================================================================ [[xpack-security-ecs-audit-logging]] ==== Audit events -Refer to the table of events that can be logged for auditing purposes. +Refer to the table of events that can be logged for auditing purposes. Each event is broken down into <>, <>, <> and <> fields to make it easy to filter, query and aggregate the resulting logs. The <> field can be used to correlate multiple events that originate from the same request. -Refer to <> for a table of fields that get logged with audit event. +Refer to <> for a table of fields that get logged with audit event. [NOTE] ============================================================================ @@ -116,6 +116,38 @@ Refer to the corresponding {es} logs for potential write errors. .1+| `case_user_action_create_case` | `success` | User has created a case. +.2+| `ml_put_ad_job` +| `success` | Creating anomaly detection job. +| `failure` | Failed to create anomaly detection job. + +.2+| `ml_put_ad_datafeed` +| `success` | Creating anomaly detection datafeed. +| `failure` | Failed to create anomaly detection datafeed. + +.2+| `ml_put_calendar` +| `success` | Creating calendar. +| `failure` | Failed to create calendar. + +.2+| `ml_post_calendar_events` +| `success` | Adding events to calendar. +| `failure` | Failed to add events to calendar. + +.2+| `ml_forecast` +| `success` | Creating anomaly detection forecast. +| `failure` | Failed to create anomaly detection forecast. + +.2+| `ml_put_filter` +| `success` | Creating filter. +| `failure` | Failed to create filter. + +.2+| `ml_put_dfa_job` +| `success` | Creating data frame analytics job. +| `failure` | Failed to create data frame analytics job. + +.2+| `ml_put_trained_model` +| `success` | Creating trained model. +| `failure` | Failed to create trained model. + 3+a| ====== Type: change @@ -234,6 +266,74 @@ Refer to the corresponding {es} logs for potential write errors. .1+| `case_user_action_update_case_title` | `success` | User has updated the case title. +.2+| `ml_open_ad_job` +| `success` | Opening anomaly detection job. +| `failure` | Failed to open anomaly detection job. + +.2+| `ml_close_ad_job` +| `success` | Closing anomaly detection job. +| `failure` | Failed to close anomaly detection job. + +.2+| `ml_start_ad_datafeed` +| `success` | Starting anomaly detection datafeed. +| `failure` | Failed to start anomaly detection datafeed. + +.2+| `ml_stop_ad_datafeed` +| `success` | Stopping anomaly detection datafeed. +| `failure` | Failed to stop anomaly detection datafeed. + +.2+| `ml_update_ad_job` +| `success` | Updating anomaly detection job. +| `failure` | Failed to update anomaly detection job. + +.2+| `ml_reset_ad_job` +| `success` | Resetting anomaly detection job. +| `failure` | Failed to reset anomaly detection job. + +.2+| `ml_revert_ad_snapshot` +| `success` | Reverting anomaly detection snapshot. +| `failure` | Failed to revert anomaly detection snapshot. + +.2+| `ml_update_ad_datafeed` +| `success` | Updating anomaly detection datafeed. +| `failure` | Failed to update anomaly detection datafeed. + +.2+| `ml_put_calendar_job` +| `success` | Adding job to calendar. +| `failure` | Failed to add job to calendar. + +.2+| `ml_delete_calendar_job` +| `success` | Removing job from calendar. +| `failure` | Failed to remove job from calendar. + +.2+| `ml_update_filter` +| `success` | Updating filter. +| `failure` | Failed to update filter. + +.2+| `ml_start_dfa_job` +| `success` | Starting data frame analytics job. +| `failure` | Failed to start data frame analytics job. + +.2+| `ml_stop_dfa_job` +| `success` | Stopping data frame analytics job. +| `failure` | Failed to stop data frame analytics job. + +.2+| `ml_update_dfa_job` +| `success` | Updating data frame analytics job. +| `failure` | Failed to update data frame analytics job. + +.2+| `ml_start_trained_model_deployment` +| `success` | Starting trained model deployment. +| `failure` | Failed to start trained model deployment. + +.2+| `ml_stop_trained_model_deployment` +| `success` | Stopping trained model deployment. +| `failure` | Failed to stop trained model deployment. + +.2+| `ml_update_trained_model_deployment` +| `success` | Updating trained model deployment. +| `failure` | Failed to update trained model deployment. + 3+a| ====== Type: deletion @@ -289,6 +389,42 @@ Refer to the corresponding {es} logs for potential write errors. .1+| `case_user_action_delete_case_tags` | `success` | User has removed tags from a case. +.2+| `ml_delete_ad_job` +| `success` | Deleting anomaly detection job. +| `failure` | Failed to delete anomaly detection job. + +.2+| `ml_delete_model_snapshot` +| `success` | Deleting model snapshot. +| `failure` | Failed to delete model snapshot. + +.2+| `ml_delete_ad_datafeed` +| `success` | Deleting anomaly detection datafeed. +| `failure` | Failed to delete anomaly detection datafeed. + +.2+| `ml_delete_calendar` +| `success` | Deleting calendar. +| `failure` | Failed to delete calendar. + +.2+| `ml_delete_calendar_event` +| `success` | Deleting calendar event. +| `failure` | Failed to delete calendar event. + +.2+| `ml_delete_filter` +| `success` | Deleting filter. +| `failure` | Failed to delete filter. + +.2+| `ml_delete_forecast` +| `success` | Deleting forecast. +| `failure` | Failed to delete forecast. + +.2+| `ml_delete_dfa_job` +| `success` | Deleting data frame analytics job. +| `failure` | Failed to delete data frame analytics job. + +.2+| `ml_delete_trained_model` +| `success` | Deleting trained model. +| `failure` | Failed to delete trained model. + 3+a| ====== Type: access @@ -448,6 +584,10 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | User has accessed the connectors of a case. | `failure` | User is not authorized to access the connectors of a case. +.2+| `ml_infer_trained_model` +| `success` | Inferring using trained model. +| `failure` | Failed to infer using trained model. + 3+a| ===== Category: web @@ -474,12 +614,12 @@ Audit logs are written in JSON using https://www.elastic.co/guide/en/ecs/1.6/ind | *Description* | `@timestamp` -| Time when the event was generated. +| Time when the event was generated. Example: `2016-05-23T08:05:34.853Z` | `message` -| Human readable description of the event. +| Human readable description of the event. 2+a| ===== Event Fields @@ -489,7 +629,7 @@ Example: `2016-05-23T08:05:34.853Z` | [[field-event-action]] `event.action` | The action captured by the event. -Refer to <> for a table of possible actions. +Refer to <> for a table of possible actions. | [[field-event-category]] `event.category` | High level category associated with the event. @@ -513,7 +653,7 @@ Possible values: `deletion` | [[field-event-outcome]] `event.outcome` -a| Denotes whether the event represents a success or failure: +a| Denotes whether the event represents a success or failure: * Any actions that the user is not authorized to perform are logged with outcome: `failure` * Authorized read operations are only logged after successfully fetching the data from {es} with outcome: `success` @@ -553,7 +693,7 @@ Example: `[kibana_admin, reporting_user]` Example: `default` | `kibana.session_id` -| ID of the user session associated with the event. +| ID of the user session associated with the event. Each login attempt results in a unique session id. @@ -604,7 +744,7 @@ Example: `[marketing]` | Error code describing the error. | `error.message` -| Error message. +| Error message. 2+a| ===== HTTP and URL Fields diff --git a/x-pack/plugins/ml/server/lib/ml_client/index.ts b/x-pack/plugins/ml/server/lib/ml_client/index.ts index 03fc5e6244b31e..92ef7b822e80aa 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/index.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/index.ts @@ -8,3 +8,4 @@ export { getMlClient } from './ml_client'; export { MLJobNotFound, MLModelNotFound } from './errors'; export type { MlClient } from './types'; +export { MlAuditLogger } from './ml_audit_logger'; diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts new file mode 100644 index 00000000000000..1d678497cc2052 --- /dev/null +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_audit_logger.ts @@ -0,0 +1,383 @@ +/* + * 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 { KibanaRequest } from '@kbn/core-http-server'; +import type { AuditLogger, CoreAuditService, EcsEvent } from '@kbn/core/server'; +import type { ArrayElement } from '@kbn/utility-types'; +import type { MlClient, MlClientParams } from './types'; +import { + getADJobIdsFromRequest, + getDFAJobIdsFromRequest, + getDatafeedIdsFromRequest, + getModelIdsFromRequest, +} from './ml_client'; + +type TaskTypeAD = + | 'ml_put_ad_job' + | 'ml_delete_ad_job' + | 'ml_delete_model_snapshot' + | 'ml_open_ad_job' + | 'ml_close_ad_job' + | 'ml_update_ad_job' + | 'ml_reset_ad_job' + | 'ml_revert_ad_snapshot' + | 'ml_put_ad_datafeed' + | 'ml_delete_ad_datafeed' + | 'ml_start_ad_datafeed' + | 'ml_stop_ad_datafeed' + | 'ml_update_ad_datafeed' + | 'ml_put_calendar' + | 'ml_delete_calendar' + | 'ml_put_calendar_job' + | 'ml_delete_calendar_job' + | 'ml_post_calendar_events' + | 'ml_delete_calendar_event' + | 'ml_put_filter' + | 'ml_update_filter' + | 'ml_delete_filter' + | 'ml_forecast' + | 'ml_delete_forecast'; + +type TaskTypeDFA = + | 'ml_put_dfa_job' + | 'ml_delete_dfa_job' + | 'ml_start_dfa_job' + | 'ml_stop_dfa_job' + | 'ml_update_dfa_job'; + +type TaskTypeNLP = + | 'ml_put_trained_model' + | 'ml_delete_trained_model' + | 'ml_start_trained_model_deployment' + | 'ml_stop_trained_model_deployment' + | 'ml_update_trained_model_deployment' + | 'ml_infer_trained_model'; + +type TaskType = TaskTypeAD | TaskTypeDFA | TaskTypeNLP; + +const APPLICATION = 'elastic/ml'; +const CATEGORY = 'database'; + +const EVENT_TYPES: Record> = { + creation: 'creation', + deletion: 'deletion', + change: 'change', + access: 'access', +} as const; +type EventTypes = keyof typeof EVENT_TYPES; + +interface MlLogEntry { + event: EcsEvent; + message: string; + labels: { application: typeof APPLICATION }; +} + +export class MlAuditLogger { + private auditLogger: AuditLogger; + constructor(audit: CoreAuditService, request?: KibanaRequest) { + this.auditLogger = request ? audit.asScoped(request) : audit.withoutRequest; + } + + public async wrapTask(task: () => T, taskType: TaskType, p: P) { + try { + const resp = await task(); + this.logSuccess(taskType, p); + return resp; + } catch (error) { + this.logFailure(taskType, p); + throw error; + } + } + + public logMessage(message: string) { + this.auditLogger.log({ + message, + labels: { + application: APPLICATION, + }, + }); + } + + private logSuccess(taskType: TaskType, p: MlClientParams) { + const entry = this.createLogEntry(taskType, p, true); + if (entry) { + this.auditLogger.log(entry); + } + } + private logFailure(taskType: TaskType, p: MlClientParams) { + const entry = this.createLogEntry(taskType, p, false); + if (entry) { + this.auditLogger.log(entry); + } + } + + private createLogEntry( + taskType: TaskType, + p: MlClientParams, + success: boolean + ): MlLogEntry | undefined { + try { + const { message, type } = this.createPartialLogEntry(taskType, p); + return { + event: { + action: taskType, + type, + category: [CATEGORY], + outcome: success ? 'success' : 'failure', + }, + message, + labels: { + application: APPLICATION, + }, + }; + } catch (error) { + // if an unknown task type is passed, we won't log anything + } + } + + private createPartialLogEntry( + taskType: TaskType, + p: MlClientParams + ): { message: string; type: EventTypes[] } { + switch (taskType) { + /* Anomaly Detection */ + case 'ml_put_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Creating anomaly detection job ${jobId}`, type: [EVENT_TYPES.creation] }; + } + case 'ml_delete_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Deleting anomaly detection job ${jobId}`, type: [EVENT_TYPES.deletion] }; + } + case 'ml_delete_model_snapshot': { + const [jobId] = getADJobIdsFromRequest(p); + const [params] = p as Parameters; + const snapshotId = params.snapshot_id; + return { + message: `Deleting model snapshot ${snapshotId} from job ${jobId}`, + type: [EVENT_TYPES.deletion], + }; + } + case 'ml_open_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Opening anomaly detection job ${jobId}`, type: [EVENT_TYPES.change] }; + } + case 'ml_close_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Closing anomaly detection job ${jobId}`, type: [EVENT_TYPES.change] }; + } + case 'ml_update_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Updating anomaly detection job ${jobId}`, type: [EVENT_TYPES.change] }; + } + case 'ml_reset_ad_job': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Resetting anomaly detection job ${jobId}`, type: [EVENT_TYPES.change] }; + } + case 'ml_revert_ad_snapshot': { + const [jobId] = getADJobIdsFromRequest(p); + const [params] = p as Parameters; + const snapshotId = params.snapshot_id; + return { + message: `Reverting anomaly detection snapshot ${snapshotId} in job ${jobId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_put_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + const [jobId] = getADJobIdsFromRequest(p); + return { + message: `Creating anomaly detection datafeed ${datafeedId} for job ${jobId}`, + type: [EVENT_TYPES.creation], + }; + } + case 'ml_delete_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + return { + message: `Deleting anomaly detection datafeed ${datafeedId}`, + type: [EVENT_TYPES.deletion], + }; + } + case 'ml_start_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + return { + message: `Starting anomaly detection datafeed ${datafeedId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_stop_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + return { + message: `Stopping anomaly detection datafeed ${datafeedId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_update_ad_datafeed': { + const [datafeedId] = getDatafeedIdsFromRequest(p); + return { + message: `Updating anomaly detection datafeed ${datafeedId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_put_calendar': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + // @ts-expect-error body is optional + const jobIds = (params.body ?? params).job_ids; + return { + message: `Creating calendar ${calendarId} ${jobIds ? `with job(s) ${jobIds}` : ''}`, + type: [EVENT_TYPES.creation], + }; + } + case 'ml_delete_calendar': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + return { message: `Deleting calendar ${calendarId}`, type: [EVENT_TYPES.deletion] }; + } + case 'ml_put_calendar_job': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + const jobIds = params.job_id; + return { + message: `Adding job(s) ${jobIds} to calendar ${calendarId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_delete_calendar_job': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + const jobIds = params.job_id; + return { + message: `Removing job(s) ${jobIds} from calendar ${calendarId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_post_calendar_events': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + // @ts-expect-error body is optional + const eventsCount = (params.body ?? params).events; + return { + message: `Adding ${eventsCount} event(s) to calendar ${calendarId}`, + type: [EVENT_TYPES.creation], + }; + } + case 'ml_delete_calendar_event': { + const [params] = p as Parameters; + const calendarId = params.calendar_id; + const eventId = params.event_id; + return { + message: `Removing event(s) ${eventId} from calendar ${calendarId}`, + type: [EVENT_TYPES.deletion], + }; + } + case 'ml_put_filter': { + const [params] = p as Parameters; + const filterId = params.filter_id; + return { message: `Creating filter ${filterId}`, type: [EVENT_TYPES.creation] }; + } + case 'ml_update_filter': { + const [params] = p as Parameters; + const filterId = params.filter_id; + return { message: `Updating filter ${filterId}`, type: [EVENT_TYPES.change] }; + } + case 'ml_delete_filter': { + const [params] = p as Parameters; + const filterId = params.filter_id; + return { message: `Deleting filter ${filterId}`, type: [EVENT_TYPES.deletion] }; + } + case 'ml_forecast': { + const [jobId] = getADJobIdsFromRequest(p); + return { message: `Forecasting for job ${jobId}`, type: [EVENT_TYPES.creation] }; + } + case 'ml_delete_forecast': { + const [params] = p as Parameters; + const forecastId = params.forecast_id; + const [jobId] = getADJobIdsFromRequest(p); + return { + message: `Deleting forecast ${forecastId} for job ${jobId}`, + type: [EVENT_TYPES.deletion], + }; + } + + /* Data Frame Analytics */ + case 'ml_put_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { + message: `Creating data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.creation], + }; + } + case 'ml_delete_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { + message: `Deleting data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.deletion], + }; + } + case 'ml_start_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { + message: `Starting data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_stop_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { + message: `Stopping data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_update_dfa_job': { + const [analyticsId] = getDFAJobIdsFromRequest(p); + return { + message: `Updating data frame analytics job ${analyticsId}`, + type: [EVENT_TYPES.change], + }; + } + + /* Trained Models */ + case 'ml_put_trained_model': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Creating trained model ${modelId}`, type: [EVENT_TYPES.creation] }; + } + case 'ml_delete_trained_model': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Deleting trained model ${modelId}`, type: [EVENT_TYPES.deletion] }; + } + case 'ml_start_trained_model_deployment': { + const [modelId] = getModelIdsFromRequest(p); + return { + message: `Starting trained model deployment for model ${modelId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_stop_trained_model_deployment': { + const [modelId] = getModelIdsFromRequest(p); + return { + message: `Stopping trained model deployment for model ${modelId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_update_trained_model_deployment': { + const [modelId] = getModelIdsFromRequest(p); + return { + message: `Updating trained model deployment for model ${modelId}`, + type: [EVENT_TYPES.change], + }; + } + case 'ml_infer_trained_model': { + const [modelId] = getModelIdsFromRequest(p); + return { message: `Inferring trained model ${modelId}`, type: [EVENT_TYPES.access] }; + } + + default: + throw new Error(`Unsupported task type: ${taskType}`); + } + } +} diff --git a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts index 33f0e1462b2b27..c7bf9ca8bc5d8a 100644 --- a/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts +++ b/x-pack/plugins/ml/server/lib/ml_client/ml_client.ts @@ -25,10 +25,12 @@ import type { MlGetDatafeedParams, MlGetTrainedModelParams, } from './types'; +import type { MlAuditLogger } from './ml_audit_logger'; export function getMlClient( client: IScopedClusterClient, - mlSavedObjectService: MLSavedObjectService + mlSavedObjectService: MLSavedObjectService, + auditLogger: MlAuditLogger ): MlClient { const mlClient = client.asInternalUser.ml; @@ -160,28 +162,44 @@ export function getMlClient( return { async closeJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.closeJob(...p); + return auditLogger.wrapTask(() => mlClient.closeJob(...p), 'ml_close_ad_job', p); }, async deleteCalendar(...p: Parameters) { - return mlClient.deleteCalendar(...p); + return auditLogger.wrapTask(() => mlClient.deleteCalendar(...p), 'ml_delete_calendar', p); }, async deleteCalendarEvent(...p: Parameters) { - return mlClient.deleteCalendarEvent(...p); + return auditLogger.wrapTask( + () => mlClient.deleteCalendarEvent(...p), + 'ml_delete_calendar_event', + p + ); }, async deleteCalendarJob(...p: Parameters) { - return mlClient.deleteCalendarJob(...p); + return auditLogger.wrapTask( + () => mlClient.deleteCalendarJob(...p), + 'ml_delete_calendar_job', + p + ); }, async deleteDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - const resp = await mlClient.deleteDataFrameAnalytics(...p); + const resp = await auditLogger.wrapTask( + () => mlClient.deleteDataFrameAnalytics(...p), + 'ml_delete_dfa_job', + p + ); // don't delete the job saved object as the real job will not be // deleted initially and could still fail. return resp; }, async deleteDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - const resp = await mlClient.deleteDatafeed(...p); const [datafeedId] = getDatafeedIdsFromRequest(p); + const resp = await auditLogger.wrapTask( + () => mlClient.deleteDatafeed(...p), + 'ml_delete_ad_datafeed', + p + ); if (datafeedId !== undefined) { await mlSavedObjectService.deleteDatafeed(datafeedId); } @@ -192,26 +210,33 @@ export function getMlClient( return mlClient.deleteExpiredData(...p); }, async deleteFilter(...p: Parameters) { - return mlClient.deleteFilter(...p); + return auditLogger.wrapTask(() => mlClient.deleteFilter(...p), 'ml_delete_filter', p); }, async deleteForecast(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.deleteForecast(...p); + return auditLogger.wrapTask(() => mlClient.deleteForecast(...p), 'ml_delete_forecast', p); }, async deleteJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - const resp = await mlClient.deleteJob(...p); + return auditLogger.wrapTask(() => mlClient.deleteJob(...p), 'ml_delete_ad_job', p); // don't delete the job saved object as the real job will not be // deleted initially and could still fail. - return resp; }, async deleteModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.deleteModelSnapshot(...p); + return auditLogger.wrapTask( + () => mlClient.deleteModelSnapshot(...p), + 'ml_delete_model_snapshot', + p + ); }, async deleteTrainedModel(...p: Parameters) { await modelIdsCheck(p); - return mlClient.deleteTrainedModel(...p); + return auditLogger.wrapTask( + () => mlClient.deleteTrainedModel(...p), + 'ml_delete_trained_model', + p + ); }, async estimateModelMemory(...p: Parameters) { return mlClient.estimateModelMemory(...p); @@ -229,7 +254,7 @@ export function getMlClient( }, async forecast(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.forecast(...p); + return auditLogger.wrapTask(() => mlClient.forecast(...p), 'ml_forecast', p); }, async getBuckets(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); @@ -502,16 +527,21 @@ export function getMlClient( await modelIdsCheck(p); // TODO use mlClient.startTrainedModelDeployment when esClient is updated const { model_id: modelId, adaptive_allocations: adaptiveAllocations, ...queryParams } = p[0]; - return client.asInternalUser.transport.request( - { - method: 'POST', - path: `_ml/trained_models/${modelId}/deployment/_start`, - ...(isPopulatedObject(queryParams) ? { querystring: queryParams } : {}), - ...(isPopulatedObject(adaptiveAllocations) - ? { body: { adaptive_allocations: adaptiveAllocations } } - : {}), - }, - p[1] + return auditLogger.wrapTask( + () => + client.asInternalUser.transport.request( + { + method: 'POST', + path: `_ml/trained_models/${modelId}/deployment/_start`, + ...(isPopulatedObject(queryParams) ? { querystring: queryParams } : {}), + ...(isPopulatedObject(adaptiveAllocations) + ? { body: { adaptive_allocations: adaptiveAllocations } } + : {}), + }, + p[1] + ), + 'ml_start_trained_model_deployment', + p ); }, async updateTrainedModelDeployment(...p: Parameters) { @@ -519,17 +549,26 @@ export function getMlClient( const { deployment_id: deploymentId, model_id: modelId, ...bodyParams } = p[0]; // TODO use mlClient.updateTrainedModelDeployment when esClient is updated - return client.asInternalUser.transport.request({ - method: 'POST', - path: `/_ml/trained_models/${deploymentId}/deployment/_update`, - body: bodyParams, - }); + return auditLogger.wrapTask( + () => + client.asInternalUser.transport.request({ + method: 'POST', + path: `/_ml/trained_models/${deploymentId}/deployment/_update`, + body: bodyParams, + }), + 'ml_update_trained_model_deployment', + p + ); }, async stopTrainedModelDeployment(...p: Parameters) { await modelIdsCheck(p); switchDeploymentId(p); - return mlClient.stopTrainedModelDeployment(...p); + return auditLogger.wrapTask( + () => mlClient.stopTrainedModelDeployment(...p), + 'ml_stop_trained_model_deployment', + p + ); }, async inferTrainedModel(...p: Parameters) { await modelIdsCheck(p); @@ -547,14 +586,19 @@ export function getMlClient( // @ts-expect-error body doesn't exist in the type const { model_id: id, body, query: querystring } = p[0]; - return client.asInternalUser.transport.request( - { - method: 'POST', - path: `/_ml/trained_models/${id}/_infer`, - body, - querystring, - }, - p[1] + return auditLogger.wrapTask( + () => + client.asInternalUser.transport.request( + { + method: 'POST', + path: `/_ml/trained_models/${id}/_infer`, + body, + querystring, + }, + p[1] + ), + 'ml_infer_trained_model', + p ); }, async info(...p: Parameters) { @@ -562,10 +606,14 @@ export function getMlClient( }, async openJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.openJob(...p); + return auditLogger.wrapTask(() => mlClient.openJob(...p), 'ml_open_ad_job', p); }, async postCalendarEvents(...p: Parameters) { - return mlClient.postCalendarEvents(...p); + return auditLogger.wrapTask( + () => mlClient.postCalendarEvents(...p), + 'ml_post_calendar_events', + p + ); }, async postData(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); @@ -576,22 +624,30 @@ export function getMlClient( return mlClient.previewDatafeed(...p); }, async putCalendar(...p: Parameters) { - return mlClient.putCalendar(...p); + return auditLogger.wrapTask(() => mlClient.putCalendar(...p), 'ml_put_calendar', p); }, async putCalendarJob(...p: Parameters) { - return mlClient.putCalendarJob(...p); + return auditLogger.wrapTask(() => mlClient.putCalendarJob(...p), 'ml_put_calendar_job', p); }, async putDataFrameAnalytics(...p: Parameters) { - const resp = await mlClient.putDataFrameAnalytics(...p); const [analyticsId] = getDFAJobIdsFromRequest(p); + const resp = await auditLogger.wrapTask( + () => mlClient.putDataFrameAnalytics(...p), + 'ml_put_dfa_job', + p + ); if (analyticsId !== undefined) { await mlSavedObjectService.createDataFrameAnalyticsJob(analyticsId); } return resp; }, async putDatafeed(...p: Parameters) { - const resp = await mlClient.putDatafeed(...p); const [datafeedId] = getDatafeedIdsFromRequest(p); + const resp = await auditLogger.wrapTask( + () => mlClient.putDatafeed(...p), + 'ml_put_ad_datafeed', + p + ); const jobId = getJobIdFromBody(p); if (datafeedId !== undefined && jobId !== undefined) { await mlSavedObjectService.addDatafeed(datafeedId, jobId); @@ -600,18 +656,22 @@ export function getMlClient( return resp; }, async putFilter(...p: Parameters) { - return mlClient.putFilter(...p); + return auditLogger.wrapTask(() => mlClient.putFilter(...p), 'ml_put_filter', p); }, async putJob(...p: Parameters) { - const resp = await mlClient.putJob(...p); const [jobId] = getADJobIdsFromRequest(p); + const resp = await auditLogger.wrapTask(() => mlClient.putJob(...p), 'ml_put_ad_job', p); if (jobId !== undefined) { await mlSavedObjectService.createAnomalyDetectionJob(jobId); } return resp; }, async putTrainedModel(...p: Parameters) { - const resp = await mlClient.putTrainedModel(...p); + const resp = await auditLogger.wrapTask( + () => mlClient.putTrainedModel(...p), + 'ml_put_trained_model', + p + ); const [modelId] = getModelIdsFromRequest(p); if (modelId !== undefined) { const model = (p[0] as estypes.MlPutTrainedModelRequest).body; @@ -622,70 +682,61 @@ export function getMlClient( }, async revertModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.revertModelSnapshot(...p); + return auditLogger.wrapTask( + () => mlClient.revertModelSnapshot(...p), + 'ml_revert_ad_snapshot', + p + ); }, async setUpgradeMode(...p: Parameters) { return mlClient.setUpgradeMode(...p); }, async startDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - return mlClient.startDataFrameAnalytics(...p); + return auditLogger.wrapTask( + () => mlClient.startDataFrameAnalytics(...p), + 'ml_start_dfa_job', + p + ); }, async startDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - return mlClient.startDatafeed(...p); + return auditLogger.wrapTask(() => mlClient.startDatafeed(...p), 'ml_start_ad_datafeed', p); }, async stopDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - return mlClient.stopDataFrameAnalytics(...p); + return auditLogger.wrapTask( + () => mlClient.stopDataFrameAnalytics(...p), + 'ml_stop_dfa_job', + p + ); }, async stopDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - return mlClient.stopDatafeed(...p); + return auditLogger.wrapTask(() => mlClient.stopDatafeed(...p), 'ml_stop_ad_datafeed', p); }, async updateDataFrameAnalytics(...p: Parameters) { await jobIdsCheck('data-frame-analytics', p); - return mlClient.updateDataFrameAnalytics(...p); + return auditLogger.wrapTask( + () => mlClient.updateDataFrameAnalytics(...p), + 'ml_update_dfa_job', + p + ); }, async updateDatafeed(...p: Parameters) { await datafeedIdsCheck(p); - - // Temporary workaround for the incorrect updateDatafeed function in the esclient - if ( - // @ts-expect-error TS complains it's always false - p.length === 0 || - p[0] === undefined - ) { - // Temporary generic error message. This should never be triggered - // but is added for type correctness below - throw new Error('Incorrect arguments supplied'); - } - // @ts-expect-error body doesn't exist in the type - const { datafeed_id: id, body } = p[0]; - - return client.asInternalUser.transport.request( - { - method: 'POST', - path: `/_ml/datafeeds/${id}/_update`, - body, - }, - p[1] - ); - - // this should be reinstated once https://github.com/elastic/elasticsearch-js/issues/1601 - // is fixed - // return mlClient.updateDatafeed(...p); + return auditLogger.wrapTask(() => mlClient.updateDatafeed(...p), 'ml_update_ad_datafeed', p); }, async updateFilter(...p: Parameters) { - return mlClient.updateFilter(...p); + return auditLogger.wrapTask(() => mlClient.updateFilter(...p), 'ml_update_filter', p); }, async updateJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.updateJob(...p); + return auditLogger.wrapTask(() => mlClient.updateJob(...p), 'ml_update_ad_job', p); }, async resetJob(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); - return mlClient.resetJob(...p); + return auditLogger.wrapTask(() => mlClient.resetJob(...p), 'ml_reset_ad_job', p); }, async updateModelSnapshot(...p: Parameters) { await jobIdsCheck('anomaly-detector', p); @@ -705,29 +756,29 @@ export function getMlClient( } as MlClient; } -function getDFAJobIdsFromRequest([params]: MlGetDFAParams): string[] { +export function getDFAJobIdsFromRequest([params]: MlGetDFAParams): string[] { const ids = params?.id?.split(','); return ids || []; } -function getModelIdsFromRequest([params]: MlGetTrainedModelParams): string[] { +export function getModelIdsFromRequest([params]: MlGetTrainedModelParams): string[] { const id = params?.model_id; const ids = Array.isArray(id) ? id : id?.split(','); return ids || []; } -function getADJobIdsFromRequest([params]: MlGetADParams): string[] { +export function getADJobIdsFromRequest([params]: MlGetADParams): string[] { const ids = typeof params?.job_id === 'string' ? params?.job_id.split(',') : params?.job_id; return ids || []; } -function getDatafeedIdsFromRequest([params]: MlGetDatafeedParams): string[] { +export function getDatafeedIdsFromRequest([params]: MlGetDatafeedParams): string[] { const ids = typeof params?.datafeed_id === 'string' ? params?.datafeed_id.split(',') : params?.datafeed_id; return ids || []; } -function getJobIdFromBody(p: any): string | undefined { +export function getJobIdFromBody(p: any): string | undefined { const [params] = p; return params?.body?.job_id; } diff --git a/x-pack/plugins/ml/server/lib/route_guard.ts b/x-pack/plugins/ml/server/lib/route_guard.ts index 733dcb66f06f2d..23a324e9fd2fca 100644 --- a/x-pack/plugins/ml/server/lib/route_guard.ts +++ b/x-pack/plugins/ml/server/lib/route_guard.ts @@ -27,7 +27,7 @@ import { mlSavedObjectServiceFactory } from '../saved_objects'; import type { MlLicense } from '../../common/license'; import type { MlClient } from './ml_client'; -import { getMlClient } from './ml_client'; +import { MlAuditLogger, getMlClient } from './ml_client'; import { getDataViewsServiceFactory } from './data_views_utils'; type MLRequestHandlerContext = CustomRequestHandlerContext<{ @@ -42,6 +42,7 @@ type Handler

= (handlerParams: { mlSavedObjectService: MLSavedObjectService; mlClient: MlClient; getDataViewsService(): Promise; + auditLogger: MlAuditLogger; }) => ReturnType>; type GetMlSavedObjectClient = (request: KibanaRequest) => SavedObjectsClientContract | null; @@ -121,6 +122,8 @@ export class RouteGuard { const [coreStart] = await this._getStartServices(); const executionContext = createExecutionContext(coreStart, PLUGIN_ID, request.route.path); + const auditLogger = new MlAuditLogger(coreStart.security.audit, request); + return await coreStart.executionContext.withContext(executionContext, () => handler({ client, @@ -128,13 +131,14 @@ export class RouteGuard { response, context, mlSavedObjectService, - mlClient: getMlClient(client, mlSavedObjectService), + mlClient: getMlClient(client, mlSavedObjectService, auditLogger), getDataViewsService: getDataViewsServiceFactory( this._getDataViews, savedObjectClient, client, request ), + auditLogger, }) ); }; diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 2a36e94f2f2e7b..01f3ec7e5d1315 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -17,6 +17,7 @@ import type { IClusterClient, SavedObjectsServiceStart, UiSettingsServiceStart, + CoreAuditService, } from '@kbn/core/server'; import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; @@ -94,6 +95,7 @@ export class MlServerPlugin private home: HomeServerPluginSetup | null = null; private cases: CasesServerSetup | null | undefined = null; private dataViews: DataViewsPluginStart | null = null; + private auditService: CoreAuditService | null = null; private isMlReady: Promise; private setMlReady: () => void = () => {}; private savedObjectsSyncService: SavedObjectsSyncService; @@ -218,6 +220,7 @@ export class MlServerPlugin () => this.uiSettings, () => this.fieldsFormat, getDataViews, + () => this.auditService, () => this.isMlReady, this.compatibleModuleType ); @@ -313,6 +316,7 @@ export class MlServerPlugin this.capabilities = coreStart.capabilities; this.clusterClient = coreStart.elasticsearch.client; this.savedObjectsStart = coreStart.savedObjects; + this.auditService = coreStart.security.audit; this.dataViews = plugins.dataViews; this.mlLicense.setup(plugins.licensing.license$, async (mlLicense: MlLicense) => { diff --git a/x-pack/plugins/ml/server/shared_services/shared_services.ts b/x-pack/plugins/ml/server/shared_services/shared_services.ts index dc65a5bc01817b..f7b0d424092212 100644 --- a/x-pack/plugins/ml/server/shared_services/shared_services.ts +++ b/x-pack/plugins/ml/server/shared_services/shared_services.ts @@ -11,6 +11,7 @@ import type { SavedObjectsClientContract, UiSettingsServiceStart, KibanaRequest, + CoreAuditService, } from '@kbn/core/server'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/server'; import { CoreKibanaRequest } from '@kbn/core/server'; @@ -49,7 +50,7 @@ import { MLUISettingsClientUninitialized, } from './errors'; import type { MlClient } from '../lib/ml_client'; -import { getMlClient } from '../lib/ml_client'; +import { getMlClient, MlAuditLogger } from '../lib/ml_client'; import type { MLSavedObjectService } from '../saved_objects'; import { mlSavedObjectServiceFactory } from '../saved_objects'; import type { MlAlertingServiceProvider } from './providers/alerting_service'; @@ -110,6 +111,7 @@ export function createSharedServices( getUiSettings: () => UiSettingsServiceStart | null, getFieldsFormat: () => FieldFormatsStart | null, getDataViews: () => DataViewsPluginStart, + getAuditService: () => CoreAuditService | null, isMlReady: () => Promise, compatibleModuleType: CompatibleModule | null ): { @@ -137,7 +139,8 @@ export function createSharedServices( isMlReady, getUiSettings, getFieldsFormat, - getDataViews + getDataViews, + getAuditService ); const { @@ -209,7 +212,8 @@ function getRequestItemsProvider( isMlReady: () => Promise, getUiSettings: () => UiSettingsServiceStart | null, getFieldsFormat: () => FieldFormatsStart | null, - getDataViews: () => DataViewsPluginStart + getDataViews: () => DataViewsPluginStart, + getAuditService: () => CoreAuditService | null ) { return (request: KibanaRequest) => { let hasMlCapabilities: HasMlCapabilities = hasMlCapabilitiesProvider( @@ -237,6 +241,11 @@ function getRequestItemsProvider( throw new MLClusterClientUninitialized(`ML's cluster client has not been initialized`); } + const auditService = getAuditService(); + if (!auditService) { + throw new Error('Audit service not initialized'); + } + const uiSettingsClient = getUiSettings()?.asScopedToClient(savedObjectsClient); if (!uiSettingsClient) { throw new MLUISettingsClientUninitialized(`ML's UI settings client has not been initialized`); @@ -263,7 +272,8 @@ function getRequestItemsProvider( if (request instanceof CoreKibanaRequest) { scopedClient = clusterClient.asScoped(request); mlSavedObjectService = getSobSavedObjectService(scopedClient); - mlClient = getMlClient(scopedClient, mlSavedObjectService); + const auditLogger = new MlAuditLogger(auditService, request); + mlClient = getMlClient(scopedClient, mlSavedObjectService, auditLogger); } else { hasMlCapabilities = () => Promise.resolve(); const { asInternalUser } = clusterClient; @@ -273,7 +283,8 @@ function getRequestItemsProvider( asSecondaryAuthUser: asInternalUser, }; mlSavedObjectService = getSobSavedObjectService(scopedClient); - mlClient = getMlClient(scopedClient, mlSavedObjectService); + const auditLogger = new MlAuditLogger(auditService); + mlClient = getMlClient(scopedClient, mlSavedObjectService, auditLogger); } const getDataViewsService = getDataViewsServiceFactory( diff --git a/x-pack/plugins/ml/tsconfig.json b/x-pack/plugins/ml/tsconfig.json index 24ed35277b93ae..73ab75d5f25151 100644 --- a/x-pack/plugins/ml/tsconfig.json +++ b/x-pack/plugins/ml/tsconfig.json @@ -25,28 +25,46 @@ }, // add references to other TypeScript projects the plugin depends on "@kbn/actions-plugin", + "@kbn/aiops-change-point-detection", + "@kbn/aiops-common", "@kbn/aiops-plugin", "@kbn/alerting-plugin", + "@kbn/alerts-as-data-utils", "@kbn/analytics", "@kbn/cases-plugin", "@kbn/charts-plugin", "@kbn/cloud-plugin", + "@kbn/code-editor", "@kbn/config-schema", + "@kbn/content-management-plugin", + "@kbn/core-elasticsearch-client-server-mocks", + "@kbn/core-elasticsearch-server", + "@kbn/core-http-browser", + "@kbn/core-http-server", + "@kbn/core-lifecycle-browser", + "@kbn/core-notifications-browser-mocks", + "@kbn/core-ui-settings-browser", "@kbn/dashboard-plugin", "@kbn/data-plugin", + "@kbn/data-view-editor-plugin", "@kbn/data-views-plugin", "@kbn/data-visualizer-plugin", + "@kbn/deeplinks-management", + "@kbn/deeplinks-ml", "@kbn/embeddable-plugin", "@kbn/es-query", "@kbn/es-types", "@kbn/es-ui-shared-plugin", + "@kbn/esql-utils", "@kbn/features-plugin", "@kbn/field-formats-plugin", "@kbn/field-types", "@kbn/home-plugin", "@kbn/i18n-react", "@kbn/i18n", + "@kbn/inference_integration_flyout", "@kbn/inspector-plugin", + "@kbn/json-schemas", "@kbn/kibana-react-plugin", "@kbn/kibana-utils-plugin", "@kbn/lens-plugin", @@ -55,28 +73,55 @@ "@kbn/management-plugin", "@kbn/maps-plugin", "@kbn/ml-agg-utils", + "@kbn/ml-anomaly-utils", + "@kbn/ml-category-validator", + "@kbn/ml-creation-wizard-utils", + "@kbn/ml-data-frame-analytics-utils", + "@kbn/ml-data-grid", + "@kbn/ml-data-view-utils", "@kbn/ml-date-picker", + "@kbn/ml-date-utils", + "@kbn/ml-error-utils", + "@kbn/ml-field-stats-flyout", + "@kbn/ml-in-memory-table", "@kbn/ml-is-defined", "@kbn/ml-is-populated-object", + "@kbn/ml-kibana-theme", "@kbn/ml-local-storage", "@kbn/ml-nested-property", "@kbn/ml-number-utils", + "@kbn/ml-parse-interval", "@kbn/ml-query-utils", "@kbn/ml-route-utils", + "@kbn/ml-runtime-field-utils", "@kbn/ml-string-hash", + "@kbn/ml-time-buckets", "@kbn/ml-trained-models-utils", "@kbn/ml-trained-models-utils", + "@kbn/ml-ui-actions", "@kbn/ml-url-state", + "@kbn/ml-validators", "@kbn/monaco", + "@kbn/observability-ai-assistant-plugin", + "@kbn/presentation-containers", + "@kbn/presentation-panel-plugin", + "@kbn/presentation-publishing", + "@kbn/presentation-util-plugin", + "@kbn/react-kibana-context-render", + "@kbn/react-kibana-mount", "@kbn/rison", + "@kbn/rule-data-utils", + "@kbn/rule-registry-plugin", "@kbn/saved-objects-finder-plugin", "@kbn/saved-objects-management-plugin", "@kbn/saved-search-plugin", "@kbn/security-plugin", + "@kbn/securitysolution-ecs", "@kbn/share-plugin", "@kbn/shared-ux-link-redirect-app", "@kbn/shared-ux-page-kibana-template", "@kbn/shared-ux-router", + "@kbn/shared-ux-utility", "@kbn/spaces-plugin", "@kbn/std", "@kbn/task-manager-plugin", @@ -84,53 +129,9 @@ "@kbn/triggers-actions-ui-plugin", "@kbn/ui-actions-plugin", "@kbn/ui-theme", + "@kbn/unified-field-list", "@kbn/unified-search-plugin", "@kbn/usage-collection-plugin", "@kbn/utility-types", - "@kbn/ml-error-utils", - "@kbn/ml-anomaly-utils", - "@kbn/ml-data-frame-analytics-utils", - "@kbn/ml-data-grid", - "@kbn/ml-kibana-theme", - "@kbn/ml-runtime-field-utils", - "@kbn/ml-date-utils", - "@kbn/ml-category-validator", - "@kbn/ml-ui-actions", - "@kbn/deeplinks-ml", - "@kbn/core-notifications-browser-mocks", - "@kbn/unified-field-list", - "@kbn/core-ui-settings-browser", - "@kbn/content-management-plugin", - "@kbn/ml-in-memory-table", - "@kbn/presentation-util-plugin", - "@kbn/react-kibana-mount", - "@kbn/core-http-browser", - "@kbn/data-view-editor-plugin", - "@kbn/rule-data-utils", - "@kbn/alerts-as-data-utils", - "@kbn/rule-registry-plugin", - "@kbn/securitysolution-ecs", - "@kbn/ml-data-view-utils", - "@kbn/ml-creation-wizard-utils", - "@kbn/deeplinks-management", - "@kbn/code-editor", - "@kbn/presentation-publishing", - "@kbn/core-elasticsearch-server", - "@kbn/core-elasticsearch-client-server-mocks", - "@kbn/ml-time-buckets", - "@kbn/aiops-change-point-detection", - "@kbn/inference_integration_flyout", - "@kbn/presentation-containers", - "@kbn/presentation-panel-plugin", - "@kbn/shared-ux-utility", - "@kbn/react-kibana-context-render", - "@kbn/esql-utils", - "@kbn/core-lifecycle-browser", - "@kbn/observability-ai-assistant-plugin", - "@kbn/json-schemas", - "@kbn/ml-field-stats-flyout", - "@kbn/ml-parse-interval", - "@kbn/ml-validators", - "@kbn/aiops-common" ] } From f4a84795b244d158ff3ecc8f85ed2d00c6ded8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Gonz=C3=A1lez?= Date: Mon, 14 Oct 2024 12:49:24 +0200 Subject: [PATCH 20/38] [Search][Ent Search deprecation] Removing indices stats tiles (#193968) ## Summary Removing the indices stats tiles as requested and agreed as a part of Ent Search deprecation here: https://github.com/elastic/search-team/issues/8231 ![CleanShot 2024-09-25 at 12 54 16@2x](https://github.com/user-attachments/assets/bd8ee089-2bee-4beb-927b-e937975d8dbc) --------- Co-authored-by: Elastic Machine --- .../search_indices/indices_stats.tsx | 148 ------------------ .../search_indices/search_indices.tsx | 4 - .../translations/translations/fr-FR.json | 7 - .../translations/translations/ja-JP.json | 7 - .../translations/translations/zh-CN.json | 7 - 5 files changed, 173 deletions(-) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_stats.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_stats.tsx deleted file mode 100644 index 1d5ca88112f4e3..00000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/indices_stats.tsx +++ /dev/null @@ -1,148 +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 React, { useEffect } from 'react'; - -import { useActions, useValues } from 'kea'; - -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiStat } from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -import { Status } from '../../../../../common/types/api'; - -import { FetchSyncJobsStatsApiLogic } from '../../api/stats/fetch_sync_jobs_stats_api_logic'; - -export const IndicesStats: React.FC = () => { - const { makeRequest } = useActions(FetchSyncJobsStatsApiLogic); - const { data, status } = useValues(FetchSyncJobsStatsApiLogic); - const isLoading = status === Status.LOADING; - - useEffect(() => { - makeRequest({}); - }, []); - - const UNKNOWN_STRING = i18n.translate( - 'xpack.enterpriseSearch.content.searchIndices.jobStats.unknown', - { defaultMessage: 'Unknown' } - ); - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx index 218f2bcc550b4a..dece1b4beb2f7c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_indices/search_indices.tsx @@ -38,7 +38,6 @@ import { DefaultSettingsFlyout } from '../settings/default_settings_flyout'; import { DeleteIndexModal } from './delete_index_modal'; import { IndicesLogic } from './indices_logic'; -import { IndicesStats } from './indices_stats'; import { IndicesTable } from './indices_table'; import './search_indices.scss'; @@ -176,9 +175,6 @@ export const SearchIndices: React.FC = () => { )} {!hasNoIndices ? ( - - - diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 819a309616b52c..9bd57ee7ca120a 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -17027,13 +17027,6 @@ "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.idle.label": "Inactif", "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.incomplete.label": "Incomplet", "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.syncError.label": "Échec de la synchronisation", - "xpack.enterpriseSearch.content.searchIndices.jobStats.connectedMethods": "Méthodes d'ingestion connectées", - "xpack.enterpriseSearch.content.searchIndices.jobStats.errorSyncs": "Erreurs de synchronisation", - "xpack.enterpriseSearch.content.searchIndices.jobStats.incompleteMethods": "Méthodes d'ingestion incomplètes", - "xpack.enterpriseSearch.content.searchIndices.jobStats.longRunningSyncs": "Synchronisations inactives", - "xpack.enterpriseSearch.content.searchIndices.jobStats.orphanedSyncs": "Synchronisations orphelines", - "xpack.enterpriseSearch.content.searchIndices.jobStats.runningSyncs": "Synchronisations en cours d'exécution", - "xpack.enterpriseSearch.content.searchIndices.jobStats.unknown": "Inconnu", "xpack.enterpriseSearch.content.searchIndices.name.columnTitle": "Nom de l'index", "xpack.enterpriseSearch.content.searchIndices.searchIndices.breadcrumb": "Index Elasticsearch", "xpack.enterpriseSearch.content.searchIndices.searchIndices.includeHidden.label": "Afficher les index masqués", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 249c3a9e9caff2..8ec4dcb9e23aad 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -16773,13 +16773,6 @@ "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.idle.label": "アイドル", "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.incomplete.label": "未完了", "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.syncError.label": "同期失敗", - "xpack.enterpriseSearch.content.searchIndices.jobStats.connectedMethods": "接続されたインジェストメソッド", - "xpack.enterpriseSearch.content.searchIndices.jobStats.errorSyncs": "同期エラー", - "xpack.enterpriseSearch.content.searchIndices.jobStats.incompleteMethods": "不完全なインジェストメソッド", - "xpack.enterpriseSearch.content.searchIndices.jobStats.longRunningSyncs": "アイドル同期", - "xpack.enterpriseSearch.content.searchIndices.jobStats.orphanedSyncs": "孤立した同期", - "xpack.enterpriseSearch.content.searchIndices.jobStats.runningSyncs": "実行中の同期", - "xpack.enterpriseSearch.content.searchIndices.jobStats.unknown": "不明", "xpack.enterpriseSearch.content.searchIndices.name.columnTitle": "インデックス名", "xpack.enterpriseSearch.content.searchIndices.searchIndices.breadcrumb": "デフォルトのインデックス", "xpack.enterpriseSearch.content.searchIndices.searchIndices.includeHidden.label": "非表示のインデックスを表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 7ca37ec4caef07..740c7fdbb25d97 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -16802,13 +16802,6 @@ "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.idle.label": "空闲", "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.incomplete.label": "不完整", "xpack.enterpriseSearch.content.searchIndices.ingestionStatus.syncError.label": "同步失败", - "xpack.enterpriseSearch.content.searchIndices.jobStats.connectedMethods": "已连接采集方法", - "xpack.enterpriseSearch.content.searchIndices.jobStats.errorSyncs": "同步错误", - "xpack.enterpriseSearch.content.searchIndices.jobStats.incompleteMethods": "未完成的采集方法", - "xpack.enterpriseSearch.content.searchIndices.jobStats.longRunningSyncs": "闲置同步", - "xpack.enterpriseSearch.content.searchIndices.jobStats.orphanedSyncs": "孤立同步", - "xpack.enterpriseSearch.content.searchIndices.jobStats.runningSyncs": "正在运行同步", - "xpack.enterpriseSearch.content.searchIndices.jobStats.unknown": "未知", "xpack.enterpriseSearch.content.searchIndices.name.columnTitle": "索引名称", "xpack.enterpriseSearch.content.searchIndices.searchIndices.breadcrumb": "Elasticsearch 索引", "xpack.enterpriseSearch.content.searchIndices.searchIndices.includeHidden.label": "显示隐藏的索引", From 6aa1bc6fd9aa05cec1af90b7b1dcc191141cc183 Mon Sep 17 00:00:00 2001 From: Panagiota Mitsopoulou Date: Mon, 14 Oct 2024 13:50:33 +0300 Subject: [PATCH 21/38] [Side navigation] bring back getIsActive to o11y solution navigation (#196057) ## Summary I realized that as part of this [PR](https://github.com/elastic/kibana/pull/192805/files#diff-8f26b8327cc9fc31bef2b22bb53b82256edc9cf05cfc9c766d746a7aa4532437L144), `getIsActive` method was accidentally removed from `Applications` and `Infrastructure` menus. This PR brings `getIsActive` back. I didn't find any bug with the absence of `getIsActive`. Purpose of this PR is to not remove something that was there before. --- .../observability/public/navigation_tree.ts | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts index e632cee3d732cc..a36c85dbf937ae 100644 --- a/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts +++ b/x-pack/plugins/observability_solution/observability/public/navigation_tree.ts @@ -96,9 +96,25 @@ export function createNavTree(pluginsStart: ObservabilityPublicPluginsStart) { children: [ { children: [ - { link: 'apm:services' }, - { link: 'apm:traces' }, - { link: 'apm:dependencies' }, + { + link: 'apm:services', + getIsActive: ({ pathNameSerialized }) => { + const regex = /app\/apm\/.*service.*/; + return regex.test(pathNameSerialized); + }, + }, + { + link: 'apm:traces', + getIsActive: ({ pathNameSerialized, prepend }) => { + return pathNameSerialized.startsWith(prepend('/app/apm/traces')); + }, + }, + { + link: 'apm:dependencies', + getIsActive: ({ pathNameSerialized, prepend }) => { + return pathNameSerialized.startsWith(prepend('/app/apm/dependencies')); + }, + }, { link: 'ux', title: i18n.translate('xpack.observability.obltNav.apm.ux', { @@ -146,8 +162,16 @@ export function createNavTree(pluginsStart: ObservabilityPublicPluginsStart) { title: i18n.translate('xpack.observability.infrastructure.inventory', { defaultMessage: 'Infrastructure inventory', }), + getIsActive: ({ pathNameSerialized, prepend }) => { + return pathNameSerialized.startsWith(prepend('/app/metrics/inventory')); + }, + }, + { + link: 'metrics:hosts', + getIsActive: ({ pathNameSerialized, prepend }) => { + return pathNameSerialized.startsWith(prepend('/app/metrics/hosts')); + }, }, - { link: 'metrics:hosts' }, { link: 'metrics:metrics-explorer', title: i18n.translate( From 45a9cf0e343e6c4045834968fa27f6f468cdf3e3 Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Mon, 14 Oct 2024 12:50:55 +0200 Subject: [PATCH 22/38] [ResponseOps][Alerts] Don't show empty state in grouping component while first loading (#195777) ## Summary Makes the loading state and empty state mutually exclusive in the grouping component to avoid showing the empty state when first loading the groups data. ## To verify 1. Create one or more O11y rules that fire alerts 2. Open the O11y > Alerts page 3. Toggle on grouping 4. Reload the page (possibly after activating network throttling) 5. Verify that while the loading indicator is shown, the empty state is not and viceversa ## References Fixes #190954 --- .../src/components/grouping.test.tsx | 73 +++++++++++-------- .../kbn-grouping/src/components/grouping.tsx | 5 +- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/packages/kbn-grouping/src/components/grouping.test.tsx b/packages/kbn-grouping/src/components/grouping.test.tsx index 08aa78da6d10cb..c76be444fdd275 100644 --- a/packages/kbn-grouping/src/components/grouping.test.tsx +++ b/packages/kbn-grouping/src/components/grouping.test.tsx @@ -7,41 +7,52 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { fireEvent, render, within } from '@testing-library/react'; +import { fireEvent, render, within, screen } from '@testing-library/react'; import React from 'react'; import { I18nProvider } from '@kbn/i18n-react'; -import { Grouping } from './grouping'; +import { Grouping, GroupingProps } from './grouping'; import { createGroupFilter, getNullGroupFilter } from '../containers/query/helpers'; import { METRIC_TYPE } from '@kbn/analytics'; import { getTelemetryEvent } from '../telemetry/const'; import { mockGroupingProps, host1Name, host2Name } from './grouping.mock'; +import { SetRequired } from 'type-fest'; const renderChildComponent = jest.fn(); const takeActionItems = jest.fn(); const mockTracker = jest.fn(); -const testProps = { +const testProps: SetRequired, 'data'> = { ...mockGroupingProps, renderChildComponent, takeActionItems, tracker: mockTracker, }; -describe('grouping container', () => { +describe('Grouping', () => { beforeEach(() => { jest.clearAllMocks(); }); + it('Renders groups count when groupsCount > 0', () => { - const { getByTestId, getAllByTestId, queryByTestId } = render( + render( ); - expect(getByTestId('unit-count').textContent).toBe('14 events'); - expect(getByTestId('group-count').textContent).toBe('3 groups'); - expect(getAllByTestId('grouping-accordion').length).toBe(3); - expect(queryByTestId('empty-results-panel')).not.toBeInTheDocument(); + expect(screen.getByTestId('unit-count').textContent).toBe('14 events'); + expect(screen.getByTestId('group-count').textContent).toBe('3 groups'); + expect(screen.getAllByTestId('grouping-accordion').length).toBe(3); + expect(screen.queryByTestId('empty-results-panel')).not.toBeInTheDocument(); + }); + + it('Does not render empty state while loading', () => { + render( + + + + ); + expect(screen.queryByTestId('empty-results-panel')).not.toBeInTheDocument(); }); it('Does not render group counts when groupsCount = 0', () => { @@ -58,25 +69,25 @@ describe('grouping container', () => { value: 0, }, }; - const { getByTestId, queryByTestId } = render( + render( ); - expect(queryByTestId('unit-count')).not.toBeInTheDocument(); - expect(queryByTestId('group-count')).not.toBeInTheDocument(); - expect(queryByTestId('grouping-accordion')).not.toBeInTheDocument(); - expect(getByTestId('empty-results-panel')).toBeInTheDocument(); + expect(screen.queryByTestId('unit-count')).not.toBeInTheDocument(); + expect(screen.queryByTestId('group-count')).not.toBeInTheDocument(); + expect(screen.queryByTestId('grouping-accordion')).not.toBeInTheDocument(); + expect(screen.getByTestId('empty-results-panel')).toBeInTheDocument(); }); it('Opens one group at a time when each group is clicked', () => { - const { getAllByTestId } = render( + render( ); - const group1 = within(getAllByTestId('grouping-accordion')[0]).getAllByRole('button')[0]; - const group2 = within(getAllByTestId('grouping-accordion')[1]).getAllByRole('button')[0]; + const group1 = within(screen.getAllByTestId('grouping-accordion')[0]).getAllByRole('button')[0]; + const group2 = within(screen.getAllByTestId('grouping-accordion')[1]).getAllByRole('button')[0]; fireEvent.click(group1); expect(renderChildComponent).toHaveBeenNthCalledWith( 1, @@ -90,12 +101,12 @@ describe('grouping container', () => { }); it('Send Telemetry when each group is clicked', () => { - const { getAllByTestId } = render( + render( ); - const group1 = within(getAllByTestId('grouping-accordion')[0]).getAllByRole('button')[0]; + const group1 = within(screen.getAllByTestId('grouping-accordion')[0]).getAllByRole('button')[0]; fireEvent.click(group1); expect(mockTracker).toHaveBeenNthCalledWith( 1, @@ -120,19 +131,19 @@ describe('grouping container', () => { it('Renders a null group and passes the correct filter to take actions and child component', () => { takeActionItems.mockReturnValue([]); - const { getAllByTestId, getByTestId } = render( + render( ); - expect(getByTestId('null-group-icon')).toBeInTheDocument(); + expect(screen.getByTestId('null-group-icon')).toBeInTheDocument(); - let lastGroup = getAllByTestId('grouping-accordion').at(-1); + let lastGroup = screen.getAllByTestId('grouping-accordion').at(-1); fireEvent.click(within(lastGroup!).getByTestId('take-action-button')); expect(takeActionItems).toHaveBeenCalledWith(getNullGroupFilter('host.name'), 2); - lastGroup = getAllByTestId('grouping-accordion').at(-1); + lastGroup = screen.getAllByTestId('grouping-accordion').at(-1); fireEvent.click(within(lastGroup!).getByTestId('group-panel-toggle')); expect(renderChildComponent).toHaveBeenCalledWith(getNullGroupFilter('host.name')); @@ -149,7 +160,7 @@ describe('grouping container', () => { expect(groupPanelRenderer).toHaveBeenNthCalledWith( 1, 'host.name', - testProps.data.groupByFields.buckets[0], + testProps.data.groupByFields!.buckets![0], undefined, false ); @@ -157,7 +168,7 @@ describe('grouping container', () => { expect(groupPanelRenderer).toHaveBeenNthCalledWith( 2, 'host.name', - testProps.data.groupByFields.buckets[1], + testProps.data.groupByFields!.buckets![1], undefined, false ); @@ -165,7 +176,7 @@ describe('grouping container', () => { expect(groupPanelRenderer).toHaveBeenNthCalledWith( 3, 'host.name', - testProps.data.groupByFields.buckets[2], + testProps.data.groupByFields!.buckets![2], 'The selected group by field, host.name, is missing a value for this group of events.', false ); @@ -181,7 +192,7 @@ describe('grouping container', () => { expect(groupPanelRenderer).toHaveBeenNthCalledWith( 1, 'host.name', - testProps.data.groupByFields.buckets[0], + testProps.data.groupByFields!.buckets![0], undefined, true ); @@ -189,12 +200,12 @@ describe('grouping container', () => { describe('groupsUnit', () => { it('renders default groupsUnit text correctly', () => { - const { getByTestId } = render( + render( ); - expect(getByTestId('group-count').textContent).toBe('3 groups'); + expect(screen.getByTestId('group-count').textContent).toBe('3 groups'); }); it('calls custom groupsUnit callback correctly', () => { // Provide a custom groupsUnit function in testProps @@ -203,14 +214,14 @@ describe('grouping container', () => { ); const customProps = { ...testProps, groupsUnit: customGroupsUnit }; - const { getByTestId } = render( + render( ); expect(customGroupsUnit).toHaveBeenCalledWith(3, testProps.selectedGroup, true); - expect(getByTestId('group-count').textContent).toBe('3 custom units'); + expect(screen.getByTestId('group-count').textContent).toBe('3 custom units'); }); }); }); diff --git a/packages/kbn-grouping/src/components/grouping.tsx b/packages/kbn-grouping/src/components/grouping.tsx index 7fb8a9b9edd0ad..36d284cf28df62 100644 --- a/packages/kbn-grouping/src/components/grouping.tsx +++ b/packages/kbn-grouping/src/components/grouping.tsx @@ -222,10 +222,9 @@ const GroupingComponent = ({ css={groupingLevel > 0 ? groupingContainerCssLevel : groupingContainerCss} className="eui-xScroll" > - {isLoading && ( + {isLoading ? ( - )} - {groupCount > 0 ? ( + ) : groupCount > 0 ? ( {groupPanels} {groupCount > 0 && ( From 1bd81449242a1ab57e82c211808753e82f25c92c Mon Sep 17 00:00:00 2001 From: Alex Szabo Date: Mon, 14 Oct 2024 12:57:07 +0200 Subject: [PATCH 23/38] [CI] Add timeouts for retries for docker image build (#195915) ## Summary The generated version of the docker image builder script didn't have timeouts between retries, so a temporary outage on `docker.elastic.co` would cause a docker pull error, failing the build (see: https://buildkite.com/elastic/kibana-artifacts-snapshot/builds/4845#01927b40-43f9-471e-9e2c-407320fac978) This PR adds a fix 15s per retry to the docker build runner. --- .../docker_generator/templates/build_docker_sh.template.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts index 8bd2cc99a6684e..d383855865265a 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/build_docker_sh.template.ts @@ -57,7 +57,8 @@ function generator({ echo "Docker pull successful." break else - echo "Docker pull unsuccessful, attempt '$attempt'." + echo "Docker pull unsuccessful, attempt '$attempt'. Retrying in 15s" + sleep 15 fi done From 97322a871357ba69e7c64543831fbf1597ca8ff9 Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 14 Oct 2024 13:25:29 +0200 Subject: [PATCH 24/38] [ResponseOps][Rules] Fix case action templates in stack for security serverless (#195763) Fixes #195599 ## Summary This PR ensures that we can use templates in the case action when: 1. the project is serverless security, and 2. the rule is created in stack management ### How to test 1. Add the following line to `serverless.yml` - `xpack.cloud.serverless.project_id: test-123` 3. Start Elastic search in serverless security mode - `yarn es serverless --projectType security` 4. Start Kibana in serverless security mode - `yarn start --serverless=security` 5. Go to Security > Cases > Settings and add a template. 6. Go to stack and create a rule with the case action. 7. Confirm the template created in step 5 can be selected. Screenshot 2024-10-10 at 15 00 46 **Please double-check also that the templates in the case action still work as expected in normal scenarios.** --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../cases/cases_params.test.tsx | 56 +++++++++++++++++-- .../system_actions/cases/cases_params.tsx | 13 ++++- x-pack/plugins/cases/public/types.ts | 2 + .../public/application/rules_app.tsx | 2 + .../triggers_actions_ui/public/plugin.ts | 4 +- .../plugins/triggers_actions_ui/tsconfig.json | 3 +- 6 files changed, 72 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.test.tsx b/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.test.tsx index 0dc8ca9cbfb136..5e93d0b061a848 100644 --- a/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.test.tsx +++ b/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.test.tsx @@ -18,6 +18,7 @@ import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react import { useGetAllCaseConfigurations } from '../../../containers/configure/use_get_all_case_configurations'; import { useGetAllCaseConfigurationsResponse } from '../../configure_cases/__mock__'; import { templatesConfigurationMock } from '../../../containers/mock'; +import * as utils from '../../../containers/configure/utils'; jest.mock('@kbn/alerts-ui-shared/src/common/hooks/use_alerts_data_view'); jest.mock('../../../common/lib/kibana/use_application'); @@ -29,10 +30,6 @@ const useAlertsDataViewMock = jest.mocked(useAlertsDataView); const useApplicationMock = useApplication as jest.Mock; const useGetAllCaseConfigurationsMock = useGetAllCaseConfigurations as jest.Mock; -useKibanaMock.mockReturnValue({ - services: { ...createStartServicesMock(), data: { dataViews: {} } }, -} as unknown as ReturnType); - const actionParams = { subAction: 'run', subActionParams: { @@ -98,6 +95,9 @@ describe('CasesParamsFields renders', () => { }, }); useGetAllCaseConfigurationsMock.mockImplementation(() => useGetAllCaseConfigurationsResponse); + useKibanaMock.mockReturnValue({ + services: { ...createStartServicesMock(), data: { dataViews: {} } }, + } as unknown as ReturnType); }); afterEach(() => { @@ -268,6 +268,54 @@ describe('CasesParamsFields renders', () => { expect(await screen.findByText(templatesConfigurationMock[1].name)).toBeInTheDocument(); }); + it('renders security templates if the project is serverless security', async () => { + useKibanaMock.mockReturnValue({ + services: { + ...createStartServicesMock(), + // simulate a serverless security project + cloud: { isServerlessEnabled: true, serverless: { projectType: 'security' } }, + data: { dataViews: {} }, + }, + } as unknown as ReturnType); + + const configuration = { + ...useGetAllCaseConfigurationsResponse.data[0], + templates: templatesConfigurationMock, + }; + useGetAllCaseConfigurationsMock.mockImplementation(() => ({ + ...useGetAllCaseConfigurationsResponse, + data: [configuration], + })); + const getConfigurationByOwnerSpy = jest + .spyOn(utils, 'getConfigurationByOwner') + .mockImplementation(() => configuration); + + const observabilityOwnedRule = { + ...defaultProps, + // these two would normally produce an observability owner + producerId: 'observability', + featureId: 'observability', + actionParams: { + subAction: 'run', + subActionParams: { + ...actionParams.subActionParams, + templateId: templatesConfigurationMock[1].key, + }, + }, + }; + + render(); + + expect(getConfigurationByOwnerSpy).toHaveBeenCalledWith( + expect.objectContaining({ + // the security owner was forced + owner: 'securitySolution', + }) + ); + + getConfigurationByOwnerSpy.mockRestore(); + }); + it('updates template correctly', async () => { useGetAllCaseConfigurationsMock.mockReturnValueOnce({ ...useGetAllCaseConfigurationsResponse, diff --git a/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.tsx b/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.tsx index 6c93b2435af8e3..9fabf39db0bc45 100644 --- a/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.tsx +++ b/x-pack/plugins/cases/public/components/system_actions/cases/cases_params.tsx @@ -39,11 +39,20 @@ export const CasesParamsFieldsComponent: React.FunctionComponent< ActionParamsProps > = ({ actionParams, editAction, errors, index, producerId, featureId }) => { const { + cloud, + data: { dataViews: dataViewsService }, http, notifications: { toasts }, - data: { dataViews: dataViewsService }, } = useKibana().services; - const owner = getOwnerFromRuleConsumerProducer({ consumer: featureId, producer: producerId }); + + const owner = getOwnerFromRuleConsumerProducer({ + consumer: featureId, + producer: producerId, + // This is a workaround for a very specific bug with the cases action in serverless security + // More info here: https://github.com/elastic/kibana/issues/195599 + isServerlessSecurity: + cloud?.isServerlessEnabled && cloud?.serverless.projectType === 'security', + }); const { dataView, isLoading: loadingAlertDataViews } = useAlertsDataView({ http, diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index c857446ea042c2..7f1d4863eac7ec 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -30,6 +30,7 @@ import type { ContentManagementPublicStart } from '@kbn/content-management-plugi import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; +import type { CloudStart } from '@kbn/cloud-plugin/public'; import type { UseCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal'; import type { UseCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout'; import type { UseIsAddToCaseOpen } from './components/cases_context/state/use_is_add_to_case_open'; @@ -73,6 +74,7 @@ export interface CasesPublicSetupDependencies { export interface CasesPublicStartDependencies { apm?: ApmBase; + cloud?: CloudStart; data: DataPublicPluginStart; embeddable: EmbeddableStart; features: FeaturesPluginStart; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/rules_app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/rules_app.tsx index 7284489a29b2af..9f472c251a91bd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/rules_app.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/rules_app.tsx @@ -35,6 +35,7 @@ import { ruleDetailsRoute } from '@kbn/rule-data-utils'; import { QueryClientProvider } from '@tanstack/react-query'; import { DashboardStart } from '@kbn/dashboard-plugin/public'; import { ExpressionsStart } from '@kbn/expressions-plugin/public'; +import { CloudSetup } from '@kbn/cloud-plugin/public'; import { suspendedComponentWithProps } from './lib/suspended_component_with_props'; import { ActionTypeRegistryContract, @@ -61,6 +62,7 @@ const RuleDetailsRoute = lazy( export interface TriggersAndActionsUiServices extends CoreStart { actions: ActionsPublicPluginSetup; + cloud?: CloudSetup; data: DataPublicPluginStart; dataViews: DataViewsPublicPluginStart; dataViewEditor: DataViewEditorStart; diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index 514f77310bb0a0..33d8733b3b48b1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -32,6 +32,7 @@ import { FieldFormatsRegistry } from '@kbn/field-formats-plugin/common'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { RuleAction } from '@kbn/alerting-plugin/common'; import { TypeRegistry } from '@kbn/alerts-ui-shared/src/common/type_registry'; +import { CloudSetup } from '@kbn/cloud-plugin/public'; import { getAlertsTableDefaultAlertActionsLazy } from './common/get_alerts_table_default_row_actions'; import type { AlertActionsProps, RuleUiAction } from './types'; import type { AlertsSearchBarProps } from './application/sections/alerts_search_bar'; @@ -167,7 +168,7 @@ export interface TriggersAndActionsUIPublicPluginStart { interface PluginsSetup { management: ManagementSetup; home?: HomePublicPluginSetup; - cloud?: { isCloudEnabled: boolean }; + cloud?: CloudSetup; actions: ActionsPublicPluginSetup; } @@ -305,6 +306,7 @@ export class Plugin ...coreStart, actions: plugins.actions, dashboard: pluginsStart.dashboard, + cloud: plugins.cloud, data: pluginsStart.data, dataViews: pluginsStart.dataViews, dataViewEditor: pluginsStart.dataViewEditor, diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index ff488d41ccbbbd..f4b718bff46722 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -71,7 +71,8 @@ "@kbn/visualization-utils", "@kbn/core-ui-settings-browser", "@kbn/observability-alerting-rule-utils", - "@kbn/core-application-browser" + "@kbn/core-application-browser", + "@kbn/cloud-plugin" ], "exclude": ["target/**/*"] } From cc46549c2f293bed7d24d8b1abf02c4d65db7bcb Mon Sep 17 00:00:00 2001 From: Maxim Kholod Date: Mon, 14 Oct 2024 13:31:59 +0200 Subject: [PATCH 25/38] [Cloud Security] handle both rule.references and rule.reference in misconfiguraiton flyout (#195932) ## Summary Fixes: - https://github.com/elastic/security-team/issues/10793 --- .../schema/rules/v3.ts | 1 + .../findings_flyout/rule_tab.tsx | 194 +++++++++--------- 2 files changed, 100 insertions(+), 95 deletions(-) diff --git a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v3.ts b/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v3.ts index a00bf1a8077e6c..353fe093d64a16 100644 --- a/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v3.ts +++ b/x-pack/packages/kbn-cloud-security-posture-common/schema/rules/v3.ts @@ -37,6 +37,7 @@ export const cspBenchmarkRuleMetadataSchema = schema.object({ profile_applicability: schema.string(), rationale: schema.string(), references: schema.maybe(schema.string()), + reference: schema.maybe(schema.string()), rego_rule_id: schema.string(), remediation: schema.string(), section: schema.string(), diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/rule_tab.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/rule_tab.tsx index 2dcca4932935c2..a64ac099df14a2 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/rule_tab.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/rule_tab.tsx @@ -13,111 +13,115 @@ import type { CspFinding } from '@kbn/cloud-security-posture-common'; import { RulesDetectionRuleCounter } from '../../rules/rules_detection_rule_counter'; import { BenchmarkIcons, CspFlyoutMarkdown, EMPTY_VALUE, RuleNameLink } from './findings_flyout'; +const getReferenceFromRule = (rule?: CspFinding['rule']) => { + return rule?.reference || rule?.references; +}; + export const getRuleList = ( rule?: CspFinding['rule'], ruleState = 'unmuted', ruleFlyoutLink?: string -) => [ - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.nameTitle', { - defaultMessage: 'Name', - }), - description: rule?.name ? ( - - ) : ( - EMPTY_VALUE - ), - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.descriptionTitle', { - defaultMessage: 'Description', - }), - description: rule?.description ? ( - {rule.description} - ) : ( - EMPTY_VALUE - ), - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.AlertsTitle', { - defaultMessage: 'Alerts', - }), - description: - ruleState === 'muted' ? ( - - ) : rule?.benchmark?.name ? ( - +) => { + const reference = getReferenceFromRule(rule); + + return [ + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.nameTitle', { + defaultMessage: 'Name', + }), + description: rule?.name ? ( + + ) : ( + EMPTY_VALUE + ), + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.descriptionTitle', { + defaultMessage: 'Description', + }), + description: rule?.description ? ( + {rule.description} ) : ( EMPTY_VALUE ), - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.tagsTitle', { - defaultMessage: 'Tags', - }), - description: rule?.tags?.length ? ( - <> - {rule.tags.map((tag) => ( - {tag} - ))} - - ) : ( - EMPTY_VALUE - ), - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.frameworkSourcesTitle', { - defaultMessage: 'Framework Sources', - }), - description: - rule?.benchmark?.id && rule?.benchmark?.name ? ( - + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.AlertsTitle', { + defaultMessage: 'Alerts', + }), + description: + ruleState === 'muted' ? ( + + ) : rule?.benchmark?.name ? ( + + ) : ( + EMPTY_VALUE + ), + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.tagsTitle', { + defaultMessage: 'Tags', + }), + description: rule?.tags?.length ? ( + <> + {rule.tags.map((tag) => ( + {tag} + ))} + ) : ( EMPTY_VALUE ), - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.cisSectionTitle', { - defaultMessage: 'Framework Section', - }), - description: rule?.section || EMPTY_VALUE, - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.profileApplicabilityTitle', { - defaultMessage: 'Profile Applicability', - }), - description: rule?.profile_applicability ? ( - {rule.profile_applicability} - ) : ( - EMPTY_VALUE - ), - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.benchmarkTitle', { - defaultMessage: 'Benchmark', - }), - description: rule?.benchmark?.name || EMPTY_VALUE, - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.auditTitle', { - defaultMessage: 'Audit', - }), - description: rule?.audit ? {rule.audit} : EMPTY_VALUE, - }, - { - title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.referencesTitle', { - defaultMessage: 'References', - }), - description: rule?.references ? ( - {rule.references} - ) : ( - EMPTY_VALUE - ), - }, -]; + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.frameworkSourcesTitle', { + defaultMessage: 'Framework Sources', + }), + description: + rule?.benchmark?.id && rule?.benchmark?.name ? ( + + ) : ( + EMPTY_VALUE + ), + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.cisSectionTitle', { + defaultMessage: 'Framework Section', + }), + description: rule?.section || EMPTY_VALUE, + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.profileApplicabilityTitle', { + defaultMessage: 'Profile Applicability', + }), + description: rule?.profile_applicability ? ( + {rule.profile_applicability} + ) : ( + EMPTY_VALUE + ), + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.benchmarkTitle', { + defaultMessage: 'Benchmark', + }), + description: rule?.benchmark?.name || EMPTY_VALUE, + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.auditTitle', { + defaultMessage: 'Audit', + }), + description: rule?.audit ? {rule.audit} : EMPTY_VALUE, + }, + { + title: i18n.translate('xpack.csp.findings.findingsFlyout.ruleTab.referencesTitle', { + defaultMessage: 'References', + }), + description: reference ? {reference} : EMPTY_VALUE, + }, + ]; +}; export const RuleTab = ({ data, From cd60c66d19ffdb4130a5c2d1b76e80cfaf8cde78 Mon Sep 17 00:00:00 2001 From: Cee Chen <549407+cee-chen@users.noreply.github.com> Date: Mon, 14 Oct 2024 04:35:35 -0700 Subject: [PATCH 26/38] Upgrade EUI to v97.0.0 (#195525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `v96.1.0`⏩`v97.0.0` _[Questions? Please see our Kibana upgrade FAQ.](https://github.com/elastic/eui/blob/main/wiki/eui-team-processes/upgrading-kibana.md#faq-for-kibana-teams)_ --- ## [`v97.0.0`](https://github.com/elastic/eui/releases/v97.0.0) **Breaking changes** - EuiDataGrid's custom grid body (rendered via `renderCustomGridBody`) no longer automatically renders the column header row or footer rows. It instead now passes the `headerRow` and `footerRow` React elements, which require manual rendering. ([#8028](https://github.com/elastic/eui/pull/8028)) - This change was made to allow consumers to sync header/footer rows with their own custom virtualization libraries. - To facilitate this, a `gridWidth` prop is now also passed to custom grid body renderers. **Bug fixes** - Fixed inputs not taking the whole width when passing `fullWidth` as `true` to EuiDatePickerRange component ([#8061](https://github.com/elastic/eui/pull/8061)) **Accessibility** - Improved accessibility of `EuiExternalLinkIcon` by clarifying text for Screen Reader users. ([#8065](https://github.com/elastic/eui/pull/8065)) --------- Co-authored-by: Elastic Machine --- package.json | 2 +- .../__snapshots__/i18n_service.test.tsx.snap | 4 +-- .../src/i18n_eui_mapping.tsx | 11 +++--- .../src/test_utils/index.ts | 3 +- .../src/utils/get_render_cell_value.test.tsx | 4 +-- src/dev/license_checker/config.ts | 2 +- .../url/__snapshots__/url.test.tsx.snap | 14 ++++---- .../lib/embeddables/error_embeddable.test.tsx | 7 ++-- .../dashboard_link_component.test.tsx | 8 ++--- .../external_link_component.test.tsx | 10 +++--- .../not_found_errors.test.tsx.snap | 28 +++++++-------- .../components/not_found_errors.test.tsx | 8 ++--- .../legend/__snapshots__/legend.test.tsx.snap | 2 +- .../__snapshots__/app.test.tsx.snap | 2 +- .../cases/public/common/test_utils.tsx | 2 +- .../extend_index_management.test.tsx.snap | 28 +++++++-------- .../request_trial_extension.test.js.snap | 28 +++++++-------- .../revert_to_basic.test.js.snap | 21 +++++------ .../__snapshots__/start_trial.test.js.snap | 28 +++++++-------- .../__snapshots__/exporters.test.js.snap | 21 +++++------ .../__snapshots__/reason_found.test.js.snap | 21 +++++------ .../link_preview.test.tsx | 3 +- .../journeys/alerting_default.journey.ts | 8 ++--- .../journeys/data_retention.journey.ts | 6 ++-- .../__snapshots__/expanded_row.test.tsx.snap | 7 ++-- .../monitor/ping_list/doc_link_body.test.tsx | 2 +- .../monitor_status.bar.test.tsx.snap | 7 ++-- .../unauthenticated_page.test.tsx.snap | 4 +-- .../reset_session_page.test.tsx.snap | 4 +-- .../components/investigation_guide.test.tsx | 2 +- .../left/components/response_details.test.tsx | 2 +- .../left/components/session_view.test.tsx | 2 +- .../analyzer_preview_container.test.tsx | 2 +- .../session_preview_container.test.tsx | 2 +- .../session_view_no_data_message.test.tsx | 2 +- .../console/components/command_list.test.tsx | 2 +- .../related_detection_rules_callout.test.tsx | 2 +- .../components/setting_locked_card.test.tsx | 2 +- .../field_renderers.test.tsx.snap | 7 ++-- .../netflow/__snapshots__/index.test.tsx.snap | 35 ++++++++----------- .../netflow_row_renderer.test.tsx.snap | 35 ++++++++----------- .../custom_timeline_data_grid_body.test.tsx | 3 ++ .../custom_timeline_data_grid_body.tsx | 15 ++++++-- .../unified_components/data_table/index.tsx | 6 ++++ .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../components/health_check.test.tsx | 12 +++---- .../sections/alerts_table/alerts_table.tsx | 26 ++++++++------ ...lert_details_left_panel_response_tab.cy.ts | 2 +- ...ert_details_right_panel_overview_tab.cy.ts | 2 +- .../investigations/timelines/notes_tab.cy.ts | 4 ++- .../cypress/screens/rule_details.ts | 3 +- yarn.lock | 8 ++--- 54 files changed, 228 insertions(+), 246 deletions(-) diff --git a/package.json b/package.json index f6412a3da925a4..22a3f88bfe03e0 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "@elastic/ecs": "^8.11.1", "@elastic/elasticsearch": "^8.15.0", "@elastic/ems-client": "8.5.3", - "@elastic/eui": "96.1.0", + "@elastic/eui": "97.0.0", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", diff --git a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap index 23a5239116c988..bd50f4ffe0e442 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap +++ b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap @@ -140,8 +140,8 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiDisplaySelector.rowHeightLabel": "Row height", "euiDualRange.sliderScreenReaderInstructions": "You are in a custom range slider. Use the Up and Down arrow keys to change the minimum value. Press Tab to interact with the maximum value.", "euiErrorBoundary.error": "Error", - "euiExternalLinkIcon.ariaLabel": "External link", - "euiExternalLinkIcon.newTarget.screenReaderOnlyText": "(opens in a new tab or window)", + "euiExternalLinkIcon.externalTarget.screenReaderOnlyText": "(external)", + "euiExternalLinkIcon.newTarget.screenReaderOnlyText": "(external, opens in a new tab or window)", "euiFieldPassword.maskPassword": "Mask password", "euiFieldPassword.showPassword": "Show password as plain text. Note: this will visually expose your password on the screen.", "euiFieldSearch.clearSearchButtonLabel": "Clear search input", diff --git a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx index 3fa687fddd9dae..ad1ba505dc6f2d 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx +++ b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx @@ -860,13 +860,16 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'core.euiInlineEditForm.saveButtonAriaLabel', { defaultMessage: 'Save edit' } ), - 'euiExternalLinkIcon.ariaLabel': i18n.translate('core.euiExternalLinkIcon.ariaLabel', { - defaultMessage: 'External link', - }), + 'euiExternalLinkIcon.externalTarget.screenReaderOnlyText': i18n.translate( + 'core.euiExternalLinkIcon.externalTarget.screenReaderOnlyText', + { + defaultMessage: '(external)', + } + ), 'euiExternalLinkIcon.newTarget.screenReaderOnlyText': i18n.translate( 'core.euiExternalLinkIcon.newTarget.screenReaderOnlyText', { - defaultMessage: '(opens in a new tab or window)', + defaultMessage: '(external, opens in a new tab or window)', } ), 'euiLoadingStrings.ariaLabel': i18n.translate('core.euiLoadingStrings.ariaLabel', { diff --git a/packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts b/packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts index 2dd31d37414f10..c96b575f439c4a 100644 --- a/packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts +++ b/packages/kbn-securitysolution-io-ts-utils/src/test_utils/index.ts @@ -53,5 +53,4 @@ export const getPaths = (validation: t.Validation): string[] => { /** * Convenience utility to remove text appended to links by EUI */ -export const removeExternalLinkText = (str: string) => - str.replace(/\(opens in a new tab or window\)/g, ''); +export const removeExternalLinkText = (str: string) => str.replace(/\(external[^)]*\)/g, ''); diff --git a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx index 7775b35ba5f68d..11636b9d1f7614 100644 --- a/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx +++ b/packages/kbn-unified-data-table/src/utils/get_render_cell_value.test.tsx @@ -157,7 +157,7 @@ describe('Unified data table cell rendering', function () { /> ); expect(component.html()).toMatchInlineSnapshot( - `"

"` + `"
100
"` ); }); @@ -184,7 +184,7 @@ describe('Unified data table cell rendering', function () { /> ); expect(component.html()).toMatchInlineSnapshot( - `"
100
"` + `"
100
"` ); findTestSubject(component, 'docTableClosePopover').simulate('click'); expect(closePopoverMockFn).toHaveBeenCalledTimes(1); diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index c4d5357ceefb13..ce399520cbc12e 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -87,7 +87,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.5.3': ['Elastic License 2.0'], - '@elastic/eui@96.1.0': ['Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0'], + '@elastic/eui@97.0.0': ['Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry 'buffers@0.1.1': ['MIT'], // license in importing module https://www.npmjs.com/package/binary '@bufbuild/protobuf@1.2.1': ['Apache-2.0'], // license (Apache-2.0 AND BSD-3-Clause) diff --git a/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap b/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap index 1780ae7a8ced8d..6d6ac340f63223 100644 --- a/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap +++ b/src/plugins/data_view_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap @@ -170,13 +170,12 @@ exports[`UrlFormatEditor should render normally 1`] = ` - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window)
@@ -231,13 +230,12 @@ exports[`UrlFormatEditor should render normally 1`] = ` - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window)
diff --git a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx index db9b322c077cdc..77ed3a1bc2f1d6 100644 --- a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx @@ -42,13 +42,12 @@ test('ErrorEmbeddable renders an embeddable with markdown message', async () => - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) `); diff --git a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx index d60ba248d14fc5..b245870a8757a7 100644 --- a/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx +++ b/src/plugins/links/public/components/dashboard_link/dashboard_link_component.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { DEFAULT_DASHBOARD_DRILLDOWN_OPTIONS } from '@kbn/presentation-util-plugin/public'; -import { createEvent, fireEvent, render, screen, within } from '@testing-library/react'; +import { createEvent, fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { LINKS_VERTICAL_LAYOUT } from '../../../common/content_management'; @@ -75,7 +75,7 @@ describe('Dashboard link component', () => { expect(link).toHaveTextContent('Dashboard 1'); // does not render external link icon - const externalIcon = within(link).queryByText('External link'); + const externalIcon = link.querySelector('[data-euiicon-type="popout"]'); expect(externalIcon).toBeNull(); // calls `navigate` on click @@ -122,8 +122,8 @@ describe('Dashboard link component', () => { const link = screen.getByTestId('dashboardLink--foo'); expect(link).toBeInTheDocument(); // external link icon is rendered - const externalIcon = within(link).getByText('External link'); - expect(externalIcon?.getAttribute('data-euiicon-type')).toBe('popout'); + const externalIcon = link.querySelector('[data-euiicon-type="popout"]'); + expect(externalIcon).toBeInTheDocument(); // calls `window.open` await userEvent.click(link); diff --git a/src/plugins/links/public/components/external_link/external_link_component.test.tsx b/src/plugins/links/public/components/external_link/external_link_component.test.tsx index 4230e28b702e75..b80cf30e89f391 100644 --- a/src/plugins/links/public/components/external_link/external_link_component.test.tsx +++ b/src/plugins/links/public/components/external_link/external_link_component.test.tsx @@ -10,7 +10,7 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; -import { createEvent, fireEvent, render, screen, within } from '@testing-library/react'; +import { createEvent, fireEvent, render, screen } from '@testing-library/react'; import { LINKS_VERTICAL_LAYOUT } from '../../../common/content_management'; import { ExternalLinkComponent } from './external_link_component'; import { coreServices } from '../../services/kibana_services'; @@ -39,8 +39,8 @@ describe('external link component', () => { const link = await screen.findByTestId('externalLink--foo'); expect(link).toBeInTheDocument(); - const externalIcon = within(link).getByText('External link'); - expect(externalIcon.getAttribute('data-euiicon-type')).toBe('popout'); + const externalIcon = link.querySelector('[data-euiicon-type="popout"]'); + expect(externalIcon).toBeInTheDocument(); await userEvent.click(link); expect(window.open).toHaveBeenCalledWith('https://example.com', '_blank'); }); @@ -52,8 +52,8 @@ describe('external link component', () => { }; render(); const link = await screen.findByTestId('externalLink--foo'); - const externalIcon = within(link).getByText('External link'); - expect(externalIcon?.getAttribute('data-euiicon-type')).toBe('popout'); + const externalIcon = link.querySelector('[data-euiicon-type="popout"]'); + expect(externalIcon).toBeInTheDocument(); }); test('modified click does not trigger event.preventDefault', async () => { diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap index 829472941701c1..0c5045a1c8662b 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/__snapshots__/not_found_errors.test.tsx.snap @@ -37,13 +37,12 @@ exports[`NotFoundErrors component renders correctly for index-pattern type 1`] = - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) to fix it — otherwise click the delete button above. @@ -89,13 +88,12 @@ exports[`NotFoundErrors component renders correctly for index-pattern-field type - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) to fix it — otherwise click the delete button above. @@ -141,13 +139,12 @@ exports[`NotFoundErrors component renders correctly for search type 1`] = ` - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) to fix it — otherwise click the delete button above. @@ -191,13 +188,12 @@ exports[`NotFoundErrors component renders correctly for unknown type 1`] = ` - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window) to fix it — otherwise click the delete button above. diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx index 72604fbda1fc35..41919c9172e564 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/not_found_errors.test.tsx @@ -26,7 +26,7 @@ describe('NotFoundErrors component', () => { const callOut = mounted.find('EuiCallOut'); expect(callOut.render()).toMatchSnapshot(); expect(mounted.text()).toMatchInlineSnapshot( - `"There is a problem with this saved objectThe saved search associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIsExternal link(opens in a new tab or window) to fix it — otherwise click the delete button above."` + `"There is a problem with this saved objectThe saved search associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIs(external, opens in a new tab or window) to fix it — otherwise click the delete button above."` ); }); @@ -35,7 +35,7 @@ describe('NotFoundErrors component', () => { const callOut = mounted.find('EuiCallOut'); expect(callOut.render()).toMatchSnapshot(); expect(mounted.text()).toMatchInlineSnapshot( - `"There is a problem with this saved objectThe data view associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIsExternal link(opens in a new tab or window) to fix it — otherwise click the delete button above."` + `"There is a problem with this saved objectThe data view associated with this object no longer exists.If you know what this error means, you can use the Saved objects APIs(external, opens in a new tab or window) to fix it — otherwise click the delete button above."` ); }); @@ -44,7 +44,7 @@ describe('NotFoundErrors component', () => { const callOut = mounted.find('EuiCallOut'); expect(callOut.render()).toMatchSnapshot(); expect(mounted.text()).toMatchInlineSnapshot( - `"There is a problem with this saved objectA field associated with this object no longer exists in the data view.If you know what this error means, you can use the Saved objects APIsExternal link(opens in a new tab or window) to fix it — otherwise click the delete button above."` + `"There is a problem with this saved objectA field associated with this object no longer exists in the data view.If you know what this error means, you can use the Saved objects APIs(external, opens in a new tab or window) to fix it — otherwise click the delete button above."` ); }); @@ -53,7 +53,7 @@ describe('NotFoundErrors component', () => { const callOut = mounted.find('EuiCallOut'); expect(callOut.render()).toMatchSnapshot(); expect(mounted.text()).toMatchInlineSnapshot( - `"There is a problem with this saved objectIf you know what this error means, you can use the Saved objects APIsExternal link(opens in a new tab or window) to fix it — otherwise click the delete button above."` + `"There is a problem with this saved objectIf you know what this error means, you can use the Saved objects APIs(external, opens in a new tab or window) to fix it — otherwise click the delete button above."` ); }); }); diff --git a/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap b/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap index 05da269b239b0c..c9a3c41edca938 100644 --- a/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap +++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap @@ -2,4 +2,4 @@ exports[`VisLegend Component Legend closed should match the snapshot 1`] = `"
"`; -exports[`VisLegend Component Legend open should match the snapshot 1`] = `"
"`; +exports[`VisLegend Component Legend open should match the snapshot 1`] = `"
"`; diff --git a/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap b/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap index 4aa379aa194bc5..447771435a1dc4 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap +++ b/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` App renders properly 1`] = `"
markdown mock
markdown mock

Page level controls

My Canvas Workpad

There is a new region landmark with page level controls at the end of the document.

"`; +exports[` App renders properly 1`] = `"
markdown mock
markdown mock

Page level controls

My Canvas Workpad

There is a new region landmark with page level controls at the end of the document.

"`; diff --git a/x-pack/plugins/cases/public/common/test_utils.tsx b/x-pack/plugins/cases/public/common/test_utils.tsx index 0028d79019f2aa..1cbf5e2a5d4544 100644 --- a/x-pack/plugins/cases/public/common/test_utils.tsx +++ b/x-pack/plugins/cases/public/common/test_utils.tsx @@ -18,7 +18,7 @@ import { EuiButton } from '@elastic/eui'; * Convenience utility to remove text appended to links by EUI */ export const removeExternalLinkText = (str: string | null) => - str?.replace(/\(opens in a new tab or window\)/g, ''); + str?.replace(/\(external[^)]*\)/g, ''); export async function waitForComponentToPaint

(wrapper: ReactWrapper

, amount = 0) { await act(async () => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.tsx.snap b/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.tsx.snap index 31555edfad65ff..ff8d0f4d7caa2d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.tsx.snap +++ b/x-pack/plugins/index_lifecycle_management/__jest__/__snapshots__/extend_index_management.test.tsx.snap @@ -132,13 +132,12 @@ exports[`extend index management ilm summary extension should render a phase def - External link - + role="presentation" + /> - (opens in a new tab or window) + (external, opens in a new tab or window)