diff --git a/x-pack/plugins/data_usage/common/index.ts b/x-pack/plugins/data_usage/common/index.ts index eb0787f53f344..8b952b13d4cc7 100644 --- a/x-pack/plugins/data_usage/common/index.ts +++ b/x-pack/plugins/data_usage/common/index.ts @@ -12,6 +12,8 @@ export const PLUGIN_NAME = i18n.translate('xpack.dataUsage.name', { defaultMessage: 'Data Usage', }); +export const DEFAULT_SELECTED_OPTIONS = 50 as const; + export const DATA_USAGE_API_ROUTE_PREFIX = '/api/data_usage/'; export const DATA_USAGE_METRICS_API_ROUTE = `/internal${DATA_USAGE_API_ROUTE_PREFIX}metrics`; export const DATA_USAGE_DATA_STREAMS_API_ROUTE = `/internal${DATA_USAGE_API_ROUTE_PREFIX}data_streams`; diff --git a/x-pack/plugins/data_usage/public/app/components/data_usage_metrics.test.tsx b/x-pack/plugins/data_usage/public/app/components/data_usage_metrics.test.tsx index 951a8eca08802..72814a0f09c5f 100644 --- a/x-pack/plugins/data_usage/public/app/components/data_usage_metrics.test.tsx +++ b/x-pack/plugins/data_usage/public/app/components/data_usage_metrics.test.tsx @@ -4,9 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import React from 'react'; - import { render, waitFor } from '@testing-library/react'; import userEvent, { type UserEvent } from '@testing-library/user-event'; import { DataUsageMetrics } from './data_usage_metrics'; @@ -147,6 +145,13 @@ const getBaseMockedDataUsageMetrics = () => ({ refetch: jest.fn(), }); +const generateDataStreams = (count: number) => { + return Array.from({ length: count }, (_, i) => ({ + name: `.ds-${i}`, + storageSizeBytes: 1024 ** 2 * (22 / 7), + })); +}; + describe('DataUsageMetrics', () => { let user: UserEvent; const testId = 'test'; @@ -174,8 +179,9 @@ describe('DataUsageMetrics', () => { it('should show date filter', () => { const { getByTestId } = render(); - expect(getByTestId(`${testIdFilter}-date-range`)).toBeTruthy(); - expect(getByTestId(`${testIdFilter}-date-range`).textContent).toContain('Last 24 hours'); + const dateFilter = getByTestId(`${testIdFilter}-date-range`); + expect(dateFilter).toBeTruthy(); + expect(dateFilter.textContent).toContain('to'); expect(getByTestId(`${testIdFilter}-super-refresh-button`)).toBeTruthy(); }); @@ -196,28 +202,7 @@ describe('DataUsageMetrics', () => { it('should show selected data streams on the filter', () => { mockUseGetDataUsageDataStreams.mockReturnValue({ error: undefined, - data: [ - { - name: '.ds-1', - storageSizeBytes: 10000, - }, - { - name: '.ds-2', - storageSizeBytes: 20000, - }, - { - name: '.ds-3', - storageSizeBytes: 10300, - }, - { - name: '.ds-4', - storageSizeBytes: 23000, - }, - { - name: '.ds-5', - storageSizeBytes: 23200, - }, - ], + data: generateDataStreams(5), isFetching: false, }); const { getByTestId } = render(); @@ -226,46 +211,35 @@ describe('DataUsageMetrics', () => { ); }); + it('should show at most 50 selected data streams on the filter', async () => { + mockUseGetDataUsageDataStreams.mockReturnValue({ + error: undefined, + data: generateDataStreams(100), + isFetching: false, + }); + const { getByTestId } = render(); + const toggleFilterButton = getByTestId(`${testIdFilter}-dataStreams-popoverButton`); + + expect(toggleFilterButton).toHaveTextContent('Data streams50'); + }); + it('should allow de-selecting all but one data stream option', async () => { mockUseGetDataUsageDataStreams.mockReturnValue({ error: undefined, - data: [ - { - name: '.ds-1', - storageSizeBytes: 10000, - }, - { - name: '.ds-2', - storageSizeBytes: 20000, - }, - { - name: '.ds-3', - storageSizeBytes: 10300, - }, - { - name: '.ds-4', - storageSizeBytes: 23000, - }, - { - name: '.ds-5', - storageSizeBytes: 23200, - }, - ], + data: generateDataStreams(5), isFetching: false, }); const { getByTestId, getAllByTestId } = render(); - expect(getByTestId(`${testIdFilter}-dataStreams-popoverButton`)).toHaveTextContent( - 'Data streams5' - ); - await user.click(getByTestId(`${testIdFilter}-dataStreams-popoverButton`)); + const toggleFilterButton = getByTestId(`${testIdFilter}-dataStreams-popoverButton`); + + expect(toggleFilterButton).toHaveTextContent('Data streams5'); + await user.click(toggleFilterButton); const allFilterOptions = getAllByTestId('dataStreams-filter-option'); for (let i = 0; i < allFilterOptions.length - 1; i++) { await user.click(allFilterOptions[i]); } - expect(getByTestId(`${testIdFilter}-dataStreams-popoverButton`)).toHaveTextContent( - 'Data streams1' - ); + expect(toggleFilterButton).toHaveTextContent('Data streams1'); }); it('should not call usage metrics API if no data streams', async () => { 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 59354a1746346..8bedde117785a 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 @@ -72,7 +72,9 @@ export const DataUsageMetrics = memo( setUrlMetricTypesFilter(metricsFilters.metricTypes.join(',')); } if (!dataStreamsFromUrl && dataStreams) { - setUrlDataStreamsFilter(dataStreams.map((ds) => ds.name).join(',')); + const hasMoreThan50 = dataStreams.length > 50; + const _dataStreams = hasMoreThan50 ? dataStreams.slice(0, 50) : dataStreams; + setUrlDataStreamsFilter(_dataStreams.map((ds) => ds.name).join(',')); } if (!startDateFromUrl || !endDateFromUrl) { setUrlDateRangeFilter({ startDate: metricsFilters.from, endDate: metricsFilters.to }); diff --git a/x-pack/plugins/data_usage/public/app/components/filters/date_picker.tsx b/x-pack/plugins/data_usage/public/app/components/filters/date_picker.tsx index 044a036eea61f..62c6cc542a523 100644 --- a/x-pack/plugins/data_usage/public/app/components/filters/date_picker.tsx +++ b/x-pack/plugins/data_usage/public/app/components/filters/date_picker.tsx @@ -15,6 +15,7 @@ import type { OnRefreshChangeProps, } from '@elastic/eui/src/components/date_picker/types'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; +import moment from 'moment'; import { useTestIdGenerator } from '../../../hooks/use_test_id_generator'; export interface DateRangePickerValues { @@ -66,7 +67,7 @@ export const UsageMetricsDateRangePicker = memo ); 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 5cff100d9752e..d2c5dc554ff2d 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,6 +11,7 @@ import { METRIC_TYPE_API_VALUES_TO_UI_OPTIONS_MAP, METRIC_TYPE_VALUES, } from '../../../common/rest_types'; +import { DEFAULT_SELECTED_OPTIONS } from '../../../common'; import { FILTER_NAMES } from '../translations'; import { useDataUsageMetricsUrlParams } from './use_charts_url_params'; import { formatBytes } from '../../utils/format_bytes'; @@ -77,7 +78,7 @@ export const useChartsFilter = ({ 'data-test-subj': `${filterOptions.filterName}-filter-option`, })) : isDataStreamsFilter && !!filterOptions.options.length - ? filterOptions.options?.map((filterOption) => ({ + ? filterOptions.options?.map((filterOption, i) => ({ key: filterOption, label: filterOption, append: formatBytes(filterOptions.appendOptions?.[filterOption] ?? 0), @@ -85,7 +86,9 @@ export const useChartsFilter = ({ ? selectedDataStreamsFromUrl.includes(filterOption) ? 'on' : undefined - : 'on', + : i < DEFAULT_SELECTED_OPTIONS + ? 'on' + : undefined, 'data-test-subj': `${filterOptions.filterName}-filter-option`, })) : [] diff --git a/x-pack/plugins/data_usage/public/app/hooks/use_charts_url_params.test.tsx b/x-pack/plugins/data_usage/public/app/hooks/use_charts_url_params.test.tsx index 93a4b434931a3..2b009f05f3bb1 100644 --- a/x-pack/plugins/data_usage/public/app/hooks/use_charts_url_params.test.tsx +++ b/x-pack/plugins/data_usage/public/app/hooks/use_charts_url_params.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import moment from 'moment'; import { METRIC_TYPE_VALUES, MetricTypes } from '../../../common/rest_types'; import { getDataUsageMetricsFiltersFromUrlParams } from './use_charts_url_params'; @@ -56,12 +57,12 @@ describe('#getDataUsageMetricsFiltersFromUrlParams', () => { it('should use given relative startDate and endDate values URL params', () => { expect( getDataUsageMetricsFiltersFromUrlParams({ - startDate: 'now-24h/h', - endDate: 'now', + startDate: moment().subtract(24, 'hours').toISOString(), + endDate: moment().toISOString(), }) ).toEqual({ - endDate: 'now', - startDate: 'now-24h/h', + endDate: moment().toISOString(), + startDate: moment().subtract(24, 'hours').toISOString(), }); }); 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 cc4bfd2376da1..1b4b7e38e3554 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 @@ -5,6 +5,7 @@ * 2.0. */ +import moment from 'moment'; import { useCallback, useState } from 'react'; import type { DurationRange, @@ -18,8 +19,8 @@ export const DEFAULT_DATE_RANGE_OPTIONS = Object.freeze({ enabled: false, duration: 10000, }, - startDate: 'now-24h/h', - endDate: 'now', + startDate: moment().subtract(24, 'hours').startOf('day').toISOString(), + endDate: moment().toISOString(), recentlyUsedDateRanges: [], }); 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 acb41e45f4eb6..d43c3fff139fb 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 @@ -8,7 +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 { DATA_USAGE_DATA_STREAMS_API_ROUTE } from '../../common'; +import { DATA_USAGE_DATA_STREAMS_API_ROUTE, DEFAULT_SELECTED_OPTIONS } from '../../common'; import { useKibanaContextForPlugin } from '../utils/use_kibana'; type GetDataUsageDataStreamsResponse = Array<{ @@ -17,11 +17,6 @@ type GetDataUsageDataStreamsResponse = Array<{ selected: boolean; }>; -const PAGING_PARAMS = Object.freeze({ - default: 50, - all: 10000, -}); - export const useGetDataUsageDataStreams = ({ selectedDataStreams, options = { @@ -51,14 +46,14 @@ export const useGetDataUsageDataStreams = ({ selected: GetDataUsageDataStreamsResponse; rest: GetDataUsageDataStreamsResponse; }>( - (acc, ds) => { + (acc, ds, i) => { const item = { name: ds.name, storageSizeBytes: ds.storageSizeBytes, selected: ds.selected, }; - if (selectedDataStreams?.includes(ds.name)) { + if (selectedDataStreams?.includes(ds.name) && i < DEFAULT_SELECTED_OPTIONS) { acc.selected.push({ ...item, selected: true }); } else { acc.rest.push({ ...item, selected: false }); @@ -69,20 +64,10 @@ export const useGetDataUsageDataStreams = ({ { 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.test.tsx b/x-pack/plugins/data_usage/public/hooks/use_get_usage_metrics.test.tsx index efc3d2a9f4640..677bd4bdfcef1 100644 --- a/x-pack/plugins/data_usage/public/hooks/use_get_usage_metrics.test.tsx +++ b/x-pack/plugins/data_usage/public/hooks/use_get_usage_metrics.test.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import moment from 'moment'; import React, { ReactNode } from 'react'; import { QueryClient, QueryClientProvider, useQuery as _useQuery } from '@tanstack/react-query'; import { renderHook } from '@testing-library/react-hooks'; @@ -41,8 +42,8 @@ jest.mock('../utils/use_kibana', () => { }); const defaultUsageMetricsRequestBody = { - from: 'now-15m', - to: 'now', + from: moment().subtract(15, 'minutes').toISOString(), + to: moment().toISOString(), metricTypes: ['ingest_rate'], dataStreams: ['ds-1'], }; diff --git a/x-pack/plugins/data_usage/server/routes/internal/usage_metrics.test.ts b/x-pack/plugins/data_usage/server/routes/internal/usage_metrics.test.ts index 2c236e58a5af1..d6337bbcc8dcd 100644 --- a/x-pack/plugins/data_usage/server/routes/internal/usage_metrics.test.ts +++ b/x-pack/plugins/data_usage/server/routes/internal/usage_metrics.test.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import moment from 'moment'; import type { MockedKeys } from '@kbn/utility-types-jest'; import type { CoreSetup } from '@kbn/core/server'; import { registerUsageMetricsRoute } from './usage_metrics'; @@ -56,8 +56,8 @@ describe('registerUsageMetricsRoute', () => { const mockRequest = httpServerMock.createKibanaRequest({ body: { - from: 'now-15m', - to: 'now', + from: moment().subtract(15, 'minutes').toISOString(), + to: moment().toISOString(), metricTypes: ['ingest_rate'], dataStreams: [], }, @@ -123,8 +123,8 @@ describe('registerUsageMetricsRoute', () => { const mockRequest = httpServerMock.createKibanaRequest({ body: { - from: 'now-15m', - to: 'now', + from: moment().subtract(15, 'minutes').toISOString(), + to: moment().toISOString(), metricTypes: ['ingest_rate', 'storage_retained'], dataStreams: ['.ds-1', '.ds-2'], }, @@ -191,8 +191,8 @@ describe('registerUsageMetricsRoute', () => { const mockRequest = httpServerMock.createKibanaRequest({ body: { - from: 'now-15m', - to: 'now', + from: moment().subtract(15, 'minutes').toISOString(), + to: moment().toISOString(), metricTypes: ['ingest_rate'], dataStreams: ['.ds-1', '.ds-2'], },