From a4cb49799d9809492cf3537a5ace41487de70e5f Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:16:46 -0400 Subject: [PATCH] [8.14] [Infra][APM] Fix uptime links display condition in Infra and APM (#181425) (#181973) # Backport This will backport the following commits from `main` to `8.14`: - [[Infra][APM] Fix uptime links display condition in Infra and APM (#181425)](https://github.com/elastic/kibana/pull/181425) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Carlos Crespo --- .../src/lib/apm/apm_fields.ts | 1 + .../src/scenarios/simple_trace.ts | 3 + .../transaction_action_menu/sections.test.ts | 21 +++-- .../transaction_action_menu/sections.ts | 35 ++++----- .../transaction_action_menu.test.tsx | 11 +++ .../transaction_action_menu.tsx | 11 +-- .../components/waffle/node_context_menu.tsx | 7 +- .../inventory_view/lib/navigate_to_uptime.ts | 20 +++-- .../synthetics/public/apps/locators/index.ts | 2 - .../public/apps/locators/overview.ts | 50 ------------ .../public}/locators/overview.test.ts | 31 +++++--- .../uptime/public/locators/overview.ts | 78 +++++++++++++++++++ .../uptime/public/plugin.ts | 3 + 13 files changed, 167 insertions(+), 106 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/locators/overview.ts rename x-pack/plugins/observability_solution/{synthetics/public/apps => uptime/public}/locators/overview.test.ts (62%) create mode 100644 x-pack/plugins/observability_solution/uptime/public/locators/overview.ts diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts index d55d34cc1884..f968d710fe40 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts @@ -211,6 +211,7 @@ export type ApmFields = Fields<{ span: { id: string }; }>; 'url.original': string; + 'url.domain': string; }> & ApmApplicationMetricFields & ExperimentalFields; diff --git a/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts b/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts index 1d5ee6f53d63..fc03d49b9a17 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts @@ -33,6 +33,9 @@ const scenario: Scenario = async (runOptions) => { instance .transaction({ transactionName }) .timestamp(timestamp) + .defaults({ + 'url.domain': 'foo.bar', + }) .duration(1000) .success() .children( diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/sections.test.ts b/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/sections.test.ts index ec1c5e93321b..4660ac85f092 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/sections.test.ts +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/sections.test.ts @@ -10,24 +10,26 @@ import { IBasePath } from '@kbn/core/public'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { getSections } from './sections'; import { apmRouter as apmRouterBase, ApmRouter } from '../../routing/apm_route_config'; -import { - logsLocatorsMock, - observabilityLogsExplorerLocatorsMock, -} from '../../../context/apm_plugin/mock_apm_plugin_context'; +import { logsLocatorsMock } from '../../../context/apm_plugin/mock_apm_plugin_context'; +import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; const apmRouter = { ...apmRouterBase, link: (...args: [any]) => `some-basepath/app/apm${apmRouterBase.link(...args)}`, } as ApmRouter; -const { allDatasetsLocator } = observabilityLogsExplorerLocatorsMock; const { nodeLogsLocator, traceLogsLocator } = logsLocatorsMock; +const uptimeLocator = sharePluginMock.createLocator(); const expectLogsLocatorsToBeCalled = () => { expect(nodeLogsLocator.getRedirectUrl).toBeCalledTimes(3); expect(traceLogsLocator.getRedirectUrl).toBeCalledTimes(1); }; +const expectUptimeLocatorToBeCalled = () => { + expect(uptimeLocator.getRedirectUrl).toBeCalledTimes(1); +}; + describe('Transaction action menu', () => { const basePath = { prepend: (url: string) => { @@ -60,8 +62,8 @@ describe('Transaction action menu', () => { basePath, location, apmRouter, - allDatasetsLocator, logsLocators: logsLocatorsMock, + uptimeLocator, infraLinksAvailable: false, rangeFrom: 'now-24h', rangeTo: 'now', @@ -110,6 +112,7 @@ describe('Transaction action menu', () => { }, ], ]); + expectUptimeLocatorToBeCalled(); expectLogsLocatorsToBeCalled(); }); @@ -127,7 +130,7 @@ describe('Transaction action menu', () => { basePath, location, apmRouter, - allDatasetsLocator, + uptimeLocator, logsLocators: logsLocatorsMock, infraLinksAvailable: true, rangeFrom: 'now-24h', @@ -195,6 +198,7 @@ describe('Transaction action menu', () => { }, ], ]); + expectUptimeLocatorToBeCalled(); expectLogsLocatorsToBeCalled(); }); @@ -212,7 +216,7 @@ describe('Transaction action menu', () => { basePath, location, apmRouter, - allDatasetsLocator, + uptimeLocator, logsLocators: logsLocatorsMock, infraLinksAvailable: true, rangeFrom: 'now-24h', @@ -280,6 +284,7 @@ describe('Transaction action menu', () => { }, ], ]); + expectUptimeLocatorToBeCalled(); expectLogsLocatorsToBeCalled(); }); }); diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/sections.ts b/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/sections.ts index 4f3ffbe9602c..781a992e1f61 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/sections.ts +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/sections.ts @@ -10,18 +10,16 @@ import { Location } from 'history'; import { IBasePath } from '@kbn/core/public'; import { isEmpty, pickBy } from 'lodash'; import moment from 'moment'; -import url from 'url'; import type { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common'; import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common'; -import { LocatorPublic } from '@kbn/share-plugin/common'; -import { AllDatasetsLocatorParams } from '@kbn/deeplinks-observability/locators'; import type { ProfilingLocators } from '@kbn/observability-shared-plugin/public'; +import { LocatorPublic } from '@kbn/share-plugin/common'; +import { SerializableRecord } from '@kbn/utility-types'; import { Environment } from '../../../../common/environment_rt'; import type { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { getDiscoverHref } from '../links/discover_links/discover_link'; import { getDiscoverQuery } from '../links/discover_links/discover_transaction_link'; import { getInfraHref } from '../links/infra_link'; -import { fromQuery } from '../links/url_helpers'; import { SectionRecord, getNonEmptySections, Action } from './sections_helper'; import { HOST_NAME, TRACE_ID } from '../../../../common/es_fields/apm'; import { ApmRouter } from '../../routing/apm_route_config'; @@ -42,11 +40,11 @@ export const getSections = ({ location, apmRouter, infraLinksAvailable, + uptimeLocator, profilingLocators, rangeFrom, rangeTo, environment, - allDatasetsLocator, logsLocators, dataViewId, }: { @@ -55,11 +53,11 @@ export const getSections = ({ location: Location; apmRouter: ApmRouter; infraLinksAvailable: boolean; + uptimeLocator?: LocatorPublic; profilingLocators?: ProfilingLocators; rangeFrom: string; rangeTo: string; environment: Environment; - allDatasetsLocator: LocatorPublic; logsLocators: ReturnType; dataViewId?: string; }) => { @@ -72,19 +70,16 @@ export const getSections = ({ const time = Math.round(transaction.timestamp.us / 1000); const infraMetricsQuery = getInfraMetricsQuery(transaction); - const uptimeLink = url.format({ - pathname: basePath.prepend('/app/uptime'), - search: `?${fromQuery( - pickBy( - { - dateRangeStart: rangeFrom, - dateRangeEnd: rangeTo, - search: `url.domain:"${transaction.url?.domain}"`, - }, - (val) => !isEmpty(val) - ) - )}`, - }); + const uptimeLink = uptimeLocator?.getRedirectUrl( + pickBy( + { + dateRangeStart: rangeFrom, + dateRangeEnd: rangeTo, + search: `url.domain:"${transaction.url?.domain}"`, + }, + (val) => !isEmpty(val) + ) + ); // Logs hrefs const podLogsHref = logsLocators.nodeLogsLocator.getRedirectUrl({ @@ -231,7 +226,7 @@ export const getSections = ({ defaultMessage: 'Status', }), href: uptimeLink, - condition: !!transaction.url?.domain, + condition: !!transaction.url?.domain && !!uptimeLink, }, ]; diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx index c1dda85e8ae7..29e5da684216 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx @@ -30,6 +30,8 @@ import * as Transactions from './__fixtures__/mock_data'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import * as useAdHocApmDataView from '../../../hooks/use_adhoc_apm_data_view'; import { useProfilingIntegrationSetting } from '../../../hooks/use_profiling_integration_setting'; +import { uptimeOverviewLocatorID } from '@kbn/observability-plugin/common'; +import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; const apmContextMock = { ...mockApmPluginContextValue, @@ -52,6 +54,15 @@ const apmContextMock = { if (id === TRACE_LOGS_LOCATOR_ID) { return logsLocatorsMock.traceLogsLocator; } + if (id === uptimeOverviewLocatorID) { + return { + ...sharePluginMock.createLocator(), + getRedirectUrl: jest.fn( + () => + 'http://localhost/basepath/app/uptime?dateRangeStart=now-24h&dateRangeEnd=now&search=url.domain:%22example.com%22' + ), + }; + } }, }, }, diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx index aa73c9c5eb6f..05aaa7ccdad7 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/transaction_action_menu/transaction_action_menu.tsx @@ -21,12 +21,9 @@ import { import React, { useState } from 'react'; import { useLocation } from 'react-router-dom'; import useAsync from 'react-use/lib/useAsync'; -import { - AllDatasetsLocatorParams, - ALL_DATASETS_LOCATOR_ID, -} from '@kbn/deeplinks-observability/locators'; import type { ProfilingLocators } from '@kbn/observability-shared-plugin/public'; import { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common'; +import { uptimeOverviewLocatorID } from '@kbn/observability-plugin/common'; import { useAnyOfApmParams } from '../../../hooks/use_apm_params'; import { ApmFeatureFlagName } from '../../../../common/apm_feature_flags'; import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; @@ -124,10 +121,10 @@ function ActionMenuSections({ const apmRouter = useApmRouter(); const { dataView } = useAdHocApmDataView(); - const allDatasetsLocator = - share.url.locators.get(ALL_DATASETS_LOCATOR_ID)!; const logsLocators = getLogsLocatorsFromUrlService(share.url); + const uptimeLocator = share.url.locators.get(uptimeOverviewLocatorID); + const infraLinksAvailable = useApmFeatureFlag(ApmFeatureFlagName.InfraUiAvailable); const { @@ -145,11 +142,11 @@ function ActionMenuSections({ location, apmRouter, infraLinksAvailable, + uptimeLocator, profilingLocators, rangeFrom, rangeTo, environment, - allDatasetsLocator, logsLocators, dataViewId: dataView?.id, }); 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 447b0d52cddd..e95314668e80 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 @@ -24,6 +24,7 @@ import { import { findInventoryModel, findInventoryFields } from '@kbn/metrics-data-access-plugin/common'; import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { getLogsLocatorsFromUrlService } from '@kbn/logs-shared-plugin/common'; +import { uptimeOverviewLocatorID } from '@kbn/observability-plugin/common'; import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; import { AlertFlyout } from '../../../../../alerting/inventory/components/alert_flyout'; import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../../lib/lib'; @@ -46,6 +47,7 @@ export const NodeContextMenu: React.FC = withTheme const { services } = useKibanaContextForPlugin(); const { application, share } = services; const { nodeLogsLocator } = getLogsLocatorsFromUrlService(share.url); + const uptimeLocator = share.url.locators.get(uptimeOverviewLocatorID); const uiCapabilities = application?.capabilities; // Due to the changing nature of the fields between APM and this UI, // We need to have some exceptions until 7.0 & ECS is finalized. Reference @@ -60,7 +62,8 @@ export const NodeContextMenu: React.FC = withTheme inventoryModel.crosslinkSupport.apm && uiCapabilities?.apm && uiCapabilities?.apm.show; const showUptimeLink = inventoryModel.crosslinkSupport.uptime && - (['pod', 'container'].includes(nodeType) || node.ip); + (['pod', 'container'].includes(nodeType) || node.ip) && + !!uptimeLocator; const showCreateAlertLink = uiCapabilities?.infrastructure?.save; const inventoryId = useMemo(() => { @@ -144,7 +147,7 @@ export const NodeContextMenu: React.FC = withTheme defaultMessage: '{inventoryName} in Uptime', values: { inventoryName: inventoryModel.singularDisplayName }, }), - onClick: () => navigateToUptime(share.url.locators, nodeType, node), + onClick: () => (showUptimeLink ? navigateToUptime({ uptimeLocator, nodeType, node }) : {}), isDisabled: !showUptimeLink, }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/navigate_to_uptime.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/navigate_to_uptime.ts index 6996bbd50fe3..75a824b2bf57 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/navigate_to_uptime.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/lib/navigate_to_uptime.ts @@ -5,15 +5,19 @@ * 2.0. */ -import { uptimeOverviewLocatorID } from '@kbn/observability-plugin/public'; -import { LocatorClient } from '@kbn/share-plugin/common/url_service/locators'; +import { LocatorPublic } from '@kbn/share-plugin/common/url_service/locators'; import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; +import { SerializableRecord } from '@kbn/utility-types'; import { InfraWaffleMapNode } from '../../../../lib/lib'; -export const navigateToUptime = ( - locators: LocatorClient, - nodeType: InventoryItemType, - node: InfraWaffleMapNode -) => { - return locators.get(uptimeOverviewLocatorID)!.navigate({ [nodeType]: node.id, ip: node.ip }); +export const navigateToUptime = ({ + uptimeLocator, + nodeType, + node, +}: { + uptimeLocator: LocatorPublic; + nodeType: InventoryItemType; + node: InfraWaffleMapNode; +}) => { + return uptimeLocator.navigate({ [nodeType]: node.id, ip: node.ip }); }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/locators/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/locators/index.ts index c96858d1cc9d..4e09cc7bc297 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/locators/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/locators/index.ts @@ -6,13 +6,11 @@ */ import { LocatorPublic } from '@kbn/share-plugin/public'; import { SerializableRecord } from '@kbn/utility-types'; -import { uptimeOverviewNavigatorParams } from './overview'; import { monitorDetailNavigatorParams } from './monitor_detail'; import { editMonitorNavigatorParams } from './edit_monitor'; import { syntheticsSettingsNavigatorParams } from './settings'; export const locators: Array, 'id' | 'getLocation'>> = [ - uptimeOverviewNavigatorParams, monitorDetailNavigatorParams, editMonitorNavigatorParams, syntheticsSettingsNavigatorParams, diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/locators/overview.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/locators/overview.ts deleted file mode 100644 index 785668d8e8c7..000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/locators/overview.ts +++ /dev/null @@ -1,50 +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 { uptimeOverviewLocatorID } from '@kbn/observability-plugin/public'; -import { OVERVIEW_ROUTE } from '../../../common/constants/ui'; - -const formatSearchKey = (key: string, value: string) => `${key}: "${value}"`; - -async function navigate({ - ip, - host, - container, - pod, -}: { - ip?: string; - host?: string; - container?: string; - pod?: string; -}) { - const searchParams: string[] = []; - - if (host) searchParams.push(formatSearchKey('host.name', host)); - if (container) searchParams.push(formatSearchKey('container.id', container)); - if (pod) searchParams.push(formatSearchKey('kubernetes.pod.uid', pod)); - - if (ip) { - searchParams.push(formatSearchKey(`host.ip`, ip)); - searchParams.push(formatSearchKey(`monitor.ip`, ip)); - } - - const searchString = searchParams.join(' OR '); - - const path = - searchParams.length === 0 ? OVERVIEW_ROUTE : OVERVIEW_ROUTE + `?search=${searchString}`; - - return { - app: 'uptime', - path, - state: {}, - }; -} - -export const uptimeOverviewNavigatorParams = { - id: uptimeOverviewLocatorID, - getLocation: navigate, -}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/locators/overview.test.ts b/x-pack/plugins/observability_solution/uptime/public/locators/overview.test.ts similarity index 62% rename from x-pack/plugins/observability_solution/synthetics/public/apps/locators/overview.test.ts rename to x-pack/plugins/observability_solution/uptime/public/locators/overview.test.ts index a8ca60077bdf..6487fd052e45 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/locators/overview.test.ts +++ b/x-pack/plugins/observability_solution/uptime/public/locators/overview.test.ts @@ -5,34 +5,36 @@ * 2.0. */ -import { OVERVIEW_ROUTE } from '../../../common/constants'; -import { uptimeOverviewNavigatorParams } from './overview'; +import { OVERVIEW_ROUTE } from '../../common/constants'; +import { UptimeOverviewLocatorDefinition } from './overview'; describe('uptimeOverviewNavigatorParams', () => { + const uptimeOverviewNavigator = new UptimeOverviewLocatorDefinition(); + it('supplies the correct app name', async () => { - const location = await uptimeOverviewNavigatorParams.getLocation({}); + const location = await uptimeOverviewNavigator.getLocation({}); expect(location.app).toEqual('uptime'); }); it('creates the expected path when no params specified', async () => { - const location = await uptimeOverviewNavigatorParams.getLocation({}); + const location = await uptimeOverviewNavigator.getLocation({}); expect(location.path).toEqual(OVERVIEW_ROUTE); }); it('creates a path with expected search when ip is specified', async () => { - const location = await uptimeOverviewNavigatorParams.getLocation({ ip: '127.0.0.1' }); + const location = await uptimeOverviewNavigator.getLocation({ ip: '127.0.0.1' }); expect(location.path).toEqual( `${OVERVIEW_ROUTE}?search=host.ip: "127.0.0.1" OR monitor.ip: "127.0.0.1"` ); }); it('creates a path with expected search when hostname is specified', async () => { - const location = await uptimeOverviewNavigatorParams.getLocation({ host: 'elastic.co' }); + const location = await uptimeOverviewNavigator.getLocation({ host: 'elastic.co' }); expect(location.path).toEqual(`${OVERVIEW_ROUTE}?search=host.name: "elastic.co"`); }); it('creates a path with expected search when multiple host keys are specified', async () => { - const location = await uptimeOverviewNavigatorParams.getLocation({ + const location = await uptimeOverviewNavigator.getLocation({ host: 'elastic.co', ip: '127.0.0.1', }); @@ -42,7 +44,7 @@ describe('uptimeOverviewNavigatorParams', () => { }); it('creates a path with expected search when multiple kubernetes pod is specified', async () => { - const location = await uptimeOverviewNavigatorParams.getLocation({ + const location = await uptimeOverviewNavigator.getLocation({ pod: 'foo', ip: '10.0.0.1', }); @@ -52,9 +54,20 @@ describe('uptimeOverviewNavigatorParams', () => { }); it('creates a path with expected search when docker container is specified', async () => { - const location = await uptimeOverviewNavigatorParams.getLocation({ + const location = await uptimeOverviewNavigator.getLocation({ container: 'foo', }); expect(location.path).toEqual(`${OVERVIEW_ROUTE}?search=container.id: "foo"`); }); + + it('creates a path with expected search and date range', async () => { + const location = await uptimeOverviewNavigator.getLocation({ + search: 'foo', + dateRangeStart: 'now-15m', + dateRangeEnd: 'now', + }); + expect(location.path).toEqual( + `${OVERVIEW_ROUTE}?search=foo&dateRangeStart=now-15m&dateRangeEnd=now` + ); + }); }); diff --git a/x-pack/plugins/observability_solution/uptime/public/locators/overview.ts b/x-pack/plugins/observability_solution/uptime/public/locators/overview.ts new file mode 100644 index 000000000000..15a1c52406eb --- /dev/null +++ b/x-pack/plugins/observability_solution/uptime/public/locators/overview.ts @@ -0,0 +1,78 @@ +/* + * 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 { uptimeOverviewLocatorID } from '@kbn/observability-plugin/public'; +import type { LocatorDefinition } from '@kbn/share-plugin/common'; +import type { SerializableRecord } from '@kbn/utility-types'; +import { OVERVIEW_ROUTE } from '../../common/constants'; + +const formatSearchKey = (key: string, value: string) => `${key}: "${value}"`; + +export interface UptimeOverviewLocatorInfraParams extends SerializableRecord { + ip?: string; + host?: string; + container?: string; + pod?: string; +} + +export interface UptimeOverviewLocatorParams extends SerializableRecord { + dateRangeStart?: string; + dateRangeEnd?: string; + search?: string; +} + +function isUptimeOverviewLocatorParams( + args: UptimeOverviewLocatorInfraParams | UptimeOverviewLocatorParams +): args is UptimeOverviewLocatorParams { + return ( + (args as UptimeOverviewLocatorParams).search !== undefined || + (args as UptimeOverviewLocatorParams).dateRangeEnd !== undefined || + (args as UptimeOverviewLocatorParams).dateRangeStart !== undefined + ); +} + +export class UptimeOverviewLocatorDefinition + implements LocatorDefinition +{ + public readonly id = uptimeOverviewLocatorID; + + public readonly getLocation = async ( + params: UptimeOverviewLocatorInfraParams | UptimeOverviewLocatorParams + ) => { + let qs = ''; + + if (isUptimeOverviewLocatorParams(params)) { + qs = Object.entries(params) + .map(([key, value]) => { + if (value) { + return `${key}=${value}`; + } + }) + .join('&'); + } else { + const searchParams: string[] = []; + if (params.host) searchParams.push(formatSearchKey('host.name', params.host)); + if (params.container) searchParams.push(formatSearchKey('container.id', params.container)); + if (params.pod) searchParams.push(formatSearchKey('kubernetes.pod.uid', params.pod)); + if (params.ip) { + searchParams.push(formatSearchKey(`host.ip`, params.ip)); + searchParams.push(formatSearchKey(`monitor.ip`, params.ip)); + } + if (searchParams.length > 0) { + qs = `search=${searchParams.join(' OR ')}`; + } + } + + const path = `${OVERVIEW_ROUTE}${qs ? `?${qs}` : ''}`; + + return { + app: 'uptime', + path, + state: {}, + }; + }; +} diff --git a/x-pack/plugins/observability_solution/uptime/public/plugin.ts b/x-pack/plugins/observability_solution/uptime/public/plugin.ts index a94fdcad3ba7..1e0c967d0ef2 100644 --- a/x-pack/plugins/observability_solution/uptime/public/plugin.ts +++ b/x-pack/plugins/observability_solution/uptime/public/plugin.ts @@ -241,6 +241,9 @@ export class UptimePlugin function registerUptimeRoutesWithNavigation(coreStart: CoreStart, plugins: ClientPluginsStart) { async function getUptimeSections() { if (coreStart.application.capabilities.uptime?.show) { + const { UptimeOverviewLocatorDefinition } = await import('./locators/overview'); + plugins.share.url.locators.create(new UptimeOverviewLocatorDefinition()); + return [ { label: 'Uptime',