From cf5d2e70da3af649bd97931c4e95eee954aa0bb3 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 18 Oct 2024 14:59:44 +0200 Subject: [PATCH 1/6] [React@18 failing test] fix discover/group3 discover doc viewer flyout accessibility should use expected a11y attributes (#196844) ## Summary We're working on upgrading Kibana to React@18 (in Legacy Mode). There are a couple failing tests when running React@18 in Legacy mode and this is one of them [[job]](https://buildkite.com/elastic/kibana-pull-request/builds/243743#01929ee7-11b8-41c3-af79-1437561a6ef0) [[logs]](https://buildkite.com/organizations/elastic/pipelines/kibana-pull-request/builds/243743/jobs/01929ee7-11b8-41c3-af79-1437561a6ef0/artifacts/01929f0a-dc7a-42ff-9a01-809c31e1dc71) FTR Configs #58 / discover/group3 discover doc viewer flyout accessibility should use expected a11y attributes This one is simple. Native to react `useId` implementation produces ids like this: `:r3:` and when you attempt to use such ids with id selector they're invalid . e.g. `document.querySelector('#:r3:')` throws an error. A workaround is to use attribute selector `document.querySelector('[id=":r3:"]')`. This is the same problem as we've seen before https://github.com/elastic/kibana/pull/191632 --- test/functional/apps/discover/group3/_doc_viewer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/discover/group3/_doc_viewer.ts b/test/functional/apps/discover/group3/_doc_viewer.ts index 542cdab9543ec..6d922e6b2a889 100644 --- a/test/functional/apps/discover/group3/_doc_viewer.ts +++ b/test/functional/apps/discover/group3/_doc_viewer.ts @@ -625,7 +625,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); expect(role).to.be('dialog'); expect(tabindex).to.be('0'); - expect(await find.existsByCssSelector(`#${describedBy}`)).to.be(true); + expect(await find.existsByCssSelector(`[id="${describedBy}"]`)).to.be(true); expect(noFocusLock).to.be('true'); // overlay flyout await reduceScreenWidth(); @@ -635,7 +635,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { noFocusLock = await testSubjects.getAttribute('docViewerFlyout', 'data-no-focus-lock'); expect(role).to.be('dialog'); expect(tabindex).to.be('0'); - expect(await find.existsByCssSelector(`#${describedBy}`)).to.be(true); + expect(await find.existsByCssSelector(`[id="${describedBy}"]`)).to.be(true); expect(noFocusLock).to.be(null); }); }); From 8bf08bfee6fe15d13054bf8de7f6bc8476b9de73 Mon Sep 17 00:00:00 2001 From: Jon Date: Fri, 18 Oct 2024 08:00:57 -0500 Subject: [PATCH 2/6] [ci] Use es snapshot cache for jest integration tests (#196695) --- .buildkite/scripts/copy_es_snapshot_cache.sh | 14 ++++++++++++++ .buildkite/scripts/steps/functional/common.sh | 12 +----------- .buildkite/scripts/steps/test/jest_integration.sh | 1 + 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100755 .buildkite/scripts/copy_es_snapshot_cache.sh diff --git a/.buildkite/scripts/copy_es_snapshot_cache.sh b/.buildkite/scripts/copy_es_snapshot_cache.sh new file mode 100755 index 0000000000000..d2b325168482a --- /dev/null +++ b/.buildkite/scripts/copy_es_snapshot_cache.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# If cached snapshots are baked into the agent, copy them into our workspace first +# We are doing this rather than simply changing the ES base path because many workers +# run with the workspace mounted in memory or on a local ssd +cacheDir="$ES_CACHE_DIR/cache" +if [[ -d "$cacheDir" ]]; then + mkdir -p .es/cache + echo "--- Copying ES snapshot cache" + echo "Copying cached snapshots from $cacheDir to .es/cache" + cp -R "$cacheDir"/* .es/cache/ +fi diff --git a/.buildkite/scripts/steps/functional/common.sh b/.buildkite/scripts/steps/functional/common.sh index edb618f692093..5a58594404b15 100755 --- a/.buildkite/scripts/steps/functional/common.sh +++ b/.buildkite/scripts/steps/functional/common.sh @@ -8,17 +8,7 @@ source .buildkite/scripts/common/util.sh .buildkite/scripts/bootstrap.sh .buildkite/scripts/download_build_artifacts.sh - -# If cached snapshots are baked into the agent, copy them into our workspace first -# We are doing this rather than simply changing the ES base path because many workers -# run with the workspace mounted in memory or on a local ssd -cacheDir="$ES_CACHE_DIR/cache" -if [[ -d "$cacheDir" ]]; then - mkdir -p .es/cache - echo "--- Copying ES snapshot cache" - echo "Copying cached snapshots from $cacheDir to .es/cache" - cp -R "$cacheDir"/* .es/cache/ -fi +.buildkite/scripts/copy_es_snapshot_cache.sh is_test_execution_step diff --git a/.buildkite/scripts/steps/test/jest_integration.sh b/.buildkite/scripts/steps/test/jest_integration.sh index fd7b9a1d6ad54..83f9509f362ca 100755 --- a/.buildkite/scripts/steps/test/jest_integration.sh +++ b/.buildkite/scripts/steps/test/jest_integration.sh @@ -7,6 +7,7 @@ source .buildkite/scripts/common/util.sh is_test_execution_step .buildkite/scripts/bootstrap.sh +.buildkite/scripts/copy_es_snapshot_cache.sh echo '--- Jest Integration Tests' .buildkite/scripts/steps/test/jest_parallel.sh jest.integration.config.js From 13e19cb645e3e3b037ea40809dfbfdaf93529169 Mon Sep 17 00:00:00 2001 From: Ash <1849116+ashokaditya@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:10:30 +0200 Subject: [PATCH 3/6] [DataUsage][Serverless] Data usage charts enhancements (#196559) ## Summary follow up of: - elastic/kibana/pull/195556 Adds a lot of enhancements to the datastream dropdown including: - [x] shows storage sizes on the data stream dropdown - [x] preselects all data streams on the first page load - [x] updates selected data streams to URL params - [x] selects data streams based on URL load - [x] doesn't allow deselecting all data streams - [x] cancels older API requests ### screen ![Screenshot 2024-10-16 at 16 57 43](https://github.com/user-attachments/assets/38db2d93-f531-4269-88ea-51b4926b6a72) ### clip ![metrics-ux-16-10](https://github.com/user-attachments/assets/7913d1b6-31df-48e6-a3a9-f4dad0dc1b1e) related PRs - elastic/kibana/pull/193966 ### 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> --- .../public/app/components/chart_panel.tsx | 7 +- .../app/components/data_usage_metrics.tsx | 76 ++++++++---- .../app/components/filters/charts_filter.tsx | 114 ++++++++---------- .../app/components/filters/charts_filters.tsx | 25 ++-- .../components/filters/clear_all_button.tsx | 43 ------- .../public/app/hooks/use_charts_filter.tsx | 46 +++---- .../app/hooks/use_charts_url_params.tsx | 4 +- .../data_usage/public/app/translations.tsx | 3 - .../public/hooks/use_get_data_streams.ts | 17 +-- .../public/hooks/use_get_usage_metrics.ts | 3 +- .../data_usage/public/utils/format_bytes.ts | 12 ++ .../routes/internal/data_streams_handler.ts | 2 +- .../routes/internal/usage_metrics_handler.ts | 2 + x-pack/plugins/data_usage/tsconfig.json | 1 - 14 files changed, 160 insertions(+), 195 deletions(-) delete mode 100644 x-pack/plugins/data_usage/public/app/components/filters/clear_all_button.tsx create mode 100644 x-pack/plugins/data_usage/public/utils/format_bytes.ts diff --git a/x-pack/plugins/data_usage/public/app/components/chart_panel.tsx b/x-pack/plugins/data_usage/public/app/components/chart_panel.tsx index 1ba3f0fe3f454..7554716c59492 100644 --- a/x-pack/plugins/data_usage/public/app/components/chart_panel.tsx +++ b/x-pack/plugins/data_usage/public/app/components/chart_panel.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React, { useCallback, useMemo } from 'react'; -import numeral from '@elastic/numeral'; + import { EuiFlexItem, EuiPanel, EuiTitle, useEuiTheme } from '@elastic/eui'; import { Chart, @@ -20,6 +20,7 @@ import { import { i18n } from '@kbn/i18n'; import { LegendAction } from './legend_action'; import { MetricTypes, MetricSeries } from '../../../common/rest_types'; +import { formatBytes } from '../../utils/format_bytes'; // TODO: Remove this when we have a title for each metric type type ChartKey = Extract; @@ -118,7 +119,3 @@ export const ChartPanel: React.FC = ({ ); }; - -const formatBytes = (bytes: number) => { - return numeral(bytes).format('0.0 b'); -}; 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 index cc443c78562ee..48b6566df9e66 100644 --- 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 @@ -5,9 +5,9 @@ * 2.0. */ -import React, { useCallback, useEffect, memo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { css } from '@emotion/react'; -import { EuiFlexGroup, EuiFlexItem, EuiLoadingElastic, EuiCallOut } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingElastic } from '@elastic/eui'; import { Charts } from './charts'; import { useBreadcrumbs } from '../../utils/use_breadcrumbs'; import { useKibanaContextForPlugin } from '../../utils/use_kibana'; @@ -16,21 +16,22 @@ 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'; +import { ChartFilters, ChartFiltersProps } from './filters/charts_filters'; +import { useGetDataUsageDataStreams } from '../../hooks/use_get_data_streams'; const EuiItemCss = css` width: 100%; `; -const FlexItemWithCss = memo(({ children }: { children: React.ReactNode }) => ( +const FlexItemWithCss = ({ children }: { children: React.ReactNode }) => ( {children} -)); +); export const DataUsageMetrics = () => { const { services: { chrome, appParams }, } = useKibanaContextForPlugin(); + useBreadcrumbs([{ text: PLUGIN_NAME }], appParams, chrome); const { metricTypes: metricTypesFromUrl, @@ -38,9 +39,17 @@ export const DataUsageMetrics = () => { startDate: startDateFromUrl, endDate: endDateFromUrl, setUrlMetricTypesFilter, + setUrlDataStreamsFilter, setUrlDateRangeFilter, } = useDataUsageMetricsUrlParams(); + const { data: dataStreams, isFetching: isFetchingDataStreams } = useGetDataUsageDataStreams({ + selectedDataStreams: dataStreamsFromUrl, + options: { + enabled: true, + }, + }); + const [metricsFilters, setMetricsFilters] = useState({ metricTypes: [...DEFAULT_METRIC_TYPES], dataStreams: [], @@ -52,15 +61,22 @@ export const DataUsageMetrics = () => { if (!metricTypesFromUrl) { setUrlMetricTypesFilter(metricsFilters.metricTypes.join(',')); } + if (!dataStreamsFromUrl && dataStreams) { + setUrlDataStreamsFilter(dataStreams.map((ds) => ds.name).join(',')); + } if (!startDateFromUrl || !endDateFromUrl) { setUrlDateRangeFilter({ startDate: metricsFilters.from, endDate: metricsFilters.to }); } }, [ + dataStreams, + dataStreamsFromUrl, endDateFromUrl, metricTypesFromUrl, + metricsFilters.dataStreams, metricsFilters.from, metricsFilters.metricTypes, metricsFilters.to, + setUrlDataStreamsFilter, setUrlDateRangeFilter, setUrlMetricTypesFilter, startDateFromUrl, @@ -77,7 +93,6 @@ export const DataUsageMetrics = () => { const { dateRangePickerState, onRefreshChange, onTimeChange } = useDateRangePicker(); const { - error, data, isFetching, isFetched, @@ -90,6 +105,7 @@ export const DataUsageMetrics = () => { }, { retry: false, + enabled: !!metricsFilters.dataStreams.length, } ); @@ -111,33 +127,51 @@ export const DataUsageMetrics = () => { [setMetricsFilters] ); - useBreadcrumbs([{ text: PLUGIN_NAME }], appParams, chrome); + const filterOptions: ChartFiltersProps['filterOptions'] = useMemo(() => { + const dataStreamsOptions = dataStreams?.reduce>((acc, ds) => { + acc[ds.name] = ds.storageSizeBytes; + return acc; + }, {}); + + return { + dataStreams: { + filterName: 'dataStreams', + options: dataStreamsOptions ? Object.keys(dataStreamsOptions) : metricsFilters.dataStreams, + appendOptions: dataStreamsOptions, + selectedOptions: metricsFilters.dataStreams, + onChangeFilterOptions: onChangeDataStreamsFilter, + isFilterLoading: isFetchingDataStreams, + }, + metricTypes: { + filterName: 'metricTypes', + options: metricsFilters.metricTypes, + onChangeFilterOptions: onChangeMetricTypesFilter, + }, + }; + }, [ + dataStreams, + isFetchingDataStreams, + metricsFilters.dataStreams, + metricsFilters.metricTypes, + onChangeDataStreamsFilter, + onChangeMetricTypesFilter, + ]); return ( - {!isFetching && error?.message && ( - - - - )} + {isFetched && data?.metrics ? ( 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 index 466bc6debae77..83d417565f012 100644 --- 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 @@ -7,7 +7,7 @@ import { orderBy } from 'lodash/fp'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiPopoverTitle, EuiSelectable } from '@elastic/eui'; +import { EuiPopoverTitle, EuiSelectable } from '@elastic/eui'; import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; import { @@ -15,7 +15,6 @@ import { 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'; @@ -27,20 +26,34 @@ const getSearchPlaceholder = (filterName: FilterName) => { return UX_LABELS.filterSearchPlaceholder('metric types'); }; -export const ChartsFilter = memo( +export interface ChartsFilterProps { + filterOptions: { + filterName: FilterName; + options: string[]; + appendOptions?: Record; + selectedOptions?: string[]; + onChangeFilterOptions: (selectedOptions: string[]) => void; + isFilterLoading?: boolean; + }; + 'data-test-subj'?: string; +} + +export const ChartsFilter = memo( ({ - filterName, - onChangeFilterOptions, + filterOptions: { + filterName, + options, + appendOptions, + selectedOptions, + onChangeFilterOptions, + isFilterLoading = false, + }, '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(() => { @@ -50,11 +63,8 @@ export const ChartsFilter = memo( setIsPopoverOpen(false); }, [setIsPopoverOpen]); - // search string state - const [searchString, setSearchString] = useState(''); const { areDataStreamsSelectedOnMount, - isLoading, items, setItems, hasActiveFilters, @@ -64,17 +74,18 @@ export const ChartsFilter = memo( setUrlDataStreamsFilter, setUrlMetricTypesFilter, } = useChartsFilter({ - filterName, - searchString, + filterOptions: { + filterName, + options, + appendOptions, + selectedOptions, + onChangeFilterOptions, + isFilterLoading, + }, }); // 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( @@ -104,8 +115,16 @@ export const ChartsFilter = memo( const onOptionsChange = useCallback( (newOptions: FilterItems) => { + const optionItemsToSet = newOptions.map((option) => option); + const currChecks = optionItemsToSet.filter((option) => option.checked === 'on'); + + // don't update filter state if trying to uncheck all options + if (currChecks.length < 1) { + return; + } + // update filter UI options state - setItems(newOptions.map((option) => option)); + setItems(optionItemsToSet); // compute a selected list of options const selectedItems = newOptions.reduce((acc, curr) => { @@ -129,10 +148,7 @@ export const ChartsFilter = memo( shouldPinSelectedDataStreams(false); setAreDataStreamsSelectedOnMount(false); - // update overall query state - if (typeof onChangeFilterOptions !== 'undefined') { - onChangeFilterOptions(selectedItems); - } + onChangeFilterOptions(selectedItems); }, [ setItems, @@ -146,35 +162,11 @@ export const ChartsFilter = memo( ] ); - // 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, - ]); + useEffect(() => { + return () => { + wasPopoverOpen.current = isPopoverOpen; + }; + }, [isPopoverOpen, wasPopoverOpen]); return ( setSearchString(searchValue.trim()), }} > {(list, search) => { @@ -215,17 +206,6 @@ export const ChartsFilter = memo( )} {list} - {!isMetricsFilter && ( - - - - - - )} ); }} 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 index 72608f4a62c75..6f3b07e37dc83 100644 --- 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 @@ -14,13 +14,13 @@ import type { 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'; +import { ChartsFilter, ChartsFilterProps } from './charts_filter'; +import { FilterName } from '../../hooks'; -interface ChartFiltersProps { +export interface ChartFiltersProps { dateRangePickerState: DateRangePickerValues; isDataLoading: boolean; - onChangeDataStreamsFilter: (selectedDataStreams: string[]) => void; - onChangeMetricTypesFilter?: (selectedMetricTypes: string[]) => void; + filterOptions: Record; onRefresh: () => void; onRefreshChange: (evt: OnRefreshChangeProps) => void; onTimeChange: ({ start, end }: DurationRange) => void; @@ -33,9 +33,8 @@ export const ChartFilters = memo( ({ dateRangePickerState, isDataLoading, + filterOptions, onClick, - onChangeMetricTypesFilter, - onChangeDataStreamsFilter, onRefresh, onRefreshChange, onTimeChange, @@ -47,19 +46,13 @@ export const ChartFilters = memo( const filters = useMemo(() => { return ( <> - {showMetricsTypesFilter && ( - + {showMetricsTypesFilter && } + {!filterOptions.dataStreams.isFilterLoading && ( + )} - ); - }, [onChangeDataStreamsFilter, onChangeMetricTypesFilter, showMetricsTypesFilter]); + }, [filterOptions, showMetricsTypesFilter]); const onClickRefreshButton = useCallback(() => onClick(), [onClick]); 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 deleted file mode 100644 index afa4c2fe72917..0000000000000 --- a/x-pack/plugins/data_usage/public/app/components/filters/clear_all_button.tsx +++ /dev/null @@ -1,43 +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, { 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/hooks/use_charts_filter.tsx b/x-pack/plugins/data_usage/public/app/hooks/use_charts_filter.tsx index 330c9a633396d..5cff100d9752e 100644 --- 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 @@ -11,9 +11,10 @@ import { 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'; +import { formatBytes } from '../../utils/format_bytes'; +import { ChartsFilterProps } from '../components/filters/charts_filter'; export type FilterName = keyof typeof FILTER_NAMES; @@ -26,14 +27,11 @@ export type FilterItems = Array<{ }>; export const useChartsFilter = ({ - filterName, - searchString, + filterOptions, }: { - filterName: FilterName; - searchString: string; + filterOptions: ChartsFilterProps['filterOptions']; }): { areDataStreamsSelectedOnMount: boolean; - isLoading: boolean; items: FilterItems; setItems: React.Dispatch>; hasActiveFilters: boolean; @@ -52,12 +50,8 @@ export const useChartsFilter = ({ setUrlMetricTypesFilter, setUrlDataStreamsFilter, } = useDataUsageMetricsUrlParams(); - const isMetricTypesFilter = filterName === 'metricTypes'; - const isDataStreamsFilter = filterName === 'dataStreams'; - const { data: dataStreams, isFetching } = useGetDataUsageDataStreams({ - searchString, - selectedDataStreams: selectedDataStreamsFromUrl, - }); + const isMetricTypesFilter = filterOptions.filterName === 'metricTypes'; + const isDataStreamsFilter = filterOptions.filterName === 'dataStreams'; // track the state of selected data streams via URL // when the page is loaded via selected data streams on URL @@ -80,24 +74,23 @@ export const useChartsFilter = ({ 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`, + 'data-test-subj': `${filterOptions.filterName}-filter-option`, + })) + : isDataStreamsFilter && !!filterOptions.options.length + ? filterOptions.options?.map((filterOption) => ({ + key: filterOption, + label: filterOption, + append: formatBytes(filterOptions.appendOptions?.[filterOption] ?? 0), + checked: selectedDataStreamsFromUrl + ? selectedDataStreamsFromUrl.includes(filterOption) + ? 'on' + : undefined + : 'on', + 'data-test-subj': `${filterOptions.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, @@ -110,7 +103,6 @@ export const useChartsFilter = ({ return { areDataStreamsSelectedOnMount, - isLoading: isDataStreamsFilter && isFetching, items, setItems, hasActiveFilters, diff --git a/x-pack/plugins/data_usage/public/app/hooks/use_charts_url_params.tsx b/x-pack/plugins/data_usage/public/app/hooks/use_charts_url_params.tsx index 0e03da5d9adbd..ed833393ad7eb 100644 --- a/x-pack/plugins/data_usage/public/app/hooks/use_charts_url_params.tsx +++ b/x-pack/plugins/data_usage/public/app/hooks/use_charts_url_params.tsx @@ -53,9 +53,7 @@ export const getDataUsageMetricsFiltersFromUrlParams = ( }, []) : []; - const urlDataStreams = urlParams.dataStreams - ? String(urlParams.dataStreams).split(',').sort() - : []; + const urlDataStreams = urlParams.dataStreams ? String(urlParams.dataStreams).split(',') : []; dataUsageMetricsFilters.metricTypes = urlMetricTypes.length ? urlMetricTypes : undefined; dataUsageMetricsFilters.dataStreams = urlDataStreams.length ? urlDataStreams : undefined; diff --git a/x-pack/plugins/data_usage/public/app/translations.tsx b/x-pack/plugins/data_usage/public/app/translations.tsx index 687cdcf499b0d..ee42d3b58906b 100644 --- a/x-pack/plugins/data_usage/public/app/translations.tsx +++ b/x-pack/plugins/data_usage/public/app/translations.tsx @@ -48,7 +48,4 @@ export const UX_LABELS = Object.freeze({ 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/hooks/use_get_data_streams.ts b/x-pack/plugins/data_usage/public/hooks/use_get_data_streams.ts index 50a35bb211346..35f53c49e2c28 100644 --- 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 @@ -14,6 +14,7 @@ import { useKibanaContextForPlugin } from '../utils/use_kibana'; type GetDataUsageDataStreamsResponse = Array<{ name: string; + storageSizeBytes: number; selected: boolean; }>; @@ -23,11 +24,11 @@ const PAGING_PARAMS = Object.freeze({ }); export const useGetDataUsageDataStreams = ({ - searchString, selectedDataStreams, - options = {}, + options = { + enabled: false, + }, }: { - searchString: string; selectedDataStreams?: string[]; options?: UseQueryOptions; }): UseQueryResult => { @@ -45,7 +46,7 @@ export const useGetDataUsageDataStreams = ({ DATA_USAGE_DATA_STREAMS_API_ROUTE, { version: '1', - query: {}, + // query: {}, } ); @@ -53,12 +54,14 @@ export const useGetDataUsageDataStreams = ({ selected: GetDataUsageDataStreamsResponse; rest: GetDataUsageDataStreamsResponse; }>( - (acc, list) => { + (acc, ds) => { const item = { - name: list.name, + name: ds.name, + storageSizeBytes: ds.storageSizeBytes, + selected: ds.selected, }; - if (selectedDataStreams?.includes(list.name)) { + if (selectedDataStreams?.includes(ds.name)) { acc.selected.push({ ...item, selected: true }); } else { acc.rest.push({ ...item, selected: false }); 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 e9da41f01db54..bbd0f5d8aa02f 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 @@ -31,8 +31,9 @@ export const useGetDataUsageMetrics = ( queryKey: ['get-data-usage-metrics', body], ...options, keepPreviousData: true, - queryFn: async () => { + queryFn: async ({ signal }) => { return http.post(DATA_USAGE_METRICS_API_ROUTE, { + signal, version: '1', body: JSON.stringify({ from: body.from, diff --git a/x-pack/plugins/data_usage/public/utils/format_bytes.ts b/x-pack/plugins/data_usage/public/utils/format_bytes.ts new file mode 100644 index 0000000000000..c5f98f3f9e0d9 --- /dev/null +++ b/x-pack/plugins/data_usage/public/utils/format_bytes.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 numeral from '@elastic/numeral'; + +export const formatBytes = (bytes: number) => { + return numeral(bytes).format('0.0 b'); +}; 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 d9f5d22b44dba..bc8c5e898c35e 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 @@ -45,7 +45,7 @@ export const getDataStreamsHandler = ( .sort((a, b) => b.size_in_bytes - a.size_in_bytes) .map((stat) => ({ name: stat.name, - storageSizeBytes: stat.size_in_bytes, + storageSizeBytes: stat.size_in_bytes ?? 0, })); return response.ok({ 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 0dd7c9d109c4a..93b31033fc4fb 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 @@ -35,6 +35,8 @@ export const getUsageMetricsHandler = ( logger.debug(`Retrieving usage metrics`); const { from, to, metricTypes, dataStreams: requestDsNames } = request.body; + // redundant check as we don't allow making requests via UI without data streams, + // but it's here to make sure the request body is validated before requesting metrics from auto-ops if (!requestDsNames?.length) { return errorHandler( logger, diff --git a/x-pack/plugins/data_usage/tsconfig.json b/x-pack/plugins/data_usage/tsconfig.json index 6d3818b88b9fe..78c501922f239 100644 --- a/x-pack/plugins/data_usage/tsconfig.json +++ b/x-pack/plugins/data_usage/tsconfig.json @@ -28,7 +28,6 @@ "@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 c1ac7228503de1ed4ddac4373f9a0bbf79c595c5 Mon Sep 17 00:00:00 2001 From: Julian Gernun <17549662+jcger@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:19:40 +0200 Subject: [PATCH 4/6] [Response Ops][Alerting] HTTP Version health API (#196690) ## Summary Closes https://github.com/elastic/kibana/issues/195181 Closes https://github.com/elastic/kibana/issues/195182 OAS descriptions and HTTP version for `GET /api/alerting/_health` --- .../routes/framework/apis/health/index.ts | 15 ++++++ .../framework/apis/health/schemas/latest.ts | 8 +++ .../framework/apis/health/schemas/v1.ts | 49 +++++++++++++++++++ .../framework/apis/health/types/latest.ts | 8 +++ .../routes/framework/apis/health/types/v1.ts | 13 +++++ .../apis/health}/health.test.ts | 16 +++--- .../{ => framework/apis/health}/health.ts | 41 +++++++--------- .../routes/framework/apis/health/index.ts | 8 +++ .../framework/apis/health/transforms/index.ts | 9 ++++ .../transform_health_response/latest.ts | 8 +++ .../transform_health_response/v1.ts | 21 ++++++++ .../plugins/alerting/server/routes/index.ts | 2 +- 12 files changed, 167 insertions(+), 31 deletions(-) create mode 100644 x-pack/plugins/alerting/common/routes/framework/apis/health/index.ts create mode 100644 x-pack/plugins/alerting/common/routes/framework/apis/health/schemas/latest.ts create mode 100644 x-pack/plugins/alerting/common/routes/framework/apis/health/schemas/v1.ts create mode 100644 x-pack/plugins/alerting/common/routes/framework/apis/health/types/latest.ts create mode 100644 x-pack/plugins/alerting/common/routes/framework/apis/health/types/v1.ts rename x-pack/plugins/alerting/server/routes/{ => framework/apis/health}/health.test.ts (95%) rename x-pack/plugins/alerting/server/routes/{ => framework/apis/health}/health.ts (72%) create mode 100644 x-pack/plugins/alerting/server/routes/framework/apis/health/index.ts create mode 100644 x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/index.ts create mode 100644 x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/transform_health_response/latest.ts create mode 100644 x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/transform_health_response/v1.ts diff --git a/x-pack/plugins/alerting/common/routes/framework/apis/health/index.ts b/x-pack/plugins/alerting/common/routes/framework/apis/health/index.ts new file mode 100644 index 0000000000000..1cde6fe4d90ce --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/framework/apis/health/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { healthFrameworkResponseSchema } from './schemas/latest'; +export type { HealthFrameworkResponse } from './types/latest'; + +export { + healthFrameworkResponseBodySchema as healthFrameworkResponseBodySchemaV1, + healthFrameworkResponseSchema as healthFrameworkResponseSchemaV1, +} from './schemas/v1'; +export type { HealthFrameworkResponseBody as HealthFrameworkResponseBodyV1 } from './types/v1'; diff --git a/x-pack/plugins/alerting/common/routes/framework/apis/health/schemas/latest.ts b/x-pack/plugins/alerting/common/routes/framework/apis/health/schemas/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/framework/apis/health/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/framework/apis/health/schemas/v1.ts b/x-pack/plugins/alerting/common/routes/framework/apis/health/schemas/v1.ts new file mode 100644 index 0000000000000..c97dc4d3cc72d --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/framework/apis/health/schemas/v1.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema } from '@kbn/config-schema'; + +const alertingFrameworkHealthSchemaObject = { + status: schema.oneOf([schema.literal('ok'), schema.literal('warn'), schema.literal('error')]), + timestamp: schema.string(), +}; + +export const healthFrameworkResponseBodySchema = schema.object({ + is_sufficiently_secure: schema.boolean({ + meta: { + description: 'If false, security is enabled but TLS is not.', + }, + }), + has_permanent_encryption_key: schema.boolean({ + meta: { + description: 'If false, the encryption key is not set', + }, + }), + alerting_framework_health: schema.object( + { + decryption_health: schema.object(alertingFrameworkHealthSchemaObject, { + meta: { description: 'The timestamp and status of the alert decryption.' }, + }), + execution_health: schema.object(alertingFrameworkHealthSchemaObject, { + meta: { description: 'The timestamp and status of the alert execution.' }, + }), + read_health: schema.object(alertingFrameworkHealthSchemaObject, { + meta: { description: 'The timestamp and status of the alert reading events.' }, + }), + }, + { + meta: { + description: + 'Three substates identify the health of the alerting framework: decryptionHealth, executionHealth, and readHealth.', + }, + } + ), +}); + +export const healthFrameworkResponseSchema = schema.object({ + body: healthFrameworkResponseBodySchema, +}); diff --git a/x-pack/plugins/alerting/common/routes/framework/apis/health/types/latest.ts b/x-pack/plugins/alerting/common/routes/framework/apis/health/types/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/framework/apis/health/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/framework/apis/health/types/v1.ts b/x-pack/plugins/alerting/common/routes/framework/apis/health/types/v1.ts new file mode 100644 index 0000000000000..188e3d0816a54 --- /dev/null +++ b/x-pack/plugins/alerting/common/routes/framework/apis/health/types/v1.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { TypeOf } from '@kbn/config-schema'; + +import { healthFrameworkResponseBodySchemaV1, healthFrameworkResponseSchemaV1 } from '..'; + +export type HealthFrameworkResponseBody = TypeOf; +export type HealthFrameworkResponse = TypeOf; diff --git a/x-pack/plugins/alerting/server/routes/health.test.ts b/x-pack/plugins/alerting/server/routes/framework/apis/health/health.test.ts similarity index 95% rename from x-pack/plugins/alerting/server/routes/health.test.ts rename to x-pack/plugins/alerting/server/routes/framework/apis/health/health.test.ts index 28117eaeeb55b..f726073ed0844 100644 --- a/x-pack/plugins/alerting/server/routes/health.test.ts +++ b/x-pack/plugins/alerting/server/routes/framework/apis/health/health.test.ts @@ -8,18 +8,18 @@ import { healthRoute } from './health'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { HealthStatus } from '@kbn/alerting-types'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from '../../../_mock_handler_arguments'; +import { verifyApiAccess } from '../../../../lib/license_api_access'; +import { licenseStateMock } from '../../../../lib/license_state.mock'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; -import { rulesClientMock } from '../rules_client.mock'; -import { alertsMock } from '../mocks'; -import { RecoveredActionGroup } from '../../common'; -import { RegistryAlertTypeWithAuth } from '../authorization'; +import { rulesClientMock } from '../../../../rules_client.mock'; +import { alertsMock } from '../../../../mocks'; +import { RecoveredActionGroup } from '../../../../../common'; +import { RegistryAlertTypeWithAuth } from '../../../../authorization'; const rulesClient = rulesClientMock.create(); -jest.mock('../lib/license_api_access', () => ({ +jest.mock('../../../../lib/license_api_access', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/health.ts b/x-pack/plugins/alerting/server/routes/framework/apis/health/health.ts similarity index 72% rename from x-pack/plugins/alerting/server/routes/health.ts rename to x-pack/plugins/alerting/server/routes/framework/apis/health/health.ts index 478f0afda594a..34ef56a51daaa 100644 --- a/x-pack/plugins/alerting/server/routes/health.ts +++ b/x-pack/plugins/alerting/server/routes/framework/apis/health/health.ts @@ -7,30 +7,16 @@ import { IRouter } from '@kbn/core/server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; -import { ILicenseState } from '../lib'; -import { RewriteResponseCase, verifyAccessAndContext } from './lib'; +import { healthFrameworkResponseSchemaV1 } from '../../../../../common/routes/framework/apis/health'; +import { ILicenseState } from '../../../../lib'; +import { verifyAccessAndContext } from '../../../lib'; import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH, AlertingFrameworkHealth, -} from '../types'; -import { getSecurityHealth } from '../lib/get_security_health'; - -const rewriteBodyRes: RewriteResponseCase = ({ - isSufficientlySecure, - hasPermanentEncryptionKey, - alertingFrameworkHealth, - ...rest -}) => ({ - ...rest, - is_sufficiently_secure: isSufficientlySecure, - has_permanent_encryption_key: hasPermanentEncryptionKey, - alerting_framework_health: { - decryption_health: alertingFrameworkHealth.decryptionHealth, - execution_health: alertingFrameworkHealth.executionHealth, - read_health: alertingFrameworkHealth.readHealth, - }, -}); +} from '../../../../types'; +import { getSecurityHealth } from '../../../../lib/get_security_health'; +import { transformHealthBodyResponse } from './transforms/transform_health_response/v1'; export const healthRoute = ( router: IRouter, @@ -45,7 +31,18 @@ export const healthRoute = ( summary: `Get the alerting framework health`, tags: ['oas-tag:alerting'], }, - validate: false, + validate: { + request: {}, + response: { + 200: { + body: () => healthFrameworkResponseSchemaV1, + description: 'Indicates a successful call.', + }, + 401: { + description: 'Authorization information is missing or invalid.', + }, + }, + }, }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { @@ -68,7 +65,7 @@ export const healthRoute = ( }; return res.ok({ - body: rewriteBodyRes(frameworkHealth), + body: transformHealthBodyResponse(frameworkHealth), }); } else { return res.forbidden({ diff --git a/x-pack/plugins/alerting/server/routes/framework/apis/health/index.ts b/x-pack/plugins/alerting/server/routes/framework/apis/health/index.ts new file mode 100644 index 0000000000000..a3b39bc8eb752 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/framework/apis/health/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 { healthRoute } from './health'; diff --git a/x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/index.ts b/x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/index.ts new file mode 100644 index 0000000000000..7e16df7405434 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/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 { transformHealthBodyResponse } from './transform_health_response/latest'; +export { transformHealthBodyResponse as transformHealthBodyResponseV1 } from './transform_health_response/v1'; diff --git a/x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/transform_health_response/latest.ts b/x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/transform_health_response/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/transform_health_response/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/server/routes/framework/apis/health/transforms/transform_health_response/v1.ts b/x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/transform_health_response/v1.ts new file mode 100644 index 0000000000000..76c4a46aee5d1 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/framework/apis/health/transforms/transform_health_response/v1.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AlertingFrameworkHealth } from '../../../../../../types'; +import type { HealthFrameworkResponseBodyV1 } from '../../../../../../../common/routes/framework/apis/health'; + +export const transformHealthBodyResponse = ( + frameworkHealth: AlertingFrameworkHealth +): HealthFrameworkResponseBodyV1 => ({ + is_sufficiently_secure: frameworkHealth.isSufficientlySecure, + has_permanent_encryption_key: frameworkHealth.hasPermanentEncryptionKey, + alerting_framework_health: { + decryption_health: frameworkHealth.alertingFrameworkHealth.decryptionHealth, + execution_health: frameworkHealth.alertingFrameworkHealth.executionHealth, + read_health: frameworkHealth.alertingFrameworkHealth.readHealth, + }, +}); diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index eee0382dd834c..97fdf8c90f8d6 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -28,7 +28,7 @@ import { getGlobalExecutionKPIRoute } from './get_global_execution_kpi'; import { getActionErrorLogRoute } from './get_action_error_log'; import { getRuleExecutionKPIRoute } from './get_rule_execution_kpi'; import { getRuleStateRoute } from './get_rule_state'; -import { healthRoute } from './health'; +import { healthRoute } from './framework/apis/health'; import { resolveRuleRoute } from './rule/apis/resolve'; import { ruleTypesRoute } from './rule/apis/list_types/rule_types'; import { muteAllRuleRoute } from './rule/apis/mute_all/mute_all_rule'; From 9aaffa9d9114eda8ddca90ae8173d5cbd38ffb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Gonz=C3=A1lez?= Date: Fri, 18 Oct 2024 15:30:42 +0200 Subject: [PATCH 5/6] Fixing CTA content for web crawler (#196853) ## Summary Small bug fix for this web crawler CTA button content: ![CleanShot 2024-10-18 at 13 31 42@2x](https://github.com/user-attachments/assets/edc5f341-30d5-4a90-96bd-3eda2836bbfe) That should say just _Crawl URL_ --- .../components/product_selector/ingestion_selector.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx index e8b6924eb15b4..7a36c6a973ec2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx @@ -89,8 +89,7 @@ export const IngestionSelector: React.FC = () => { : i18n.translate( 'xpack.enterpriseSearch.ingestSelector.method.crawler.description', { - defaultMessage: - 'Discover, extract, and index searchable content from websites and knowledge bases.', + defaultMessage: 'Crawl URL', } ) } From 37b57a3772e36a933920ebac5de77fd765b235b4 Mon Sep 17 00:00:00 2001 From: Elena Stoeva <59341489+ElenaStoeva@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:54:10 +0100 Subject: [PATCH 6/6] [Ingest Pipelines] Fix functional tests for GeoIP databases (#196544) Follow-up to https://github.com/elastic/kibana/pull/190830 ## Summary This PR fixes the functional tests for GeoIP databases. Before, the tests were failing because a value could not be selected in the Type field and the Name field in the add database modal (these fields use EuiSelect for `testsubjects.setValue` did not work). This is fixed by assigning a data-test-subj to every option in the dropdown and clicking on the field and then on the corresponding option. Flaky test runner: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/7165 --- .../apps/ingest_pipelines/manage_processors.ts | 11 +++++------ .../page_objects/ingest_pipelines_page.ts | 17 +++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/x-pack/test/functional/apps/ingest_pipelines/manage_processors.ts b/x-pack/test/functional/apps/ingest_pipelines/manage_processors.ts index a4951a2829fd0..5697f57c37038 100644 --- a/x-pack/test/functional/apps/ingest_pipelines/manage_processors.ts +++ b/x-pack/test/functional/apps/ingest_pipelines/manage_processors.ts @@ -12,10 +12,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'ingestPipelines', 'savedObjects']); const security = getService('security'); const maxMindDatabaseName = 'GeoIP2-Anonymous-IP'; - const ipInfoDatabaseName = 'ASN'; + const ipInfoDatabaseName = 'Free IP to ASN'; - // TODO: Fix flaky tests - describe.skip('Ingest Pipelines: Manage Processors', function () { + describe('Ingest Pipelines: Manage Processors', function () { this.tags('smoke'); before(async () => { await security.testUser.setRoles(['manage_processors_user']); @@ -36,8 +35,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('Create a MaxMind database', async () => { await pageObjects.ingestPipelines.openCreateDatabaseModal(); await pageObjects.ingestPipelines.fillAddDatabaseForm( - 'MaxMind', - 'GeoIP2 Anonymous IP', + 'maxmind', + maxMindDatabaseName, '123456' ); await pageObjects.ingestPipelines.clickAddDatabaseButton(); @@ -55,7 +54,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('Create an IPInfo database', async () => { await pageObjects.ingestPipelines.openCreateDatabaseModal(); - await pageObjects.ingestPipelines.fillAddDatabaseForm('IPInfo', ipInfoDatabaseName); + await pageObjects.ingestPipelines.fillAddDatabaseForm('ipinfo', 'asn'); await pageObjects.ingestPipelines.clickAddDatabaseButton(); // Wait for new row to gets displayed diff --git a/x-pack/test/functional/page_objects/ingest_pipelines_page.ts b/x-pack/test/functional/page_objects/ingest_pipelines_page.ts index b62d34b114f4b..efbc86128c224 100644 --- a/x-pack/test/functional/page_objects/ingest_pipelines_page.ts +++ b/x-pack/test/functional/page_objects/ingest_pipelines_page.ts @@ -7,7 +7,6 @@ import path from 'path'; import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services'; -import expect from '@kbn/expect'; import { FtrProviderContext } from '../ftr_provider_context'; export function IngestPipelinesPageProvider({ getService, getPageObjects }: FtrProviderContext) { @@ -132,21 +131,23 @@ export function IngestPipelinesPageProvider({ getService, getPageObjects }: FtrP }, async fillAddDatabaseForm(databaseType: string, databaseName: string, maxmind?: string) { - await testSubjects.setValue('databaseTypeSelect', databaseType); + await testSubjects.selectValue('databaseTypeSelect', databaseType); - // Wait for the rest of the fields to get displayed - await pageObjects.common.sleep(1000); - expect(await testSubjects.exists('databaseNameSelect')).to.be(true); + await retry.waitFor('Database name field to be displayed', async () => { + return await testSubjects.isDisplayed('databaseNameSelect'); + }); if (maxmind) { await testSubjects.setValue('maxmindField', maxmind); } - await testSubjects.setValue('databaseNameSelect', databaseName); + + await testSubjects.selectValue('databaseNameSelect', databaseName); }, async clickAddDatabaseButton() { - // Wait for button to get enabled - await pageObjects.common.sleep(1000); + await retry.waitFor('Add button to be enabled', async () => { + return await testSubjects.isEnabled('addGeoipDatabaseSubmit'); + }); await testSubjects.click('addGeoipDatabaseSubmit'); },