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] [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 1ba3f0fe3f4546..7554716c59492d 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 cc443c78562ee4..48b6566df9e660 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 466bc6debae771..83d417565f012c 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 72608f4a62c753..6f3b07e37dc83a 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 afa4c2fe729171..00000000000000 --- 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 330c9a633396dc..5cff100d9752ef 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 0e03da5d9adbd3..ed833393ad7eba 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 687cdcf499b0d3..ee42d3b58906bd 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 50a35bb2113465..35f53c49e2c284 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 e9da41f01db54d..bbd0f5d8aa02fb 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 00000000000000..c5f98f3f9e0d9e --- /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 d9f5d22b44dba1..bc8c5e898c35ea 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 0dd7c9d109c4aa..93b31033fc4fb2 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 6d3818b88b9fe0..78c501922f239f 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",