From b4084b6d62fcb639efb826a038c884d3cc3bfeca Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Mon, 13 May 2024 18:05:33 +0200 Subject: [PATCH] [Infra] Register kibana locator to link to infras asset details and inventory uis (#181749) Relates to #176667 PR 1 of 3 ## Summary This PR replaces the asset details `link-to` component with the asset details locator and the usage link-to inside infra with the asset details locator The other locators and other plugins changes will follow in separate PRs ## Testing: Check redirects to asset details (with different assets eg host, pod, etc., and different links - open as page, hosts table, inventory flyout, etc.) On the button/link hover the link path should look similar to `/app/r?l=ASSET_DETAILS_LOCATOR&v=8.15.0&lz={Long String}`: - Metrics Explorer image - Hosts & Inventory https://github.com/elastic/kibana/assets/14139027/65de04c0-d6da-457b-a24c-40e26c3d1b3d --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../src/get_router_link_props/index.ts | 9 ++- .../links/link_to_node_details.tsx | 21 +++--- .../redirect_to_host_detail_via_ip.tsx | 53 ++++++++----- .../pages/link_to/redirect_to_node_detail.tsx | 75 ++++++++++++------- .../use_node_details_redirect.test.tsx | 69 ++++++++--------- .../link_to/use_node_details_redirect.ts | 65 +++++++++++----- .../hosts/components/table/entry_title.tsx | 23 +++--- .../components/waffle/node_context_menu.tsx | 22 +++--- .../components/chart_context_menu.test.tsx | 5 +- .../components/chart_context_menu.tsx | 23 +++--- .../infra/tsconfig.json | 3 +- .../observability_shared/public/index.ts | 5 +- .../locators/infra/asset_details_locator.ts | 7 +- 13 files changed, 219 insertions(+), 161 deletions(-) diff --git a/packages/kbn-router-utils/src/get_router_link_props/index.ts b/packages/kbn-router-utils/src/get_router_link_props/index.ts index a0b7f53afc440..63608d54ea696 100644 --- a/packages/kbn-router-utils/src/get_router_link_props/index.ts +++ b/packages/kbn-router-utils/src/get_router_link_props/index.ts @@ -8,7 +8,7 @@ export interface RouterLinkProps { href: string | undefined; - onClick: (event: React.MouseEvent) => void; + onClick: (event: React.MouseEvent) => void; } interface GetRouterLinkPropsDeps { @@ -16,10 +16,11 @@ interface GetRouterLinkPropsDeps { onClick(): void; } -const isModifiedEvent = (event: React.MouseEvent) => +const isModifiedEvent = (event: React.MouseEvent) => !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey); -const isLeftClickEvent = (event: React.MouseEvent) => event.button === 0; +const isLeftClickEvent = (event: React.MouseEvent) => + event.button === 0; /** * @@ -34,7 +35,7 @@ const isLeftClickEvent = (event: React.MouseEvent) => event.b * manage behaviours such as leftClickEvent and event with modifiers (Ctrl, Shift, etc) */ export const getRouterLinkProps = ({ href, onClick }: GetRouterLinkPropsDeps): RouterLinkProps => { - const guardedClickHandler = (event: React.MouseEvent) => { + const guardedClickHandler = (event: React.MouseEvent) => { if (event.defaultPrevented) { return; } diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_node_details.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_node_details.tsx index f4b3810479766..bd99c04de016e 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_node_details.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_node_details.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButtonEmpty } from '@elastic/eui'; -import { useLinkProps } from '@kbn/observability-shared-plugin/public'; import { parse } from '@kbn/datemath'; import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { useNodeDetailsRedirect } from '../../../pages/link_to'; @@ -27,17 +26,15 @@ export const LinkToNodeDetails = ({ assetId, assetName, assetType }: LinkToNodeD // don't propagate the autoRefresh to the details page const { dateRange, autoRefresh: _, ...assetDetails } = state ?? {}; - const nodeDetailMenuItemLinkProps = useLinkProps({ - ...getNodeDetailUrl({ - assetType, - assetId, - search: { - ...assetDetails, - name: assetName, - from: parse(dateRange?.from ?? '')?.valueOf(), - to: parse(dateRange?.to ?? '')?.valueOf(), - }, - }), + const nodeDetailMenuItemLinkProps = getNodeDetailUrl({ + assetType, + assetId, + search: { + ...assetDetails, + name: assetName, + from: parse(dateRange?.from ?? '')?.valueOf(), + to: parse(dateRange?.to ?? '')?.valueOf(), + }, }); return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx index 5f892c11c9f7a..ad7b3e6e509fd 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx @@ -5,16 +5,18 @@ * 2.0. */ -import React from 'react'; -import { Redirect, RouteComponentProps } from 'react-router-dom'; +import React, { useEffect } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; -import { replaceMetricTimeInQueryString } from '../metrics/metric_detail/hooks/use_metrics_time'; +import type { SerializableRecord } from '@kbn/utility-types'; +import { ASSET_DETAILS_LOCATOR_ID } from '@kbn/observability-shared-plugin/public'; import { useHostIpToName } from './use_host_ip_to_name'; -import { getFromFromLocation, getToFromLocation } from './query_params'; import { LoadingPage } from '../../components/loading_page'; import { Error } from '../error'; import { useSourceContext } from '../../containers/metrics_source'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; +import { getSearchParams } from './redirect_to_node_detail'; type RedirectToHostDetailType = RouteComponentProps<{ hostIp: string; @@ -27,12 +29,30 @@ export const RedirectToHostDetailViaIP = ({ location, }: RedirectToHostDetailType) => { const { source } = useSourceContext(); + const { + services: { share }, + } = useKibanaContextForPlugin(); + const baseLocator = share.url.locators.get(ASSET_DETAILS_LOCATOR_ID); const { error, name } = useHostIpToName( hostIp, (source && source.configuration && source.configuration.metricAlias) || null ); + useEffect(() => { + if (name) { + const queryParams = new URLSearchParams(location.search); + const search = getSearchParams('host', queryParams); + + baseLocator?.navigate({ + ...search, + assetType: 'host', + assetId: name, + state: location.state as SerializableRecord, + }); + } + }, [baseLocator, location.search, location.state, name]); + if (error) { return ( ; + if (!name) { + return ( + + ); } - return ( - - ); + return null; }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_node_detail.tsx b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_node_detail.tsx index f00691031b80d..137cb04a90034 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_node_detail.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_node_detail.tsx @@ -5,14 +5,15 @@ * 2.0. */ -import React from 'react'; -import { Redirect, useLocation, useRouteMatch } from 'react-router-dom'; +import { useEffect } from 'react'; +import { useLocation, useRouteMatch } from 'react-router-dom'; import rison from '@kbn/rison'; -import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; -import { replaceStateKeyInQueryString } from '../../../common/url_state_storage_service'; -import { replaceMetricTimeInQueryString } from '../metrics/metric_detail/hooks/use_metrics_time'; +import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; +import { ASSET_DETAILS_LOCATOR_ID } from '@kbn/observability-shared-plugin/public'; +import type { SerializableRecord } from '@kbn/utility-types'; import { AssetDetailsUrlState } from '../../components/asset_details/types'; import { ASSET_DETAILS_URL_STATE_KEY } from '../../components/asset_details/constants'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; export const REDIRECT_NODE_DETAILS_FROM_KEY = 'from'; export const REDIRECT_NODE_DETAILS_TO_KEY = 'to'; @@ -23,43 +24,61 @@ const getHostDetailSearch = (queryParams: URLSearchParams) => { const to = queryParams.get(REDIRECT_NODE_DETAILS_TO_KEY); const assetDetailsParam = queryParams.get(REDIRECT_ASSET_DETAILS_KEY); - return replaceStateKeyInQueryString(ASSET_DETAILS_URL_STATE_KEY, { - ...(assetDetailsParam ? (rison.decode(assetDetailsParam) as AssetDetailsUrlState) : undefined), - dateRange: { - from: from ? new Date(parseFloat(from)).toISOString() : undefined, - to: to ? new Date(parseFloat(to)).toISOString() : undefined, + return { + [ASSET_DETAILS_URL_STATE_KEY]: { + ...(assetDetailsParam + ? (rison.decode(assetDetailsParam) as AssetDetailsUrlState) + : undefined), + dateRange: { + from: from ? new Date(parseFloat(from)).toISOString() : undefined, + to: to ? new Date(parseFloat(to)).toISOString() : undefined, + }, }, - } as AssetDetailsUrlState)(''); + } as AssetDetailsUrlState; }; const getNodeDetailSearch = (queryParams: URLSearchParams) => { const from = queryParams.get(REDIRECT_NODE_DETAILS_FROM_KEY); const to = queryParams.get(REDIRECT_NODE_DETAILS_TO_KEY); - return replaceMetricTimeInQueryString( - from ? parseFloat(from) : NaN, - to ? parseFloat(to) : NaN - )(''); + return { + _a: { + time: + from && to + ? { + from: new Date(parseFloat(from)).toISOString(), + interval: '>=1m', + to: new Date(parseFloat(to)).toISOString(), + } + : undefined, + }, + }; }; +export const getSearchParams = (nodeType: InventoryItemType, queryParams: URLSearchParams) => + nodeType === 'host' ? getHostDetailSearch(queryParams) : getNodeDetailSearch(queryParams); + export const RedirectToNodeDetail = () => { const { params: { nodeType, nodeId }, } = useRouteMatch<{ nodeType: InventoryItemType; nodeId: string }>(); - + const { + services: { share }, + } = useKibanaContextForPlugin(); const location = useLocation(); - const queryParams = new URLSearchParams(location.search); + const baseLocator = share.url.locators.get(ASSET_DETAILS_LOCATOR_ID); + + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const search = getSearchParams(nodeType, queryParams); - const search = - nodeType === 'host' ? getHostDetailSearch(queryParams) : getNodeDetailSearch(queryParams); + baseLocator?.navigate({ + ...search, + assetType: nodeType, + assetId: nodeId, + state: location.state as SerializableRecord, + }); + }, [baseLocator, location.search, location.state, nodeId, nodeType]); - return ( - - ); + return null; }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/link_to/use_node_details_redirect.test.tsx b/x-pack/plugins/observability_solution/infra/public/pages/link_to/use_node_details_redirect.test.tsx index 15d8309598945..31589738ad310 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/link_to/use_node_details_redirect.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/link_to/use_node_details_redirect.test.tsx @@ -10,8 +10,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useNodeDetailsRedirect } from './use_node_details_redirect'; import { coreMock } from '@kbn/core/public/mocks'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; - -const coreStartMock = coreMock.createStart(); +import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -21,8 +20,20 @@ jest.mock('react-router-dom', () => ({ })), })); +const MOCK_HREF = '/app/r?l=ASSET_DETAILS_LOCATOR&v=8.15.0&lz=MoCkLoCaToRvAlUe'; +const coreStartMock = coreMock.createStart(); +const shareMock = sharePluginMock.createSetupContract(); + +// @ts-expect-error This object is missing some properties that we're not using in the UI +shareMock.url.locators.get = (id: string) => ({ + getRedirectUrl: (params: Record): string | undefined => MOCK_HREF, + navigate: () => {}, +}); + const wrapper = ({ children }: { children: React.ReactNode }): JSX.Element => ( - {children} + + {children} + ); describe('useNodeDetailsRedirect', () => { @@ -32,21 +43,18 @@ describe('useNodeDetailsRedirect', () => { const fromDateStrig = '2019-01-01T11:00:00Z'; const toDateStrig = '2019-01-01T12:00:00Z'; - expect( - result.current.getNodeDetailUrl({ - assetType: 'pod', - assetId: 'example-01', - search: { - from: new Date(fromDateStrig).getTime(), - to: new Date(toDateStrig).getTime(), - }, - }) - ).toStrictEqual({ - app: 'metrics', - pathname: 'link-to/pod-detail/example-01', - search: { from: '1546340400000', to: '1546344000000' }, - state: {}, + const getLinkProps = result.current.getNodeDetailUrl({ + assetType: 'pod', + assetId: 'example-01', + search: { + from: new Date(fromDateStrig).getTime(), + to: new Date(toDateStrig).getTime(), + }, }); + + expect(getLinkProps).toHaveProperty('href'); + expect(getLinkProps.href).toEqual(MOCK_HREF); + expect(getLinkProps).toHaveProperty('onClick'); }); it('should return the LinkProperties for assetType host', () => { @@ -55,25 +63,18 @@ describe('useNodeDetailsRedirect', () => { const fromDateStrig = '2019-01-01T11:00:00Z'; const toDateStrig = '2019-01-01T12:00:00Z'; - expect( - result.current.getNodeDetailUrl({ - assetType: 'host', - assetId: 'example-01', - search: { - from: new Date(fromDateStrig).getTime(), - to: new Date(toDateStrig).getTime(), - name: 'example-01', - }, - }) - ).toStrictEqual({ - app: 'metrics', - pathname: 'link-to/host-detail/example-01', + const getLinkProps = result.current.getNodeDetailUrl({ + assetType: 'host', + assetId: 'example-01', search: { - from: '1546340400000', - to: '1546344000000', - assetDetails: '(name:example-01)', + from: new Date(fromDateStrig).getTime(), + to: new Date(toDateStrig).getTime(), + name: 'example-01', }, - state: {}, }); + + expect(getLinkProps).toHaveProperty('href'); + expect(getLinkProps.href).toEqual(MOCK_HREF); + expect(getLinkProps).toHaveProperty('onClick'); }); }); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/link_to/use_node_details_redirect.ts b/x-pack/plugins/observability_solution/infra/public/pages/link_to/use_node_details_redirect.ts index adcd5e9f2f2e5..aca6a7f4ae9f4 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/link_to/use_node_details_redirect.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/link_to/use_node_details_redirect.ts @@ -7,16 +7,19 @@ import { useCallback } from 'react'; import { useLocation } from 'react-router-dom'; -import type { LinkDescriptor } from '@kbn/observability-shared-plugin/public'; import useObservable from 'react-use/lib/useObservable'; -import rison from '@kbn/rison'; import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; +import { getRouterLinkProps } from '@kbn/router-utils'; +import { + type AssetDetailsLocatorParams, + ASSET_DETAILS_LOCATOR_ID, +} from '@kbn/observability-shared-plugin/public'; +import { RouterLinkProps } from '@kbn/router-utils/src/get_router_link_props'; import type { AssetDetailsUrlState, RouteState } from '../../components/asset_details/types'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { REDIRECT_NODE_DETAILS_FROM_KEY, REDIRECT_NODE_DETAILS_TO_KEY, - REDIRECT_ASSET_DETAILS_KEY, } from './redirect_to_node_detail'; export interface MetricDetailsQueryParams { @@ -42,42 +45,66 @@ export const useNodeDetailsRedirect = () => { const { services: { application: { currentAppId$ }, + share, }, } = useKibanaContextForPlugin(); - const appId = useObservable(currentAppId$); + const locator = share.url.locators.get(ASSET_DETAILS_LOCATOR_ID); + const getNodeDetailUrl = useCallback( ({ assetType, assetId, search, - }: NodeDetailsRedirectParams): LinkDescriptor => { + }: NodeDetailsRedirectParams): RouterLinkProps => { const { from, to, ...additionalParams } = search; - - return { - app: 'metrics', - pathname: `link-to/${assetType}-detail/${assetId}`, - search: { - ...(Object.keys(additionalParams).length > 0 - ? { [REDIRECT_ASSET_DETAILS_KEY]: rison.encodeUnknown(additionalParams) } - : undefined), - // retrocompatibility - ...(from ? { [REDIRECT_NODE_DETAILS_FROM_KEY]: `${from}` } : undefined), - ...(to ? { [REDIRECT_NODE_DETAILS_TO_KEY]: `${to}` } : undefined), + const queryParams = { + assetDetails: + Object.keys(additionalParams).length > 0 + ? { + ...additionalParams, + dateRange: { + from: from ? new Date(from).toISOString() : undefined, + to: to ? new Date(to).toISOString() : undefined, + }, + } + : {}, + _a: { + time: { + ...(from + ? { [REDIRECT_NODE_DETAILS_FROM_KEY]: new Date(from).toISOString() } + : undefined), + interval: '>=1m', // need to pass the interval to consider the time valid + ...(to ? { [REDIRECT_NODE_DETAILS_TO_KEY]: new Date(to).toISOString() } : undefined), + }, }, + }; + + const nodeDetailsLocatorParams = { + ...queryParams, + assetType, + assetId, state: { + ...(location.state ?? {}), ...(location.key ? ({ originAppId: appId, originSearch: location.search, originPathname: location.pathname, } as RouteState) - : undefined), + : {}), }, }; + const nodeDetailsLinkProps = getRouterLinkProps({ + href: locator?.getRedirectUrl(nodeDetailsLocatorParams), + onClick: () => { + locator?.navigate(nodeDetailsLocatorParams, { replace: false }); + }, + }); + + return nodeDetailsLinkProps; }, - [location.key, location.search, location.pathname, appId] + [appId, location.key, location.pathname, location.search, location.state, locator] ); - return { getNodeDetailUrl }; }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/table/entry_title.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/table/entry_title.tsx index 0f6e65ef4c1d2..8bd22bde90716 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/table/entry_title.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/table/entry_title.tsx @@ -6,9 +6,8 @@ */ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiToolTip } from '@elastic/eui'; -import { useLinkProps } from '@kbn/observability-shared-plugin/public'; import { CloudProviderIcon } from '@kbn/custom-icons'; -import { useNodeDetailsRedirect } from '../../../../link_to'; +import { useNodeDetailsRedirect } from '../../../../link_to/use_node_details_redirect'; import type { HostNodeRow } from '../../hooks/use_hosts_table'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; @@ -19,19 +18,17 @@ interface EntryTitleProps { export const EntryTitle = ({ onClick, title }: EntryTitleProps) => { const { name, cloudProvider } = title; - const { getNodeDetailUrl } = useNodeDetailsRedirect(); const { parsedDateRange } = useUnifiedSearchContext(); + const { getNodeDetailUrl } = useNodeDetailsRedirect(); - const link = useLinkProps({ - ...getNodeDetailUrl({ - assetId: name, - assetType: 'host', - search: { - from: parsedDateRange?.from ? new Date(parsedDateRange?.from).getTime() : undefined, - to: parsedDateRange?.to ? new Date(parsedDateRange.to).getTime() : undefined, - name, - }, - }), + const link = getNodeDetailUrl({ + assetId: name, + assetType: 'host', + search: { + from: parsedDateRange?.from ? new Date(parsedDateRange?.from).getTime() : undefined, + to: parsedDateRange?.to ? new Date(parsedDateRange.to).getTime() : undefined, + name, + }, }); const providerName = cloudProvider ?? 'Unknown'; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx index e95314668e80c..43298c52aa2e8 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx @@ -90,17 +90,16 @@ export const NodeContextMenu: React.FC = withTheme return { label: '', value: '' }; }, [nodeType, node.ip, node.id]); - const nodeDetailMenuItemLinkProps = useLinkProps({ - ...getNodeDetailUrl({ - assetType: nodeType, - assetId: node.id, - search: { - from: nodeDetailFrom, - to: currentTime, - name: node.name, - }, - }), + const nodeDetailMenuItemLinkProps = getNodeDetailUrl({ + assetType: nodeType, + assetId: node.id, + search: { + from: nodeDetailFrom, + to: currentTime, + name: node.name, + }, }); + const apmTracesMenuItemLinkProps = useLinkProps({ app: 'apm', hash: 'traces', @@ -128,7 +127,8 @@ export const NodeContextMenu: React.FC = withTheme defaultMessage: '{inventoryName} metrics', values: { inventoryName: inventoryModel.singularDisplayName }, }), - ...nodeDetailMenuItemLinkProps, + href: nodeDetailMenuItemLinkProps.href, + onClick: nodeDetailMenuItemLinkProps.onClick, isDisabled: !showDetail, }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx index 248be6093585a..5a3ed5e2b3268 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx @@ -43,9 +43,8 @@ const mountComponentWithProviders = (props: Props): ReactWrapper => { jest.mock('../../../link_to', () => ({ useNodeDetailsRedirect: jest.fn(() => ({ getNodeDetailUrl: jest.fn(() => ({ - app: 'metrics', - pathname: 'link-to/pod-detail/example-01', - search: { from: '1546340400000', to: '1546344000000' }, + onClick: jest.fn(), + href: '/ftw/app/r?l=ASSET_DETAILS_LOCATOR&v=8.15.0&lz=N4IghgzhCmAuAicwEsA2EQC5gF8A0IA%2BmFqLMgLbSkgBmATgPYVYgCMA7GwCwAMArADZB3NgCZBADhAFYjVpx69BvMaInSc%2BcFDgAVAJ4AHaphAALRhFgydMWAEkAJqwBUt62FinQjesgBzZAA7AEEjI2dWKlh%2FAGMMAj9AkIBlaDB6OPNWAH4Y%2BIgAUQAPI1Q%2FaHoAXgAKbMzYAHkjckZgiExazziAa0wAQlo8WGNoTFQQ6DwDUJLkCABZRidxhmYALSrGAEo8Rlbkds7asACA%2BmgAryPgzDAANwC8AuQEwdrT88vrtrvH55xRgVeiYIEg3h4WjIaCoJyYCAGazQCgAOjiRgArqi5LAwKhUcE%2FGijHFYHsvhcrjd2vcnnhwX4wcC%2FGwoTC4ZhepiAEZVYJwaAQVFGFborGozEQM7QQkrWWk8l4Sk%2FGn%2FemM0GasTs2HwpyMPpVcXY3H4kVknZ7CCMTFZcarWhgTGoJXkKj0MDBALjWrrCiYIkAdwAtGxzHgQt56A98ZgAKQAZiKSfgbF4EBGjEDjCDVoAZK8EqVypV6AA1GFB5zVADkvFrtmSQWCAAUvOZgmAqKwAPTQMogqogLRAA%3D', })), })), })); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx index a793d69ea2d27..0243ccacf136a 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx @@ -106,19 +106,16 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ const nodeType = source && options.groupBy && fieldToNodeType(source, options.groupBy); - const nodeDetailLinkProps = useLinkProps({ - app: 'metrics', - ...(nodeType - ? getNodeDetailUrl({ - assetType: nodeType, - assetId: series.id, - search: { - from: dateMathExpressionToEpoch(timeRange.from), - to: dateMathExpressionToEpoch(timeRange.to, true), - }, - }) - : {}), - }); + const nodeDetailLinkProps = nodeType + ? getNodeDetailUrl({ + assetType: nodeType, + assetId: series.id, + search: { + from: dateMathExpressionToEpoch(timeRange.from), + to: dateMathExpressionToEpoch(timeRange.to, true), + }, + }) + : {}; const tsvbLinkProps = useLinkProps({ ...createTSVBLink(source, options, series, timeRange, chartOptions), }); diff --git a/x-pack/plugins/observability_solution/infra/tsconfig.json b/x-pack/plugins/observability_solution/infra/tsconfig.json index f1a25a6b0a7e3..dfd1a52466a9a 100644 --- a/x-pack/plugins/observability_solution/infra/tsconfig.json +++ b/x-pack/plugins/observability_solution/infra/tsconfig.json @@ -98,7 +98,8 @@ "@kbn/shared-svg", "@kbn/aiops-log-rate-analysis", "@kbn/react-hooks", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/router-utils" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_solution/observability_shared/public/index.ts b/x-pack/plugins/observability_solution/observability_shared/public/index.ts index 15a977a6c236b..93094c79369a9 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/index.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/index.ts @@ -103,4 +103,7 @@ export { export { BottomBarActions } from './components/bottom_bar_actions/bottom_bar_actions'; export { FieldValueSelection, FieldValueSuggestions } from './components'; export { ASSET_DETAILS_FLYOUT_LOCATOR_ID } from './locators/infra/asset_details_flyout_locator'; -export { ASSET_DETAILS_LOCATOR_ID } from './locators/infra/asset_details_locator'; +export { + ASSET_DETAILS_LOCATOR_ID, + type AssetDetailsLocatorParams, +} from './locators/infra/asset_details_locator'; diff --git a/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/asset_details_locator.ts b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/asset_details_locator.ts index b89c616bfb520..59729aeb71f0e 100644 --- a/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/asset_details_locator.ts +++ b/x-pack/plugins/observability_solution/observability_shared/public/locators/infra/asset_details_locator.ts @@ -13,10 +13,11 @@ export type AssetDetailsLocator = LocatorPublic; export interface AssetDetailsLocatorParams extends SerializableRecord { assetType: string; assetId: string; + state?: SerializableRecord; _a?: { time?: { - from: string; - to: string; + from?: string; + to?: string; }; interval?: string; }; @@ -41,7 +42,7 @@ export class AssetDetailsLocatorDefinition implements LocatorDefinition