From c7443abaaf7c9408c2c4306f79e9e8241ac79370 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Fri, 22 Nov 2024 12:00:22 -0600 Subject: [PATCH] [Fleet] Fix agent dashboard link accross multiple space (#201280) (cherry picked from commit 8e6698f6e70681d9bdb23ff88a2a76cdde3a38c0) --- .../components/agent_dashboard_link.test.tsx | 7 ++- .../components/agent_dashboard_link.tsx | 34 ++++++++++- .../components/dashboards_buttons.tsx | 60 ++++++++++++------- .../agents/services/dashboard_helper.test.ts | 38 ++++++++++++ .../agents/services/dashboard_helpers.ts | 32 ++++++++++ 5 files changed, 147 insertions(+), 24 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/dashboard_helper.test.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/dashboard_helpers.ts diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.test.tsx index 79908819ff863..03f7b2b33a83c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.test.tsx @@ -21,6 +21,7 @@ jest.mock('../../../../../../hooks/use_fleet_status', () => ({ FleetStatusProvider: (props: any) => { return props.children; }, + useFleetStatus: jest.fn().mockReturnValue({ spaceId: 'default' }), })); jest.mock('../../../../../../hooks/use_request/epm'); @@ -30,7 +31,7 @@ jest.mock('../../../../../../hooks/use_locator', () => { useDashboardLocator: jest.fn().mockImplementation(() => { return { id: 'DASHBOARD_APP_LOCATOR', - getRedirectUrl: jest.fn().mockResolvedValue('app/dashboards#/view/elastic_agent-a0001'), + getRedirectUrl: jest.fn().mockReturnValue('app/dashboards#/view/elastic_agent-a0001'), }; }), }; @@ -43,6 +44,10 @@ describe('AgentDashboardLink', () => { data: { item: { status: 'installed', + installationInfo: { + install_status: 'installed', + installed_kibana_space_id: 'default', + }, }, }, } as ReturnType); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.tsx index 6832f81961ddb..c6a7c6b1a7d43 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_dashboard_link.tsx @@ -10,21 +10,49 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton, EuiToolTip } from '@elastic/eui'; import styled from 'styled-components'; -import { useGetPackageInfoByKeyQuery, useLink, useDashboardLocator } from '../../../../hooks'; +import type { GetInfoResponse } from '../../../../../../../common/types'; +import { + useGetPackageInfoByKeyQuery, + useLink, + useDashboardLocator, + useFleetStatus, +} from '../../../../hooks'; import type { Agent, AgentPolicy } from '../../../../types'; import { FLEET_ELASTIC_AGENT_PACKAGE, DASHBOARD_LOCATORS_IDS, } from '../../../../../../../common/constants'; +import { getDashboardIdForSpace } from '../../services/dashboard_helpers'; + +function isKibanaAssetsInstalledInSpace(spaceId: string | undefined, res?: GetInfoResponse) { + if (res?.item?.status !== 'installed') { + return false; + } + + const installationInfo = res.item.installationInfo; + + if (!installationInfo || installationInfo.install_status !== 'installed') { + return false; + } + return ( + installationInfo.installed_kibana_space_id === spaceId || + (spaceId && installationInfo.additional_spaces_installed_kibana?.[spaceId]) + ); +} function useAgentDashboardLink(agent: Agent) { const { isLoading, data } = useGetPackageInfoByKeyQuery(FLEET_ELASTIC_AGENT_PACKAGE); + const { spaceId } = useFleetStatus(); - const isInstalled = data?.item.status === 'installed'; + const isInstalled = isKibanaAssetsInstalledInSpace(spaceId, data); const dashboardLocator = useDashboardLocator(); const link = dashboardLocator?.getRedirectUrl({ - dashboardId: DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_AGENT_METRICS, + dashboardId: getDashboardIdForSpace( + spaceId, + data, + DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_AGENT_METRICS + ), query: { language: 'kuery', query: `elastic_agent.id:${agent.id}`, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/dashboards_buttons.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/dashboards_buttons.tsx index 25c394e2606b0..3e50258071576 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/dashboards_buttons.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/dashboards_buttons.tsx @@ -5,49 +5,69 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { useQuery } from '@tanstack/react-query'; -import { DASHBOARD_LOCATORS_IDS } from '../../../../../../../common/constants'; +import { + DASHBOARD_LOCATORS_IDS, + FLEET_ELASTIC_AGENT_PACKAGE, +} from '../../../../../../../common/constants'; -import { useDashboardLocator, useStartServices } from '../../../../hooks'; +import { + useDashboardLocator, + useFleetStatus, + useGetPackageInfoByKeyQuery, + useStartServices, +} from '../../../../hooks'; + +import { getDashboardIdForSpace } from '../../services/dashboard_helpers'; const useDashboardExists = (dashboardId: string) => { - const [dashboardExists, setDashboardExists] = React.useState(false); - const [loading, setLoading] = React.useState(true); const { dashboard: dashboardPlugin } = useStartServices(); - useEffect(() => { - const fetchDashboard = async () => { + const { data, isLoading } = useQuery({ + queryKey: ['dashboard_exists', dashboardId], + queryFn: async () => { try { const findDashboardsService = await dashboardPlugin.findDashboardsService(); const [dashboard] = await findDashboardsService.findByIds([dashboardId]); - setLoading(false); - setDashboardExists(dashboard?.status === 'success'); + return dashboard?.status === 'success'; } catch (e) { - setLoading(false); - setDashboardExists(false); + return false; } - }; - - fetchDashboard(); - }, [dashboardId, dashboardPlugin]); - - return { dashboardExists, loading }; + }, + }); + return { dashboardExists: data ?? false, loading: isLoading }; }; export const DashboardsButtons: React.FunctionComponent = () => { + const { data } = useGetPackageInfoByKeyQuery(FLEET_ELASTIC_AGENT_PACKAGE); + const { spaceId } = useFleetStatus(); + const dashboardLocator = useDashboardLocator(); const getDashboardHref = (dashboardId: string) => { return dashboardLocator?.getRedirectUrl({ dashboardId }) || ''; }; - const { dashboardExists, loading: dashboardLoading } = useDashboardExists( + const elasticAgentOverviewDashboardId = getDashboardIdForSpace( + spaceId, + data, DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_OVERVIEW ); + const elasticAgentInfoDashboardId = getDashboardIdForSpace( + spaceId, + data, + DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_AGENT_INFO + ); + + const { dashboardExists, loading: dashboardLoading } = useDashboardExists( + elasticAgentOverviewDashboardId + ); + if (dashboardLoading || !dashboardExists) { return null; } @@ -58,7 +78,7 @@ export const DashboardsButtons: React.FunctionComponent = () => { { { + it('return the same id if package is installed in the same space', () => { + expect(() => getDashboardIdForSpace('default', PKG_INFO, 'test-id-1')); + }); + + it('return the destination ID if package is installed in an additionnal space', () => { + expect(() => getDashboardIdForSpace('test', PKG_INFO, 'test-id-1')); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/dashboard_helpers.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/dashboard_helpers.ts new file mode 100644 index 0000000000000..bc46118b93fe3 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/services/dashboard_helpers.ts @@ -0,0 +1,32 @@ +/* + * 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 { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; + +import type { GetInfoResponse } from '../../../../../../common'; + +export function getDashboardIdForSpace( + spaceId: string = DEFAULT_SPACE_ID, + res: GetInfoResponse | undefined, + dashboardId: string +) { + if (res?.item?.status !== 'installed') { + return dashboardId; + } + + const installationInfo = res.item.installationInfo; + + if (!installationInfo || installationInfo?.installed_kibana_space_id === spaceId) { + return dashboardId; + } + + return ( + installationInfo.additional_spaces_installed_kibana?.[spaceId]?.find( + ({ originId }) => originId === dashboardId + )?.id ?? dashboardId + ); +}