diff --git a/x-pack/plugins/fleet/common/constants/epm.ts b/x-pack/plugins/fleet/common/constants/epm.ts index f380d66d0223c..a1d73b452cf72 100644 --- a/x-pack/plugins/fleet/common/constants/epm.ts +++ b/x-pack/plugins/fleet/common/constants/epm.ts @@ -22,9 +22,6 @@ export const FLEET_CLOUD_SECURITY_POSTURE_KSPM_POLICY_TEMPLATE = 'kspm'; export const PACKAGE_TEMPLATE_SUFFIX = '@package'; export const USER_SETTINGS_TEMPLATE_SUFFIX = '@custom'; -export const FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID = - 'elastic_agent-f47f18cc-9c7d-4278-b2ea-a6dee816d395'; - export const DATASET_VAR_NAME = 'data_stream.dataset'; /* Package rules: diff --git a/x-pack/plugins/fleet/common/constants/index.ts b/x-pack/plugins/fleet/common/constants/index.ts index 4dcc2d58d65ba..9892c883805f7 100644 --- a/x-pack/plugins/fleet/common/constants/index.ts +++ b/x-pack/plugins/fleet/common/constants/index.ts @@ -20,6 +20,7 @@ export * from './fleet_server_policy_config'; export * from './authz'; export * from './file_storage'; export * from './message_signing_keys'; +export * from './locators'; // TODO: This is the default `index.max_result_window` ES setting, which dictates // the maximum amount of results allowed to be returned from a search. It's possible diff --git a/x-pack/plugins/fleet/common/constants/locators.ts b/x-pack/plugins/fleet/common/constants/locators.ts new file mode 100644 index 0000000000000..daa00bcae46e0 --- /dev/null +++ b/x-pack/plugins/fleet/common/constants/locators.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +export const LOCATORS_IDS = { + APM_LOCATOR: 'APM_LOCATOR', + DASHBOARD_APP: 'DASHBOARD_APP_LOCATOR', +} as const; + +// Dashboards ids +export const DASHBOARD_LOCATORS_IDS = { + ELASTIC_AGENT_OVERVIEW: 'elastic_agent-a148dc70-6b3c-11ed-98de-67bdecd21824', + ELASTIC_AGENT_AGENT_INFO: 'elastic_agent-0600ffa0-6b5e-11ed-98de-67bdecd21824', + ELASTIC_AGENT_AGENT_METRICS: 'elastic_agent-f47f18cc-9c7d-4278-b2ea-a6dee816d395', + ELASTIC_AGENT_INTEGRATIONS: 'elastic_agent-1a4e7280-6b5e-11ed-98de-67bdecd21824', +} as const; diff --git a/x-pack/plugins/fleet/common/index.ts b/x-pack/plugins/fleet/common/index.ts index 3cdfa354a8c5f..3ca552966a7c5 100644 --- a/x-pack/plugins/fleet/common/index.ts +++ b/x-pack/plugins/fleet/common/index.ts @@ -52,6 +52,8 @@ export { // Statuses // Authz ENDPOINT_PRIVILEGES, + // dashboards ids + DASHBOARD_LOCATORS_IDS, } from './constants'; export { // Route services 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 7a65be0a460c9..79908819ff863 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 @@ -25,6 +25,17 @@ jest.mock('../../../../../../hooks/use_fleet_status', () => ({ jest.mock('../../../../../../hooks/use_request/epm'); +jest.mock('../../../../../../hooks/use_locator', () => { + return { + useDashboardLocator: jest.fn().mockImplementation(() => { + return { + id: 'DASHBOARD_APP_LOCATOR', + getRedirectUrl: jest.fn().mockResolvedValue('app/dashboards#/view/elastic_agent-a0001'), + }; + }), + }; +}); + describe('AgentDashboardLink', () => { it('should enable the button if elastic_agent package is installed and policy has monitoring enabled', async () => { mockedUseGetPackageInfoByKeyQuery.mockReturnValue({ 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 6c7479fd8c801..6832f81961ddb 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,26 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton, EuiToolTip } from '@elastic/eui'; import styled from 'styled-components'; -import { useGetPackageInfoByKeyQuery, useKibanaLink, useLink } from '../../../../hooks'; +import { useGetPackageInfoByKeyQuery, useLink, useDashboardLocator } from '../../../../hooks'; import type { Agent, AgentPolicy } from '../../../../types'; import { FLEET_ELASTIC_AGENT_PACKAGE, - FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID, + DASHBOARD_LOCATORS_IDS, } from '../../../../../../../common/constants'; function useAgentDashboardLink(agent: Agent) { const { isLoading, data } = useGetPackageInfoByKeyQuery(FLEET_ELASTIC_AGENT_PACKAGE); const isInstalled = data?.item.status === 'installed'; + const dashboardLocator = useDashboardLocator(); - const dashboardLink = useKibanaLink(`/dashboard/${FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID}`); - const query = `_a=(query:(language:kuery,query:'elastic_agent.id:${agent.id}'))`; - const link = `${dashboardLink}?${query}`; + const link = dashboardLocator?.getRedirectUrl({ + dashboardId: DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_AGENT_METRICS, + query: { + language: 'kuery', + query: `elastic_agent.id:${agent.id}`, + }, + }); return { isLoading, @@ -50,7 +55,12 @@ export const AgentDashboardLink: React.FunctionComponent<{ !isInstalled || isLoading || !isLogAndMetricsEnabled ? { disabled: true } : { href: link }; const button = ( - + { + const dashboardLocator = useDashboardLocator(); + + const getDashboardHref = (dashboardId: string) => { + return dashboardLocator?.getRedirectUrl({ dashboardId }) || ''; + }; + + return ( + <> + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx index e239f31ed3adc..9af04b04761a6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.test.tsx @@ -36,6 +36,17 @@ jest.mock('../../../../components', () => { }; }); +jest.mock('../../../../../../hooks/use_locator', () => { + return { + useDashboardLocator: jest.fn().mockImplementation(() => { + return { + id: 'DASHBOARD_APP_LOCATOR', + getRedirectUrl: jest.fn().mockResolvedValue('app/dashboards#/view/elastic_agent-a0002'), + }; + }), + }; +}); + const TestComponent = (props: any) => ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx index 4ca43020e132a..c9c527ba36e0a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/search_and_filter_bar.tsx @@ -31,15 +31,12 @@ import { AgentBulkActions } from './bulk_actions'; import type { SelectionMode } from './types'; import { AgentActivityButton } from './agent_activity_button'; import { AgentStatusFilter } from './agent_status_filter'; +import { DashboardsButtons } from './dashboards_buttons'; const ClearAllTagsFilterItem = styled(EuiFilterSelectItem)` padding: ${(props) => props.theme.eui.euiSizeS}; `; -const FlexEndEuiFlexItem = styled(EuiFlexItem)` - align-self: flex-end; -`; - export const SearchAndFilterBar: React.FunctionComponent<{ agentPolicies: AgentPolicy[]; draftKuery: string; @@ -118,17 +115,18 @@ export const SearchAndFilterBar: React.FunctionComponent<{ return ( <> - {/* Search and filter bar */} - - - + {/* Top Buttons and Links */} + + {totalAgents > 0 && } + + - + - + - + + {/* Search and filters */} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/list_page/components/data_stream_row_actions.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/list_page/components/data_stream_row_actions.tsx index 7d88327e73fd6..8822117f56d67 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/list_page/components/data_stream_row_actions.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/data_stream/list_page/components/data_stream_row_actions.tsx @@ -10,13 +10,15 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { DataStream } from '../../../../types'; -import { useKibanaLink } from '../../../../hooks'; +import { useDashboardLocator } from '../../../../hooks'; import { ContextMenuActions } from '../../../../components'; import { useAPMServiceDetailHref } from '../../../../hooks/use_apm_service_href'; export const DataStreamRowActions = memo<{ datastream: DataStream }>(({ datastream }) => { const { dashboards } = datastream; + const dashboardLocator = useDashboardLocator(); + const actionNameSingular = ( (({ datastre items: [ { icon: 'dashboardApp', - /* eslint-disable-next-line react-hooks/rules-of-hooks */ - href: useKibanaLink(`/dashboard/${dashboards[0].id || ''}`), + href: dashboardLocator?.getRedirectUrl({ dashboardId: dashboards[0]?.id } || ''), name: actionNameSingular, }, ], @@ -109,8 +110,7 @@ export const DataStreamRowActions = memo<{ datastream: DataStream }>(({ datastre items: dashboards.map((dashboard) => { return { icon: 'dashboardApp', - /* eslint-disable-next-line react-hooks/rules-of-hooks */ - href: useKibanaLink(`/dashboard/${dashboard.id || ''}`), + href: dashboardLocator?.getRedirectUrl({ dashboardId: dashboard?.id } || ''), name: dashboard.title, }; }), diff --git a/x-pack/plugins/fleet/public/constants/index.ts b/x-pack/plugins/fleet/public/constants/index.ts index 22ba08f3f9a1a..2c682af1fcbcc 100644 --- a/x-pack/plugins/fleet/public/constants/index.ts +++ b/x-pack/plugins/fleet/public/constants/index.ts @@ -22,6 +22,7 @@ export { AUTO_UPDATE_PACKAGES, KEEP_POLICIES_UP_TO_DATE_PACKAGES, AUTO_UPGRADE_POLICIES_PACKAGES, + LOCATORS_IDS, } from '../../common/constants'; export * from './page_paths'; @@ -37,7 +38,3 @@ export const DURATION_APM_SETTINGS_VARS = { TAIL_SAMPLING_INTERVAL: 'tail_sampling_interval', WRITE_TIMEOUT: 'write_timeout', }; - -export const LOCATORS_IDS = { - APM_LOCATOR: 'APM_LOCATOR', -} as const; diff --git a/x-pack/plugins/fleet/public/hooks/index.ts b/x-pack/plugins/fleet/public/hooks/index.ts index 0f7f6b2f4d165..a9fb6ef7758c7 100644 --- a/x-pack/plugins/fleet/public/hooks/index.ts +++ b/x-pack/plugins/fleet/public/hooks/index.ts @@ -31,3 +31,4 @@ export * from './use_flyout_context'; export * from './use_is_guided_onboarding_active'; export * from './use_fleet_server_hosts_for_policy'; export * from './use_fleet_server_standalone'; +export * from './use_locator'; diff --git a/x-pack/plugins/fleet/public/hooks/use_locator.ts b/x-pack/plugins/fleet/public/hooks/use_locator.ts index 46ec3e4c75d13..a3fed97679456 100644 --- a/x-pack/plugins/fleet/public/hooks/use_locator.ts +++ b/x-pack/plugins/fleet/public/hooks/use_locator.ts @@ -7,7 +7,7 @@ import type { SerializableRecord } from '@kbn/utility-types'; import type { ValuesType } from 'utility-types'; -import type { LOCATORS_IDS } from '../constants'; +import { LOCATORS_IDS } from '../constants'; import { useStartServices } from './use_core'; @@ -17,3 +17,7 @@ export function useLocator( const services = useStartServices(); return services.share.url.locators.get(locatorId); } + +export function useDashboardLocator() { + return useLocator(LOCATORS_IDS.DASHBOARD_APP); +} diff --git a/x-pack/test/fleet_api_integration/apis/integrations/elastic_agent.ts b/x-pack/test/fleet_api_integration/apis/integrations/elastic_agent.ts index 7fad10904a4f4..7cd94543cad30 100644 --- a/x-pack/test/fleet_api_integration/apis/integrations/elastic_agent.ts +++ b/x-pack/test/fleet_api_integration/apis/integrations/elastic_agent.ts @@ -6,10 +6,9 @@ */ import expect from '@kbn/expect'; -import { - FLEET_ELASTIC_AGENT_PACKAGE, - FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID, -} from '@kbn/fleet-plugin/common/constants/epm'; +import { FLEET_ELASTIC_AGENT_PACKAGE } from '@kbn/fleet-plugin/common/constants/epm'; + +import { DASHBOARD_LOCATORS_IDS } from '@kbn/fleet-plugin/common'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; import { setupFleetAndAgents } from '../agents/services'; @@ -47,10 +46,10 @@ export default function (providerContext: FtrProviderContext) { it('Install elastic agent details dashboard with the correct id', async () => { const resDashboard = await kibanaServer.savedObjects.get({ type: 'dashboard', - id: FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID, + id: DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_AGENT_METRICS, }); - expect(resDashboard.id).to.eql(FLEET_ELASTIC_AGENT_DETAILS_DASHBOARD_ID); + expect(resDashboard.id).to.eql(DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_AGENT_METRICS); }); after(async () => {