From 1a20fda7021fa49d054c19d4757592dda385f5de Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Tue, 10 Dec 2024 14:33:32 +0100 Subject: [PATCH] [Infra] Fix call to service api (#203451) fixes [203389](https://github.com/elastic/kibana/issues/203389) ## Summary Fix the call to `/api/infra/services` when using a relative date range ![service_api_relative_date_range](https://github.com/user-attachments/assets/772bba2c-07c8-4031-8d8a-61bdc7ab6d70) ### How to test - Navigate to host detail view, and change the data picker to use relative dates - Click on Submit --- .../asset_details/tabs/overview/services.tsx | 12 +++- .../infra/public/hooks/use_time_range.test.ts | 59 +++++++++++++++++++ .../infra/public/hooks/use_time_range.ts | 39 ++++++++++++ .../metrics/hosts/hooks/use_unified_search.ts | 28 +++------ 4 files changed, 114 insertions(+), 24 deletions(-) create mode 100644 x-pack/plugins/observability_solution/infra/public/hooks/use_time_range.test.ts create mode 100644 x-pack/plugins/observability_solution/infra/public/hooks/use_time_range.ts diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/services.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/services.tsx index 0e504413abbcf..01e1e4889922f 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/services.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/services.tsx @@ -11,6 +11,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { TimeRange } from '@kbn/es-query'; import { useLinkProps } from '@kbn/observability-shared-plugin/public'; import { decodeOrThrow } from '@kbn/io-ts-utils'; +import { useTimeRange } from '../../../../hooks/use_time_range'; import { ServicesAPIResponseRT } from '../../../../../common/http_api'; import { isPending, useFetcher } from '../../../../hooks/use_fetcher'; import { Section } from '../../components/section'; @@ -44,13 +45,18 @@ export const ServicesContent = ({ app: 'apm', pathname: '/onboarding', }); + + const parsedDateRange = useTimeRange({ + rangeFrom: dateRange.from, + rangeTo: dateRange.to, + }); + const params = useMemo( () => ({ filters: { [HOST_NAME_FIELD]: hostName }, - from: dateRange.from, - to: dateRange.to, + ...parsedDateRange, }), - [hostName, dateRange.from, dateRange.to] + [hostName, parsedDateRange] ); const query = useMemo(() => ({ ...params, filters: JSON.stringify(params.filters) }), [params]); diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_time_range.test.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_time_range.test.ts new file mode 100644 index 0000000000000..03227d4bbf65e --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_time_range.test.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { useTimeRange } from './use_time_range'; +import * as datemath from '../utils/datemath'; + +jest.mock('../utils/datemath'); + +describe('useTimeRange', () => { + const mockParseDateRange = datemath.parseDateRange as jest.Mock; + + beforeEach(() => { + Date.now = jest.fn(() => new Date(Date.UTC(2021, 0, 1, 12)).valueOf()); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('returns default timestamps when rangeFrom and rangeTo are not provided', () => { + const { result } = renderHook(() => useTimeRange({})); + + const now = Date.now(); + const expectedFrom = new Date(now - 15 * 60000).toISOString(); + const expectedTo = new Date(now).toISOString(); + + expect(result.current.from).toBe(expectedFrom); + expect(result.current.to).toBe(expectedTo); + }); + + it('returns parsed date range when rangeFrom and rangeTo are provided', () => { + const mockFrom = '2021-01-01T00:00:00.000Z'; + const mockTo = '2021-01-01T01:00:00.000Z'; + mockParseDateRange.mockReturnValue({ from: mockFrom, to: mockTo }); + + const { result } = renderHook(() => useTimeRange({ rangeFrom: 'now-15m', rangeTo: 'now' })); + + expect(result.current.from).toBe(mockFrom); + expect(result.current.to).toBe(mockTo); + }); + + it('returns default timestamps when parseDateRange returns undefined values', () => { + mockParseDateRange.mockReturnValue({ from: undefined, to: undefined }); + + const { result } = renderHook(() => useTimeRange({ rangeFrom: 'now-15m', rangeTo: 'now' })); + + const now = Date.now(); + const expectedFrom = new Date(now - 15 * 60000).toISOString(); + const expectedTo = new Date(now).toISOString(); + + expect(result.current.from).toBe(expectedFrom); + expect(result.current.to).toBe(expectedTo); + }); +}); diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_time_range.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_time_range.ts new file mode 100644 index 0000000000000..5fd833a180f3d --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_time_range.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { parseDateRange } from '../utils/datemath'; + +const DEFAULT_FROM_IN_MILLISECONDS = 15 * 60000; + +const getDefaultTimestamps = () => { + const now = Date.now(); + + return { + from: new Date(now - DEFAULT_FROM_IN_MILLISECONDS).toISOString(), + to: new Date(now).toISOString(), + }; +}; + +export const useTimeRange = ({ rangeFrom, rangeTo }: { rangeFrom?: string; rangeTo?: string }) => { + const parsedDateRange = useMemo(() => { + const defaults = getDefaultTimestamps(); + + if (!rangeFrom || !rangeTo) { + return defaults; + } + + const { from = defaults.from, to = defaults.to } = parseDateRange({ + from: rangeFrom, + to: rangeTo, + }); + + return { from, to }; + }, [rangeFrom, rangeTo]); + + return parsedDateRange; +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts index 8a7302da1a223..6feefd399a829 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts @@ -5,14 +5,14 @@ * 2.0. */ import createContainer from 'constate'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { buildEsQuery, Filter, fromKueryExpression, TimeRange, type Query } from '@kbn/es-query'; import { Subscription, map, tap } from 'rxjs'; import deepEqual from 'fast-deep-equal'; import useEffectOnce from 'react-use/lib/useEffectOnce'; import { useKibanaQuerySettings } from '@kbn/observability-shared-plugin/public'; +import { useTimeRange } from '../../../../hooks/use_time_range'; import { useSearchSessionContext } from '../../../../hooks/use_search_session'; -import { parseDateRange } from '../../../../utils/datemath'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { telemetryTimeRangeFormatter } from '../../../../../common/formatters/telemetry_time_range'; import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; @@ -38,17 +38,6 @@ const buildQuerySubmittedPayload = ( }; }; -const DEFAULT_FROM_IN_MILLISECONDS = 15 * 60000; - -const getDefaultTimestamps = () => { - const now = Date.now(); - - return { - from: new Date(now - DEFAULT_FROM_IN_MILLISECONDS).toISOString(), - to: new Date(now).toISOString(), - }; -}; - export const useUnifiedSearch = () => { const [error, setError] = useState(null); const [searchCriteria, setSearch] = useHostsUrlState(); @@ -57,6 +46,11 @@ export const useUnifiedSearch = () => { const { services } = useKibanaContextForPlugin(); const kibanaQuerySettings = useKibanaQuerySettings(); + const parsedDateRange = useTimeRange({ + rangeFrom: searchCriteria.dateRange.from, + rangeTo: searchCriteria.dateRange.to, + }); + const { data: { query: { filterManager: filterManagerService, queryString: queryStringService }, @@ -120,14 +114,6 @@ export const useUnifiedSearch = () => { [onDateRangeChange, updateSearchSessionId] ); - const parsedDateRange = useMemo(() => { - const defaults = getDefaultTimestamps(); - - const { from = defaults.from, to = defaults.to } = parseDateRange(searchCriteria.dateRange); - - return { from, to }; - }, [searchCriteria.dateRange]); - const getDateRangeAsTimestamp = useCallback(() => { const from = new Date(parsedDateRange.from).getTime(); const to = new Date(parsedDateRange.to).getTime();