diff --git a/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts b/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts
index 05e17c16eb92..b6e35dbf5177 100644
--- a/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts
+++ b/x-pack/plugins/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts
@@ -6,42 +6,42 @@
*/
import { i18n } from '@kbn/i18n';
-import { type AssetDetailsProps, FlyoutTabIds, type Tab } from '../../../types';
+import { type AssetDetailsProps, ContentTabIds, type Tab } from '../../../types';
const links: AssetDetailsProps['links'] = ['alertRule', 'nodeDetails', 'apmServices'];
const tabs: Tab[] = [
{
- id: FlyoutTabIds.OVERVIEW,
+ id: ContentTabIds.OVERVIEW,
name: i18n.translate('xpack.infra.nodeDetails.tabs.overview.title', {
defaultMessage: 'Overview',
}),
},
{
- id: FlyoutTabIds.LOGS,
+ id: ContentTabIds.LOGS,
name: i18n.translate('xpack.infra.nodeDetails.tabs.logs', {
defaultMessage: 'Logs',
}),
},
{
- id: FlyoutTabIds.METADATA,
+ id: ContentTabIds.METADATA,
name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.metadata', {
defaultMessage: 'Metadata',
}),
},
{
- id: FlyoutTabIds.PROCESSES,
+ id: ContentTabIds.PROCESSES,
name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.processes', {
defaultMessage: 'Processes',
}),
},
{
- id: FlyoutTabIds.ANOMALIES,
+ id: ContentTabIds.ANOMALIES,
name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', {
defaultMessage: 'Anomalies',
}),
},
{
- id: FlyoutTabIds.LINK_TO_APM,
+ id: ContentTabIds.LINK_TO_APM,
name: i18n.translate('xpack.infra.infra.nodeDetails.apmTabLabel', {
defaultMessage: 'APM',
}),
diff --git a/x-pack/plugins/infra/public/components/asset_details/constants.ts b/x-pack/plugins/infra/public/components/asset_details/constants.ts
index 726f47450d0c..cdd5b9508215 100644
--- a/x-pack/plugins/infra/public/components/asset_details/constants.ts
+++ b/x-pack/plugins/infra/public/components/asset_details/constants.ts
@@ -8,3 +8,5 @@
export const ASSET_DETAILS_FLYOUT_COMPONENT_NAME = 'infraAssetDetailsFlyout';
export const METRIC_CHART_HEIGHT = 300;
export const APM_HOST_FILTER_FIELD = 'host.hostname';
+
+export const ASSET_DETAILS_URL_STATE_KEY = 'assetDetails';
diff --git a/x-pack/plugins/infra/public/components/asset_details/content/content.tsx b/x-pack/plugins/infra/public/components/asset_details/content/content.tsx
index c9d622cf0149..0cf51f1fff3d 100644
--- a/x-pack/plugins/infra/public/components/asset_details/content/content.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/content/content.tsx
@@ -10,7 +10,7 @@ import React from 'react';
import { DatePicker } from '../date_picker/date_picker';
import { useTabSwitcherContext } from '../hooks/use_tab_switcher';
import { Anomalies, Metadata, Processes, Osquery, Logs, Overview } from '../tabs';
-import { FlyoutTabIds } from '../types';
+import { ContentTabIds } from '../types';
export const Content = () => {
return (
@@ -18,31 +18,31 @@ export const Content = () => {
-
+
-
+
-
+
-
+
-
+
-
+
@@ -50,11 +50,11 @@ export const Content = () => {
);
};
-const DatePickerWrapper = ({ visibleFor }: { visibleFor: FlyoutTabIds[] }) => {
+const DatePickerWrapper = ({ visibleFor }: { visibleFor: ContentTabIds[] }) => {
const { activeTabId } = useTabSwitcherContext();
return (
-
+
);
@@ -64,7 +64,7 @@ const TabPanel = ({
activeWhen,
children,
}: {
- activeWhen: FlyoutTabIds;
+ activeWhen: ContentTabIds;
children: React.ReactNode;
}) => {
const { renderedTabsSet, activeTabId } = useTabSwitcherContext();
diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_render_props.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_render_props.ts
index 6ddba44e6da7..eab5f8326f52 100644
--- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_render_props.ts
+++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_render_props.ts
@@ -7,6 +7,7 @@
import createContainer from 'constate';
import type { AssetDetailsProps } from '../types';
+import { useAssetDetailsUrlState } from './use_asset_details_url_state';
import { useMetadataStateProviderContext } from './use_metadata_state';
export interface UseAssetDetailsRenderProps {
@@ -14,17 +15,18 @@ export interface UseAssetDetailsRenderProps {
}
export function useAssetDetailsRenderProps({ props }: UseAssetDetailsRenderProps) {
+ const [urlState] = useAssetDetailsUrlState();
const { metadata } = useMetadataStateProviderContext();
const { asset, assetType, overrides, renderMode } = props;
// When the asset asset.name is known we can load the page faster
// Otherwise we need to use metadata response.
- const loading = !asset.name && !metadata?.name;
+ const loading = !urlState?.name && !asset.name && !metadata?.name;
return {
asset: {
...asset,
- name: asset.name || metadata?.name || 'asset-name',
+ name: urlState?.name || asset.name || metadata?.name || '',
},
assetType,
overrides,
diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts
index 370778555f7d..2199b76fa8fe 100644
--- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts
+++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts
@@ -9,15 +9,13 @@ import * as rt from 'io-ts';
import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { constant, identity } from 'fp-ts/lib/function';
-import { FlyoutTabIds } from '../types';
+import { ContentTabIds } from '../types';
import { useUrlState } from '../../../utils/use_url_state';
+import { ASSET_DETAILS_URL_STATE_KEY } from '../constants';
-export const DEFAULT_STATE: AssetDetailsState = {
- tabId: FlyoutTabIds.OVERVIEW,
- processSearch: undefined,
- metadataSearch: undefined,
+export const DEFAULT_STATE: AssetDetailsUrlState = {
+ tabId: ContentTabIds.OVERVIEW,
};
-const ASSET_DETAILS_URL_STATE_KEY = 'asset_details';
type SetAssetDetailsState = (newProp: Payload | null) => void;
@@ -44,34 +42,31 @@ export const useAssetDetailsUrlState = (): [AssetDetailsUrl, SetAssetDetailsStat
};
const TabIdRT = rt.union([
- rt.literal(FlyoutTabIds.OVERVIEW),
- rt.literal(FlyoutTabIds.METADATA),
- rt.literal(FlyoutTabIds.PROCESSES),
- rt.literal(FlyoutTabIds.LOGS),
- rt.literal(FlyoutTabIds.ANOMALIES),
- rt.literal(FlyoutTabIds.OSQUERY),
+ rt.literal(ContentTabIds.OVERVIEW),
+ rt.literal(ContentTabIds.METADATA),
+ rt.literal(ContentTabIds.PROCESSES),
+ rt.literal(ContentTabIds.LOGS),
+ rt.literal(ContentTabIds.ANOMALIES),
+ rt.literal(ContentTabIds.OSQUERY),
]);
-const AssetDetailsStateRT = rt.intersection([
- rt.type({
- tabId: TabIdRT,
+const AssetDetailsUrlStateRT = rt.partial({
+ tabId: TabIdRT,
+ name: rt.string,
+ dateRange: rt.type({
+ from: rt.string,
+ to: rt.string,
}),
- rt.partial({
- dateRange: rt.type({
- from: rt.string,
- to: rt.string,
- }),
- processSearch: rt.string,
- metadataSearch: rt.string,
- logsSearch: rt.string,
- }),
-]);
+ processSearch: rt.string,
+ metadataSearch: rt.string,
+ logsSearch: rt.string,
+});
-const AssetDetailsUrlRT = rt.union([AssetDetailsStateRT, rt.null]);
+const AssetDetailsUrlRT = rt.union([AssetDetailsUrlStateRT, rt.null]);
-export type AssetDetailsState = rt.TypeOf
;
+export type AssetDetailsUrlState = rt.TypeOf;
type AssetDetailsUrl = rt.TypeOf;
-type Payload = Partial;
+type Payload = Partial;
const encodeUrlState = AssetDetailsUrlRT.encode;
const decodeUrlState = (value: unknown) => {
diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_date_range.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_date_range.ts
index 7e98c5683452..c6109d90af04 100644
--- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_date_range.ts
+++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_date_range.ts
@@ -7,42 +7,55 @@
import type { TimeRange } from '@kbn/es-query';
import createContainer from 'constate';
-import { useCallback, useMemo } from 'react';
+import { useCallback, useState } from 'react';
+import useEffectOnce from 'react-use/lib/useEffectOnce';
import { parseDateRange } from '../../../utils/datemath';
import { toTimestampRange } from '../utils';
import { useAssetDetailsUrlState } from './use_asset_details_url_state';
-const DEFAULT_DATE_RANGE: TimeRange = {
- from: 'now-15m',
- to: 'now',
-};
+const DEFAULT_FROM_IN_MILLISECONDS = 15 * 60000;
+const getDefaultDateRange = () => {
+ const now = Date.now();
+ return {
+ from: new Date(now - DEFAULT_FROM_IN_MILLISECONDS).toISOString(),
+ to: new Date(now).toISOString(),
+ };
+};
export interface UseDateRangeProviderProps {
initialDateRange: TimeRange;
}
export function useDateRangeProvider({ initialDateRange }: UseDateRangeProviderProps) {
const [urlState, setUrlState] = useAssetDetailsUrlState();
- const dateRange: TimeRange = urlState?.dateRange ?? initialDateRange;
+ const dateRange: TimeRange = urlState?.dateRange ?? initialDateRange ?? getDefaultDateRange();
+ const [parsedDateRange, setParsedDateRange] = useState(parseDateRange(dateRange));
+
+ useEffectOnce(() => {
+ const { from, to } = getParsedDateRange();
+
+ // forces the date picker to initiallize with absolute dates.
+ setUrlState({ dateRange: { from, to } });
+ });
const setDateRange = useCallback(
(newDateRange: TimeRange) => {
setUrlState({ dateRange: newDateRange });
+ setParsedDateRange(parseDateRange(newDateRange));
},
[setUrlState]
);
- const parsedDateRange = useMemo(() => {
- const { from = DEFAULT_DATE_RANGE.from, to = DEFAULT_DATE_RANGE.to } =
- parseDateRange(dateRange);
+ const getParsedDateRange = useCallback(() => {
+ const { from = dateRange.from, to = dateRange.to } = parsedDateRange;
return { from, to };
- }, [dateRange]);
+ }, [dateRange, parsedDateRange]);
const getDateRangeInTimestamp = useCallback(
- () => toTimestampRange(parsedDateRange),
- [parsedDateRange]
+ () => toTimestampRange(getParsedDateRange()),
+ [getParsedDateRange]
);
return { dateRange, setDateRange, getDateRangeInTimestamp };
diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_metadata_state.ts b/x-pack/plugins/infra/public/components/asset_details/hooks/use_metadata_state.ts
index 6fc1991fcb1d..b19840f7e3ae 100644
--- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_metadata_state.ts
+++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_metadata_state.ts
@@ -5,16 +5,19 @@
* 2.0.
*/
+import { useEffect } from 'react';
import createContainer from 'constate';
import { findInventoryModel } from '../../../../common/inventory_models';
import { useSourceContext } from '../../../containers/metrics_source';
import { useMetadata } from './use_metadata';
import { AssetDetailsProps } from '../types';
import { useDateRangeProviderContext } from './use_date_range';
+import { useAssetDetailsUrlState } from './use_asset_details_url_state';
export type UseMetadataProviderProps = Pick;
export function useMetadataProvider({ asset, assetType }: UseMetadataProviderProps) {
+ const [, setUrlState] = useAssetDetailsUrlState();
const { getDateRangeInTimestamp } = useDateRangeProviderContext();
const inventoryModel = findInventoryModel(assetType);
const { sourceId } = useSourceContext();
@@ -27,6 +30,12 @@ export function useMetadataProvider({ asset, assetType }: UseMetadataProviderPro
getDateRangeInTimestamp()
);
+ useEffect(() => {
+ if (metadata?.name) {
+ setUrlState({ name: metadata.name });
+ }
+ }, [metadata?.name, setUrlState]);
+
return {
loading,
error,
diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx b/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx
index 6e0cbf9ad215..5edfc6c8ce45 100644
--- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx
@@ -21,10 +21,10 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { APM_HOST_FILTER_FIELD } from '../constants';
import { LinkToAlertsRule, LinkToApmServices, LinkToNodeDetails } from '../links';
-import { FlyoutTabIds, type RouteState, type LinkOptions, type Tab, type TabIds } from '../types';
+import { ContentTabIds, type RouteState, type LinkOptions, type Tab, type TabIds } from '../types';
import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props';
-import { useDateRangeProviderContext } from './use_date_range';
import { useTabSwitcherContext } from './use_tab_switcher';
+import { useAssetDetailsUrlState } from './use_asset_details_url_state';
type TabItem = NonNullable['tabs']>[number];
@@ -60,7 +60,7 @@ export const useTemplateHeaderBreadcrumbs = () => {
const breadcrumbs: EuiBreadcrumbsProps['breadcrumbs'] =
// If there is a state object in location, it's persisted in case the page is opened in a new tab or after page refresh
// With that, we can show the return button. Otherwise, it will be hidden (ex: the user opened a shared URL or opened the page from their bookmarks)
- location.state || history.length > 1
+ location.state || location.key
? [
{
text: (
@@ -89,22 +89,16 @@ export const useTemplateHeaderBreadcrumbs = () => {
};
const useRightSideItems = (links?: LinkOptions[]) => {
- const { getDateRangeInTimestamp } = useDateRangeProviderContext();
- const { asset, assetType, overrides } = useAssetDetailsRenderPropsContext();
+ const [state] = useAssetDetailsUrlState();
+ const { asset, overrides } = useAssetDetailsRenderPropsContext();
const topCornerLinkComponents: Record = useMemo(
() => ({
- nodeDetails: (
-
- ),
+ nodeDetails: ,
alertRule: ,
apmServices: ,
}),
- [asset, assetType, getDateRangeInTimestamp, overrides?.alertRule?.onCreateRuleClick]
+ [asset, overrides?.alertRule?.onCreateRuleClick, state]
);
const rightSideItems = useMemo(
@@ -157,7 +151,7 @@ const useTabs = (tabs: Tab[]) => {
const tabEntries: TabItem[] = useMemo(
() =>
tabs.map(({ name, ...tab }) => {
- if (tab.id === FlyoutTabIds.LINK_TO_APM) {
+ if (tab.id === ContentTabIds.LINK_TO_APM) {
return getTabToApmTraces(name);
}
diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx b/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx
index 5de6383099e1..9ab02ce3f5fd 100644
--- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_tab_switcher.tsx
@@ -8,7 +8,7 @@
import createContainer from 'constate';
import { useLazyRef } from '../../../hooks/use_lazy_ref';
import type { TabIds } from '../types';
-import { AssetDetailsState, useAssetDetailsUrlState } from './use_asset_details_url_state';
+import { AssetDetailsUrlState, useAssetDetailsUrlState } from './use_asset_details_url_state';
interface TabSwitcherParams {
defaultActiveTabId?: TabIds;
@@ -26,7 +26,7 @@ export function useTabSwitcher({ defaultActiveTabId }: TabSwitcherParams) {
// On a tab click, mark the tab content as allowed to be rendered
renderedTabsSet.current.add(tabId);
- setUrlState({ tabId: tabId as AssetDetailsState['tabId'] });
+ setUrlState({ tabId: tabId as AssetDetailsUrlState['tabId'] });
};
return {
diff --git a/x-pack/plugins/infra/public/components/asset_details/links/link_to_node_details.tsx b/x-pack/plugins/infra/public/components/asset_details/links/link_to_node_details.tsx
index 87297b247bb3..399f1e5bef64 100644
--- a/x-pack/plugins/infra/public/components/asset_details/links/link_to_node_details.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/links/link_to_node_details.tsx
@@ -8,29 +8,28 @@ import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButtonEmpty } from '@elastic/eui';
import { useLinkProps } from '@kbn/observability-shared-plugin/public';
+
import { useNodeDetailsRedirect } from '../../../pages/link_to';
-import type { InventoryItemType } from '../../../../common/inventory_models/types';
+import { Asset, AssetDetailsUrlState } from '../types';
export interface LinkToNodeDetailsProps {
- dateRangeTimestamp: { from: number; to: number };
asset: Asset;
- assetType: InventoryItemType;
+ search: AssetDetailsUrlState;
}
-export const LinkToNodeDetails = ({
- asset,
- assetType,
- dateRangeTimestamp,
-}: LinkToNodeDetailsProps) => {
+export const LinkToNodeDetails = ({ asset, search }: LinkToNodeDetailsProps) => {
const { getNodeDetailUrl } = useNodeDetailsRedirect();
+ const { dateRange, ...assetDetails } = search;
+
const nodeDetailMenuItemLinkProps = useLinkProps({
...getNodeDetailUrl({
- nodeType: assetType,
- nodeId: asset.id,
+ assetType: 'host',
+ assetId: asset.id,
search: {
- from: dateRangeTimestamp.from,
- to: dateRangeTimestamp.to,
- assetName: asset.name,
+ ...assetDetails,
+ from: dateRange?.from ? new Date(dateRange?.from).getTime() : undefined,
+ to: dateRange?.to ? new Date(dateRange.to).getTime() : undefined,
+ name: asset.name,
},
}),
});
diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx
index bdc4f3d30518..e178c665ef45 100644
--- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx
+++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx
@@ -20,7 +20,7 @@ import { EuiTitle } from '@elastic/eui';
import type { InfraMetadata } from '../../../../../../common/http_api';
import { NOT_AVAILABLE_LABEL } from '../../../translations';
import { useTabSwitcherContext } from '../../../hooks/use_tab_switcher';
-import { FlyoutTabIds } from '../../../types';
+import { ContentTabIds } from '../../../types';
import { ExpandableContent } from '../../../components/expandable_content';
import { MetadataHeader } from './metadata_header';
import { MetadataExplanationMessage } from '../../../components/metadata_explanation';
@@ -74,7 +74,7 @@ export const MetadataSummaryList = ({
const { showTab } = useTabSwitcherContext();
const onClick = () => {
- showTab(FlyoutTabIds.METADATA);
+ showTab(ContentTabIds.METADATA);
};
return (
diff --git a/x-pack/plugins/infra/public/components/asset_details/types.ts b/x-pack/plugins/infra/public/components/asset_details/types.ts
index 548068d93e66..a0856d66736f 100644
--- a/x-pack/plugins/infra/public/components/asset_details/types.ts
+++ b/x-pack/plugins/infra/public/components/asset_details/types.ts
@@ -9,12 +9,14 @@ import { TimeRange } from '@kbn/es-query';
import { Search } from 'history';
import type { InventoryItemType } from '../../../common/inventory_models/types';
+export type { AssetDetailsUrlState } from './hooks/use_asset_details_url_state';
+
export interface Asset {
id: string;
name?: string;
}
-export enum FlyoutTabIds {
+export enum ContentTabIds {
OVERVIEW = 'overview',
METADATA = 'metadata',
PROCESSES = 'processes',
@@ -22,10 +24,9 @@ export enum FlyoutTabIds {
OSQUERY = 'osquery',
LOGS = 'logs',
LINK_TO_APM = 'linkToApm',
- LINK_TO_UPTIME = 'linkToUptime',
}
-export type TabIds = `${FlyoutTabIds}`;
+export type TabIds = `${ContentTabIds}`;
export interface OverridableTabState {
metadata?: {
@@ -55,7 +56,7 @@ export interface FullPageProps {
export type RenderMode = FlyoutProps | FullPageProps;
export interface Tab {
- id: FlyoutTabIds;
+ id: ContentTabIds;
name: string;
}
@@ -64,7 +65,7 @@ export type LinkOptions = 'alertRule' | 'nodeDetails' | 'apmServices';
export interface AssetDetailsProps {
asset: Asset;
assetType: InventoryItemType;
- dateRange: TimeRange;
+ dateRange?: TimeRange;
tabs: Tab[];
overrides?: OverridableTabState;
renderMode: RenderMode;
@@ -82,7 +83,7 @@ export interface ContentTemplateProps {
export interface RouteState {
originAppId: string;
- originPathname?: string;
+ originPathname: string;
originSearch?: Search;
}
diff --git a/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/components/metrics_node_details_link.tsx b/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/components/metrics_node_details_link.tsx
index fd8868f0218a..d19170dfce52 100644
--- a/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/components/metrics_node_details_link.tsx
+++ b/x-pack/plugins/infra/public/components/infrastructure_node_metrics_tables/shared/components/metrics_node_details_link.tsx
@@ -31,11 +31,16 @@ export const MetricsNodeDetailsLink = ({
const { getNodeDetailUrl } = useNodeDetailsRedirect();
const linkProps = useLinkProps(
getNodeDetailUrl({
- nodeType,
- nodeId: id,
+ assetType: nodeType,
+ assetId: id,
search: {
from: parse(timerange.from)?.valueOf(),
to: parse(timerange.to)?.valueOf(),
+ ...(nodeType === 'host'
+ ? {
+ name: id,
+ }
+ : undefined),
},
})
);
diff --git a/x-pack/plugins/infra/public/pages/link_to/index.ts b/x-pack/plugins/infra/public/pages/link_to/index.ts
index aae22218a4f8..41dc4177186e 100644
--- a/x-pack/plugins/infra/public/pages/link_to/index.ts
+++ b/x-pack/plugins/infra/public/pages/link_to/index.ts
@@ -9,4 +9,9 @@ export { LinkToLogsPage } from './link_to_logs';
export { LinkToMetricsPage } from './link_to_metrics';
export { RedirectToNodeLogs } from './redirect_to_node_logs';
export { RedirectToNodeDetail } from './redirect_to_node_detail';
+
export { useNodeDetailsRedirect } from './use_node_details_redirect';
+export type {
+ AssetDetailsQueryParams,
+ MetricDetailsQueryParams,
+} from './use_node_details_redirect';
diff --git a/x-pack/plugins/infra/public/pages/link_to/query_params.ts b/x-pack/plugins/infra/public/pages/link_to/query_params.ts
index a80f16399358..45e1bc9a7991 100644
--- a/x-pack/plugins/infra/public/pages/link_to/query_params.ts
+++ b/x-pack/plugins/infra/public/pages/link_to/query_params.ts
@@ -28,13 +28,3 @@ export const getFromFromLocation = (location: Location) => {
const timeParam = getParamFromQueryString(getQueryStringFromLocation(location), 'from');
return timeParam ? parseFloat(timeParam) : NaN;
};
-
-export const getNodeNameFromLocation = (location: Location) => {
- const nameParam = getParamFromQueryString(getQueryStringFromLocation(location), 'assetName');
- return nameParam;
-};
-
-export const getStateFromLocation = (location: Location) => {
- const nameParam = getParamFromQueryString(getQueryStringFromLocation(location), 'state');
- return nameParam;
-};
diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx
index 1bd8aa3b793c..47c2e8714908 100644
--- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx
@@ -7,15 +7,41 @@
import React from 'react';
import { Redirect, useLocation, useRouteMatch } from 'react-router-dom';
+import rison from '@kbn/rison';
+import { replaceStateKeyInQueryString } from '../../../common/url_state_storage_service';
import { replaceMetricTimeInQueryString } from '../metrics/metric_detail/hooks/use_metrics_time';
-import {
- getFromFromLocation,
- getToFromLocation,
- getNodeNameFromLocation,
- getStateFromLocation,
-} from './query_params';
import { InventoryItemType } from '../../../common/inventory_models/types';
-import { RouteState } from '../../components/asset_details/types';
+import { AssetDetailsUrlState, RouteState } from '../../components/asset_details/types';
+import { ASSET_DETAILS_URL_STATE_KEY } from '../../components/asset_details/constants';
+
+export const REDIRECT_NODE_DETAILS_FROM_KEY = 'from';
+export const REDIRECT_NODE_DETAILS_TO_KEY = 'to';
+export const REDIRECT_ASSET_DETAILS_KEY = 'assetDetails';
+export const REDIRECT_NODE_DETAILS_STATE_KEY = 'state';
+
+const getHostDetailSearch = (queryParams: URLSearchParams) => {
+ const from = queryParams.get(REDIRECT_NODE_DETAILS_FROM_KEY);
+ 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,
+ },
+ } 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
+ )('');
+};
export const RedirectToNodeDetail = () => {
const {
@@ -23,25 +49,17 @@ export const RedirectToNodeDetail = () => {
} = useRouteMatch<{ nodeType: InventoryItemType; nodeId: string }>();
const location = useLocation();
+ const queryParams = new URLSearchParams(location.search);
- const searchString = replaceMetricTimeInQueryString(
- getFromFromLocation(location),
- getToFromLocation(location)
- )('');
-
- const queryParams = new URLSearchParams(searchString);
-
- if (nodeType === 'host') {
- const assetName = getNodeNameFromLocation(location);
- if (assetName) {
- queryParams.set('assetName', assetName);
- }
- }
+ const search =
+ nodeType === 'host' ? getHostDetailSearch(queryParams) : getNodeDetailSearch(queryParams);
let state: RouteState | undefined;
try {
- const stateFromLocation = getStateFromLocation(location);
- state = stateFromLocation ? JSON.parse(stateFromLocation) : undefined;
+ const stateFromLocation = queryParams.get(REDIRECT_NODE_DETAILS_STATE_KEY);
+ state = stateFromLocation
+ ? (rison.decode(stateFromLocation) as unknown as RouteState)
+ : undefined;
} catch (err) {
state = undefined;
}
@@ -50,7 +68,7 @@ export const RedirectToNodeDetail = () => {
diff --git a/x-pack/plugins/infra/public/pages/link_to/use_node_details_redirect.test.tsx b/x-pack/plugins/infra/public/pages/link_to/use_node_details_redirect.test.tsx
index 81b13a652b7e..2aea52cfa3e2 100644
--- a/x-pack/plugins/infra/public/pages/link_to/use_node_details_redirect.test.tsx
+++ b/x-pack/plugins/infra/public/pages/link_to/use_node_details_redirect.test.tsx
@@ -34,8 +34,8 @@ describe('useNodeDetailsRedirect', () => {
expect(
result.current.getNodeDetailUrl({
- nodeType: 'host',
- nodeId: 'example-01',
+ assetType: 'host',
+ assetId: 'example-01',
search: {
from: new Date(fromDateStrig).getTime(),
to: new Date(toDateStrig).getTime(),
diff --git a/x-pack/plugins/infra/public/pages/link_to/use_node_details_redirect.ts b/x-pack/plugins/infra/public/pages/link_to/use_node_details_redirect.ts
index ea4726b2e681..4ffc1f773ec6 100644
--- a/x-pack/plugins/infra/public/pages/link_to/use_node_details_redirect.ts
+++ b/x-pack/plugins/infra/public/pages/link_to/use_node_details_redirect.ts
@@ -9,14 +9,33 @@ 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 '../../../common/inventory_models/types';
-import type { RouteState } from '../../components/asset_details/types';
+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,
+ REDIRECT_NODE_DETAILS_STATE_KEY,
+} from './redirect_to_node_detail';
-interface QueryParams {
+export interface MetricDetailsQueryParams {
from?: number;
to?: number;
- assetName?: string;
+}
+
+export type AssetDetailsQueryParams = MetricDetailsQueryParams &
+ Omit;
+
+type SearchParams = T extends 'host'
+ ? AssetDetailsQueryParams
+ : MetricDetailsQueryParams;
+
+export interface NodeDetailsRedirectParams {
+ assetType: T;
+ assetId: string;
+ search: SearchParams;
}
export const useNodeDetailsRedirect = () => {
@@ -29,38 +48,30 @@ export const useNodeDetailsRedirect = () => {
const appId = useObservable(currentAppId$);
const getNodeDetailUrl = useCallback(
- ({
- nodeType,
- nodeId,
+ ({
+ assetType,
+ assetId,
search,
- }: {
- nodeType: InventoryItemType;
- nodeId: string;
- search: QueryParams;
- }): LinkDescriptor => {
- const { to, from, ...rest } = search;
+ }: NodeDetailsRedirectParams): LinkDescriptor => {
+ const { from, to, ...rest } = search;
return {
app: 'metrics',
- pathname: `link-to/${nodeType}-detail/${nodeId}`,
+ pathname: `link-to/${assetType}-detail/${assetId}`,
search: {
- ...rest,
- ...(to && from
- ? {
- to: `${to}`,
- from: `${from}`,
- }
- : undefined),
- // While we don't have a shared state between all page in infra, this makes it possible to restore a page state when returning to the previous route
- ...(location.search || location.pathname
+ ...(rest ? { [REDIRECT_ASSET_DETAILS_KEY]: rison.encodeUnknown(rest) } : undefined),
+ ...(location.pathname
? {
- state: JSON.stringify({
+ [REDIRECT_NODE_DETAILS_STATE_KEY]: rison.encodeUnknown({
originAppId: appId,
originSearch: location.search,
originPathname: location.pathname,
} as RouteState),
}
: undefined),
+ // retrocompatibility
+ ...(from ? { [REDIRECT_NODE_DETAILS_FROM_KEY]: `${from}` } : undefined),
+ ...(to ? { [REDIRECT_NODE_DETAILS_TO_KEY]: `${to}` } : undefined),
},
};
},
diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/tabs.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/tabs.ts
index 7d354d19bed1..6ce0eeddbcb0 100644
--- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/tabs.ts
+++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/host_details_flyout/tabs.ts
@@ -6,41 +6,41 @@
*/
import { i18n } from '@kbn/i18n';
-import { FlyoutTabIds, type Tab } from '../../../../../components/asset_details/types';
+import { ContentTabIds, type Tab } from '../../../../../components/asset_details/types';
export const orderedFlyoutTabs: Tab[] = [
{
- id: FlyoutTabIds.OVERVIEW,
+ id: ContentTabIds.OVERVIEW,
name: i18n.translate('xpack.infra.nodeDetails.tabs.overview.title', {
defaultMessage: 'Overview',
}),
},
{
- id: FlyoutTabIds.METADATA,
+ id: ContentTabIds.METADATA,
name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', {
defaultMessage: 'Metadata',
}),
},
{
- id: FlyoutTabIds.PROCESSES,
+ id: ContentTabIds.PROCESSES,
name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.processes', {
defaultMessage: 'Processes',
}),
},
{
- id: FlyoutTabIds.LOGS,
+ id: ContentTabIds.LOGS,
name: i18n.translate('xpack.infra.nodeDetails.tabs.logs.title', {
defaultMessage: 'Logs',
}),
},
{
- id: FlyoutTabIds.ANOMALIES,
+ id: ContentTabIds.ANOMALIES,
name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', {
defaultMessage: 'Anomalies',
}),
},
{
- id: FlyoutTabIds.OSQUERY,
+ id: ContentTabIds.OSQUERY,
name: i18n.translate('xpack.infra.nodeDetails.tabs.osquery', {
defaultMessage: 'Osquery',
}),
diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/table/entry_title.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/table/entry_title.tsx
index 2019d2efa1c4..c4b029179feb 100644
--- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/table/entry_title.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/table/entry_title.tsx
@@ -7,6 +7,7 @@
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiToolTip, IconType } from '@elastic/eui';
import { useLinkProps } from '@kbn/observability-shared-plugin/public';
+import { AssetDetailsUrlState } from '../../../../../components/asset_details/types';
import { useNodeDetailsRedirect } from '../../../../link_to';
import type { CloudProvider, HostNodeRow } from '../../hooks/use_hosts_table';
@@ -19,22 +20,25 @@ const cloudIcons: Record = {
interface EntryTitleProps {
onClick: () => void;
- dateRangeTs: { from: number; to: number };
title: HostNodeRow['title'];
+ search: AssetDetailsUrlState;
}
-export const EntryTitle = ({ onClick, dateRangeTs, title }: EntryTitleProps) => {
+export const EntryTitle = ({ onClick, title, search }: EntryTitleProps) => {
const { name, cloudProvider } = title;
const { getNodeDetailUrl } = useNodeDetailsRedirect();
+ const { dateRange, ...assetDetails } = search;
+
const link = useLinkProps({
...getNodeDetailUrl({
- nodeId: name,
- nodeType: 'host',
+ assetId: name,
+ assetType: 'host',
search: {
- from: dateRangeTs.from,
- to: dateRangeTs.to,
- assetName: name,
+ ...assetDetails,
+ from: dateRange?.from ? new Date(dateRange?.from).getTime() : undefined,
+ to: dateRange?.to ? new Date(dateRange.to).getTime() : undefined,
+ name,
},
}),
});
diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx
index f70718fa855a..076dd8c449ca 100644
--- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx
@@ -127,7 +127,7 @@ const sortTableData =
export const useHostsTable = () => {
const [selectedItems, setSelectedItems] = useState([]);
const { hostNodes } = useHostsViewContext();
- const { getDateRangeAsTimestamp } = useUnifiedSearchContext();
+ const { parsedDateRange } = useUnifiedSearchContext();
const [{ detailsItemId, pagination, sorting }, setProperties] = useHostsTableUrlState();
const {
services: {
@@ -236,7 +236,11 @@ export const useHostsTable = () => {
render: (title: HostNodeRow['title']) => (
reportHostEntryClick(title)}
/>
),
@@ -343,7 +347,7 @@ export const useHostsTable = () => {
width: '120px',
},
],
- [detailsItemId, getDateRangeAsTimestamp, reportHostEntryClick, setProperties]
+ [detailsItemId, parsedDateRange, reportHostEntryClick, setProperties]
);
const selection: EuiTableSelectionType = {
diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx
index bf854fb546ae..562c7a43175c 100644
--- a/x-pack/plugins/infra/public/pages/metrics/index.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx
@@ -22,7 +22,7 @@ import { MetricsExplorerOptionsContainer } from './metrics_explorer/hooks/use_me
import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state';
import { MetricsExplorerPage } from './metrics_explorer';
import { SnapshotPage } from './inventory_view';
-import { MetricDetail } from './metric_detail';
+import { NodeDetail } from './metric_detail';
import { MetricsSettingsPage } from './settings';
import { HostsLandingPage } from './hosts/hosts_landing_page';
import { SourceLoadingPage } from '../../components/source_loading_page';
@@ -109,7 +109,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
)}
-
+
} />
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
index ef69c1d10406..de69f117540b 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
@@ -80,12 +80,16 @@ export const NodeContextPopover = ({
const nodeDetailMenuItemLinkProps = useLinkProps({
...getNodeDetailUrl({
- nodeType,
- nodeId: node.id,
+ assetType: nodeType,
+ assetId: node.id,
search: {
from: nodeDetailFrom,
to: currentTime,
- assetName: node.name,
+ ...(nodeType === 'host'
+ ? {
+ name: node.name,
+ }
+ : undefined),
},
}),
});
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
index fec97c5fc072..6f2a5c61346e 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
@@ -78,12 +78,16 @@ export const NodeContextMenu: React.FC = withTheme
const nodeDetailMenuItemLinkProps = useLinkProps({
...getNodeDetailUrl({
- nodeType,
- nodeId: node.id,
+ assetType: nodeType,
+ assetId: node.id,
search: {
from: nodeDetailFrom,
to: currentTime,
- assetName: node.name,
+ ...(nodeType === 'host'
+ ? {
+ name: node.name,
+ }
+ : undefined),
},
}),
});
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx
index 10af65db3fdd..d5ba376a514b 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx
@@ -5,53 +5,51 @@
* 2.0.
*/
-import React, { useMemo } from 'react';
-import { useLocation, useRouteMatch } from 'react-router-dom';
+import React from 'react';
+import { useRouteMatch } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
-import type { TimeRange } from '@kbn/es-query';
import { NoRemoteCluster } from '../../../components/empty_states';
import { SourceErrorPage } from '../../../components/source_error_page';
import { SourceLoadingPage } from '../../../components/source_loading_page';
import { useSourceContext } from '../../../containers/metrics_source';
-import { FlyoutTabIds, type Tab } from '../../../components/asset_details/types';
+import { ContentTabIds, type Tab } from '../../../components/asset_details/types';
import type { InventoryItemType } from '../../../../common/inventory_models/types';
import { AssetDetails } from '../../../components/asset_details/asset_details';
import { MetricsPageTemplate } from '../page_template';
-import { useMetricsTimeContext } from './hooks/use_metrics_time';
const orderedFlyoutTabs: Tab[] = [
{
- id: FlyoutTabIds.OVERVIEW,
+ id: ContentTabIds.OVERVIEW,
name: i18n.translate('xpack.infra.nodeDetails.tabs.overview.title', {
defaultMessage: 'Overview',
}),
},
{
- id: FlyoutTabIds.METADATA,
+ id: ContentTabIds.METADATA,
name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', {
defaultMessage: 'Metadata',
}),
},
{
- id: FlyoutTabIds.PROCESSES,
+ id: ContentTabIds.PROCESSES,
name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.processes', {
defaultMessage: 'Processes',
}),
},
{
- id: FlyoutTabIds.LOGS,
+ id: ContentTabIds.LOGS,
name: i18n.translate('xpack.infra.nodeDetails.tabs.logs.title', {
defaultMessage: 'Logs',
}),
},
{
- id: FlyoutTabIds.ANOMALIES,
+ id: ContentTabIds.ANOMALIES,
name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', {
defaultMessage: 'Anomalies',
}),
},
{
- id: FlyoutTabIds.OSQUERY,
+ id: ContentTabIds.OSQUERY,
name: i18n.translate('xpack.infra.nodeDetails.tabs.osquery', {
defaultMessage: 'Osquery',
}),
@@ -63,25 +61,6 @@ export const AssetDetailPage = () => {
const {
params: { type: nodeType, node: nodeId },
} = useRouteMatch<{ type: InventoryItemType; node: string }>();
- const { search } = useLocation();
-
- const assetName = useMemo(() => {
- const queryParams = new URLSearchParams(search);
- return queryParams.get('assetName') ?? undefined;
- }, [search]);
-
- const { timeRange } = useMetricsTimeContext();
-
- const dateRange: TimeRange = useMemo(
- () => ({
- from:
- typeof timeRange.from === 'number'
- ? new Date(timeRange.from).toISOString()
- : timeRange.from,
- to: typeof timeRange.to === 'number' ? new Date(timeRange.to).toISOString() : timeRange.to,
- }),
- [timeRange.from, timeRange.to]
- );
const { metricIndicesExist, remoteClustersExist } = source?.status ?? {};
@@ -110,10 +89,8 @@ export const AssetDetailPage = () => {
{
+export const NodeDetail = () => {
const {
params: { type: nodeType, node: nodeName },
} = useRouteMatch<{ type: InventoryItemType; node: string }>();
@@ -27,9 +27,13 @@ export const MetricDetail = () => {
return (
-
- {nodeType === 'host' ? : }
-
+ {nodeType === 'host' ? (
+
+ ) : (
+
+
+
+ )}
);
};
diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx
deleted file mode 100644
index 43009ad6b8ff..000000000000
--- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx
+++ /dev/null
@@ -1,21 +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 { EuiErrorBoundary } from '@elastic/eui';
-import React from 'react';
-import { MetricsTimeProvider } from './hooks/use_metrics_time';
-
-export const withMetricPageProviders =
- (Component: React.ComponentType) =>
- (props: T) =>
- (
-
-
-
-
-
- );
diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx
index 0888424c8f2d..f52a137b8792 100644
--- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx
@@ -105,12 +105,13 @@ export const MetricsExplorerChartContextMenu: React.FC = ({
: [];
const nodeType = source && options.groupBy && fieldToNodeType(source, options.groupBy);
+
const nodeDetailLinkProps = useLinkProps({
app: 'metrics',
...(nodeType
? getNodeDetailUrl({
- nodeType,
- nodeId: series.id,
+ assetType: nodeType,
+ assetId: series.id,
search: {
from: dateMathExpressionToEpoch(timeRange.from),
to: dateMathExpressionToEpoch(timeRange.to, true),
diff --git a/x-pack/plugins/observability/public/hooks/create_use_rules_link.ts b/x-pack/plugins/observability/public/hooks/create_use_rules_link.ts
index 5d50efa31a0b..c789fb73ae45 100644
--- a/x-pack/plugins/observability/public/hooks/create_use_rules_link.ts
+++ b/x-pack/plugins/observability/public/hooks/create_use_rules_link.ts
@@ -4,7 +4,15 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import { UseLinkPropsOptions, useLinkProps } from '@kbn/observability-shared-plugin/public';
+import {
+ UseLinkPropsOptions,
+ useLinkProps,
+ type LinkDescriptor,
+} from '@kbn/observability-shared-plugin/public';
+import rison from '@kbn/rison';
+import { useLocation } from 'react-router-dom';
+import useObservable from 'react-use/lib/useObservable';
+import { useKibana } from '../utils/kibana_react';
export const createUseRulesLink =
() =>
@@ -16,3 +24,43 @@ export const createUseRulesLink =
return useLinkProps(linkProps, options);
};
+
+export const crateInfraNodeDetailsLink =
+ ({
+ assetType,
+ assetId,
+ search,
+ }: {
+ assetType: 'host';
+ assetId: string;
+ search: LinkDescriptor['search'];
+ }) =>
+ (options: UseLinkPropsOptions = {}) => {
+ const location = useLocation();
+ const {
+ services: {
+ application: { currentAppId$ },
+ },
+ } = useKibana();
+
+ const appId = useObservable(currentAppId$);
+
+ const linkProps = {
+ app: 'metrics',
+ pathname: `link-to/${assetType}-detail/${assetId}`,
+ search: {
+ ...search,
+ ...(location.pathname
+ ? {
+ state: rison.encodeUnknown({
+ originAppId: appId,
+ originPathname: location.pathname,
+ originSearch: location.search,
+ }),
+ }
+ : undefined),
+ },
+ };
+
+ return useLinkProps(linkProps, options);
+ };
diff --git a/x-pack/plugins/observability/public/pages/overview/components/sections/metrics/host_link.tsx b/x-pack/plugins/observability/public/pages/overview/components/sections/metrics/host_link.tsx
index 91db16d08144..43ad5ff7e633 100644
--- a/x-pack/plugins/observability/public/pages/overview/components/sections/metrics/host_link.tsx
+++ b/x-pack/plugins/observability/public/pages/overview/components/sections/metrics/host_link.tsx
@@ -5,19 +5,37 @@
* 2.0.
*/
import React from 'react';
+import rison from '@kbn/rison';
+import { EuiLink } from '@elastic/eui';
+import { crateInfraNodeDetailsLink } from '../../../../../hooks/create_use_rules_link';
import { StringOrNull } from '../../../../..';
interface Props {
name: StringOrNull;
- id: StringOrNull;
+ id: string;
timerange: { from: number; to: number };
}
export function HostLink({ name, id, timerange }: Props) {
- const link = `../../app/metrics/link-to/host-detail/${id}?from=${timerange.from}&to=${timerange.to}`;
+ const linkProps = crateInfraNodeDetailsLink({
+ assetType: 'host',
+ assetId: id,
+ search: {
+ from: `${timerange.from}`,
+ to: `${timerange.to}`,
+ ...(!!name
+ ? {
+ assetDetails: rison.encodeUnknown({
+ name,
+ }),
+ }
+ : undefined),
+ },
+ })();
+
return (
- <>
- {name}
- >
+
+ {name}
+
);
}