[8.17] [Fleet] Fix agent dashboard link accross multiple space (#201280) (#201452)

# Backport

This will backport the following commits from `main` to `8.17`:
- [[Fleet] Fix agent dashboard link accross multiple space
(#201280)](https://github.com/elastic/kibana/pull/201280)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Nicolas
Chaulet","email":"nicolas.chaulet@elastic.co"},"sourceCommit":{"committedDate":"2024-11-22T18:00:22Z","message":"[Fleet]
Fix agent dashboard link accross multiple space
(#201280)","sha":"8e6698f6e70681d9bdb23ff88a2a76cdde3a38c0","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Fleet","v9.0.0","backport:prev-major"],"title":"[Fleet]
Fix agent dashboard link accross multiple
space","number":201280,"url":"https://github.com/elastic/kibana/pull/201280","mergeCommit":{"message":"[Fleet]
Fix agent dashboard link accross multiple space
(#201280)","sha":"8e6698f6e70681d9bdb23ff88a2a76cdde3a38c0"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/201280","number":201280,"mergeCommit":{"message":"[Fleet]
Fix agent dashboard link accross multiple space
(#201280)","sha":"8e6698f6e70681d9bdb23ff88a2a76cdde3a38c0"}}]}]
BACKPORT-->

Co-authored-by: Nicolas Chaulet <nicolas.chaulet@elastic.co>
This commit is contained in:
Kibana Machine 2024-11-23 06:46:07 +11:00 committed by GitHub
parent 142f3fa178
commit 4e05cb7979
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 147 additions and 24 deletions

View file

@ -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<typeof useGetPackageInfoByKeyQuery>);

View file

@ -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}`,

View file

@ -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<boolean>(false);
const [loading, setLoading] = React.useState<boolean>(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 = () => {
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="dashboardApp"
href={getDashboardHref(DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_OVERVIEW)}
href={getDashboardHref(elasticAgentOverviewDashboardId)}
data-test-subj="ingestOverviewLinkButton"
>
<FormattedMessage
@ -70,7 +90,7 @@ export const DashboardsButtons: React.FunctionComponent = () => {
<EuiFlexItem grow={false}>
<EuiButtonEmpty
iconType="dashboardApp"
href={getDashboardHref(DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_AGENT_INFO)}
href={getDashboardHref(elasticAgentInfoDashboardId)}
data-test-subj="agentInfoLinkButton"
>
<FormattedMessage

View file

@ -0,0 +1,38 @@
/*
* 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 type { GetInfoResponse } from '../../../../../../common';
import { getDashboardIdForSpace } from './dashboard_helpers';
const PKG_INFO = {
item: {
status: 'installed',
installationInfo: {
install_status: 'installed',
installed_kibana_space_id: 'default',
additional_spaces_installed_kibana: {
test: [
{
id: 'test-destination-1',
originId: 'test-id-1',
},
],
},
},
},
} as unknown as GetInfoResponse;
describe('getDashboardIdForSpace', () => {
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'));
});
});

View file

@ -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
);
}