From 6321b19dae2440a4f8359774dd584c5d4dd67797 Mon Sep 17 00:00:00 2001 From: Mark Hopkin Date: Wed, 23 Aug 2023 09:53:49 +0100 Subject: [PATCH] [Fleet] Only show agent dashboard links if there is more than one non-server agent and if the dashboards exist (#164469) ## Summary Closes #161827 These buttons were showing in cloud when there was only the cloud agent added: Screenshot 2023-08-22 at 16 18 51 The ingest dashboard links will now only show if: - the user has one agent that is not part of a policy containing fleet server (I have re-used an existing hook we have to check this here) - the agent ingest metrics dashaboard exists in the current space Test Scenarios 1. Setup fleet with ONLY a fleet server connected, the dashboard links should not show 2. Setup fleet with fleet server and another non-fleet server agent, the dashboard links should show 3. With the same setup as test case 2, now create another space, visit fleet in the new space, the links should not show as the dashboards are not installed in the current space. ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit 805c974ed0c6f30be9153734859e0a28fa95eae1) --- .../fleet/.storybook/context/index.tsx | 3 ++ x-pack/plugins/fleet/kibana.jsonc | 3 +- .../components/dashboards_buttons.tsx | 36 +++++++++++++++++-- .../components/search_and_filter_bar.tsx | 8 ++++- .../public/mock/fleet_start_services.tsx | 1 + x-pack/plugins/fleet/public/plugin.ts | 4 +++ x-pack/plugins/fleet/tsconfig.json | 1 + 7 files changed, 52 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/.storybook/context/index.tsx b/x-pack/plugins/fleet/.storybook/context/index.tsx index cd89848008f98..43d3eea74df33 100644 --- a/x-pack/plugins/fleet/.storybook/context/index.tsx +++ b/x-pack/plugins/fleet/.storybook/context/index.tsx @@ -16,6 +16,8 @@ import { I18nProvider } from '@kbn/i18n-react'; import { CoreScopedHistory } from '@kbn/core/public'; import { getStorybookContextProvider } from '@kbn/custom-integrations-plugin/storybook'; +import type { DashboardStart } from '@kbn/dashboard-plugin/public'; + import { IntegrationsAppContext } from '../../public/applications/integrations/app'; import type { FleetConfigType, FleetStartServices } from '../../public/plugin'; import { ExperimentalFeaturesService } from '../../public/services'; @@ -76,6 +78,7 @@ export const StorybookContext: React.FC<{ storyContext?: Parameters languageClientsUiComponents: {}, }, customBranding: getCustomBranding(), + dashboard: {} as unknown as DashboardStart, docLinks: getDocLinks(), http: getHttp(), i18n: { diff --git a/x-pack/plugins/fleet/kibana.jsonc b/x-pack/plugins/fleet/kibana.jsonc index f0b01080db22d..b9f892ba4809d 100644 --- a/x-pack/plugins/fleet/kibana.jsonc +++ b/x-pack/plugins/fleet/kibana.jsonc @@ -23,7 +23,8 @@ "taskManager", "guidedOnboarding", "files", - "uiActions" + "uiActions", + "dashboard" ], "optionalPlugins": [ "features", 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 e42dfd997d241..25c394e2606b0 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,13 +5,37 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { DASHBOARD_LOCATORS_IDS } from '../../../../../../../common/constants'; -import { useDashboardLocator } from '../../../../hooks'; +import { useDashboardLocator, useStartServices } from '../../../../hooks'; + +const useDashboardExists = (dashboardId: string) => { + const [dashboardExists, setDashboardExists] = React.useState(false); + const [loading, setLoading] = React.useState(true); + const { dashboard: dashboardPlugin } = useStartServices(); + + useEffect(() => { + const fetchDashboard = async () => { + try { + const findDashboardsService = await dashboardPlugin.findDashboardsService(); + const [dashboard] = await findDashboardsService.findByIds([dashboardId]); + setLoading(false); + setDashboardExists(dashboard?.status === 'success'); + } catch (e) { + setLoading(false); + setDashboardExists(false); + } + }; + + fetchDashboard(); + }, [dashboardId, dashboardPlugin]); + + return { dashboardExists, loading }; +}; export const DashboardsButtons: React.FunctionComponent = () => { const dashboardLocator = useDashboardLocator(); @@ -20,6 +44,14 @@ export const DashboardsButtons: React.FunctionComponent = () => { return dashboardLocator?.getRedirectUrl({ dashboardId }) || ''; }; + const { dashboardExists, loading: dashboardLoading } = useDashboardExists( + DASHBOARD_LOCATORS_IDS.ELASTIC_AGENT_OVERVIEW + ); + + if (dashboardLoading || !dashboardExists) { + return null; + } + return ( <> 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 437f982bed706..27b62d54bd789 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 @@ -22,6 +22,8 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import styled from 'styled-components'; +import { useIsFirstTimeAgentUserQuery } from '../../../../../integrations/sections/epm/screens/detail/hooks'; + import type { Agent, AgentPolicy } from '../../../../types'; import { SearchBar } from '../../../../components'; import { AGENTS_INDEX, AGENTS_PREFIX } from '../../../../constants'; @@ -94,6 +96,8 @@ export const SearchAndFilterBar: React.FunctionComponent { const { euiTheme } = useEuiTheme(); const { isFleetServerStandalone } = useFleetServerStandalone(); + const { isFirstTimeAgentUser, isLoading: isFirstTimeAgentUserLoading } = + useIsFirstTimeAgentUserQuery(); const showAddFleetServerBtn = !isFleetServerStandalone; // Policies state for filtering @@ -126,7 +130,9 @@ export const SearchAndFilterBar: React.FunctionComponent {/* Top Buttons and Links */} - {totalAgents > 0 && } + + {!isFirstTimeAgentUserLoading && !isFirstTimeAgentUser && } + , authz: fleetAuthzMock, guidedOnboarding: guidedOnboardingMock.createStart(), diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index bc35914238b58..ecef1c469e954 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -49,6 +49,8 @@ import type { SendRequestResponse } from '@kbn/es-ui-shared-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; +import type { DashboardStart } from '@kbn/dashboard-plugin/public'; + import { PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, setupRouteService, appRoutesService } from '../common'; import { calculateAuthz, calculatePackagePrivilegesFromCapabilities } from '../common/authz'; import { parseExperimentalConfigValue } from '../common/experimental_features'; @@ -115,6 +117,7 @@ export interface FleetSetupDeps { export interface FleetStartDeps { licensing: LicensingPluginStart; data: DataPublicPluginStart; + dashboard: DashboardStart; dataViews: DataViewsPublicPluginStart; unifiedSearch: UnifiedSearchPublicPluginStart; navigation: NavigationPublicPluginStart; @@ -128,6 +131,7 @@ export interface FleetStartDeps { export interface FleetStartServices extends CoreStart, Exclude { storage: Storage; share: SharePluginStart; + dashboard: DashboardStart; cloud?: CloudSetup & CloudStart; discover?: DiscoverStart; spaces?: SpacesPluginStart; diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index 82b22c90779b7..e0d261d7b7e72 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -100,5 +100,6 @@ "@kbn/core-http-router-server-mocks", "@kbn/core-application-browser", "@kbn/core-saved-objects-base-server-internal", + "@kbn/dashboard-plugin", ] }