From e81acb84b9ab76fab71d6b6edca3dc57b579930b Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 11:04:04 +0200 Subject: [PATCH 01/20] very wip * added new assets screen * added routes to new assets view on the package details view --- .../plugins/fleet/common/types/models/epm.ts | 2 +- .../epm/screens/detail/assets/assets.tsx | 144 ++++++++++++++++++ .../epm/screens/detail/assets/index.ts | 7 + .../sections/epm/screens/detail/index.tsx | 22 +++ .../fleet/public/constants/page_paths.ts | 6 + 5 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/index.ts diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index f19684b0445e2a..58f9903eeea3a7 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -45,7 +45,7 @@ export type InstallSource = 'registry' | 'upload'; export type EpmPackageInstallStatus = 'installed' | 'installing'; -export type DetailViewPanelName = 'overview' | 'policies' | 'settings' | 'custom'; +export type DetailViewPanelName = 'overview' | 'policies' | 'assets' | 'settings' | 'custom'; export type ServiceName = 'kibana' | 'elasticsearch'; export type AgentAssetType = typeof agentAssetTypes; export type DocAssetType = 'doc' | 'notice'; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx new file mode 100644 index 00000000000000..6f8a5bd45c4d10 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -0,0 +1,144 @@ +/* + * 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 React, { useEffect, useState } from 'react'; +import { Redirect } from 'react-router-dom'; +import type { SimpleSavedObject } from 'src/core/public'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiAccordion } from '@elastic/eui'; + +import { Loading } from '../../../../../components'; + +import type { PackageInfo } from '../../../../../types'; +import { InstallStatus, KibanaAssetType } from '../../../../../types'; + +import { useGetPackageInstallStatus, useLink, useStartServices } from '../../../../../hooks'; + +import { AssetTitleMap } from '../../../../../constants'; + +interface AssetsPanelProps { + packageInfo: PackageInfo; +} + +const allowedAssetTypes: KibanaAssetType[] = [ + KibanaAssetType.dashboard, + KibanaAssetType.search, + KibanaAssetType.visualization, +]; + +export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { + const { name, version } = packageInfo; + const { + savedObjects: { client: savedObjectsClient }, + http, + } = useStartServices(); + + const { getPath } = useLink(); + const getPackageInstallStatus = useGetPackageInstallStatus(); + const packageInstallStatus = getPackageInstallStatus(packageInfo.name); + + const [assetSavedObjects, setAssetsSavedObjects] = useState(); + const [fetchError, setFetchError] = useState(); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchAssetSavedObjects = async () => { + if ('savedObject' in packageInfo) { + const { + savedObject: { attributes: packageAttributes }, + } = packageInfo; + + if ( + !packageAttributes.installed_kibana || + packageAttributes.installed_kibana.length === 0 + ) { + setIsLoading(false); + return; + } + + try { + const { savedObjects } = await savedObjectsClient.bulkGet( + packageAttributes.installed_kibana.map(({ id, type }) => ({ + id, + type, + })) + ); + setAssetsSavedObjects(savedObjects); + } catch (e) { + setFetchError(e); + } finally { + setIsLoading(false); + } + } else { + setIsLoading(false); + } + }; + fetchAssetSavedObjects(); + }, [savedObjectsClient, packageInfo]); + + // if they arrive at this page and the package is not installed, send them to overview + // this happens if they arrive with a direct url or they uninstall while on this tab + if (packageInstallStatus.status !== InstallStatus.installed) { + return ( + + ); + } + + let content: JSX.Element; + + if (isLoading) { + content = ; + } else if (fetchError) { + content =

TODO HANDLE ERROR

; + } else if (assetSavedObjects === undefined) { + content = ( + +

+ +

+
+ ); + } else { + content = ( + <> + {allowedAssetTypes.map((assetType) => { + const sectionAssetSavedObjects = assetSavedObjects.filter((so) => so.type === assetType); + if (!sectionAssetSavedObjects.length) { + return null; + } + + return ( + + {AssetTitleMap[assetType]} {sectionAssetSavedObjects.length} + + } + id={assetType} + > + {sectionAssetSavedObjects.map(({ attributes }) => attributes.title)} + + ); + })} + + ); + } + + return ( + + + {content} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/index.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/index.ts new file mode 100644 index 00000000000000..ceb030b7ce02eb --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/index.ts @@ -0,0 +1,7 @@ +/* + * 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 { AssetsPage } from './assets'; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 99a29a8194f9b0..fb47d66df5f94d 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -56,6 +56,7 @@ import { WithHeaderLayout } from '../../../../layouts'; import { RELEASE_BADGE_DESCRIPTION, RELEASE_BADGE_LABEL } from '../../components/release_badge'; import { IntegrationAgentPolicyCount, UpdateIcon, IconPanel, LoadingIconPanel } from './components'; +import { AssetsPage } from './assets'; import { OverviewPage } from './overview'; import { PackagePoliciesPage } from './policies'; import { SettingsPage } from './settings'; @@ -408,6 +409,24 @@ export function Detail() { }); } + if (packageInfo.assets) { + tabs.push({ + id: 'assets', + name: ( + + ), + isSelected: panel === 'assets', + 'data-test-subj': `tab-assets`, + href: getHref('integration_details_assets', { + pkgkey: packageInfoKey, + ...(integration ? { integration } : {}), + }), + }); + } + tabs.push({ id: 'settings', name: ( @@ -476,6 +495,9 @@ export function Detail() { + + + diff --git a/x-pack/plugins/fleet/public/constants/page_paths.ts b/x-pack/plugins/fleet/public/constants/page_paths.ts index 3c9c0e57596151..97f04b41d2cb9e 100644 --- a/x-pack/plugins/fleet/public/constants/page_paths.ts +++ b/x-pack/plugins/fleet/public/constants/page_paths.ts @@ -20,6 +20,7 @@ export type StaticPage = export type DynamicPage = | 'integration_details_overview' | 'integration_details_policies' + | 'integration_details_assets' | 'integration_details_settings' | 'integration_details_custom' | 'integration_policy_edit' @@ -65,6 +66,7 @@ export const INTEGRATIONS_ROUTING_PATHS = { integration_details: '/detail/:pkgkey/:panel?', integration_details_overview: '/detail/:pkgkey/overview', integration_details_policies: '/detail/:pkgkey/policies', + integration_details_assets: '/detail/:pkgkey/assets', integration_details_settings: '/detail/:pkgkey/settings', integration_details_custom: '/detail/:pkgkey/custom', integration_policy_edit: '/edit-integration/:packagePolicyId', @@ -89,6 +91,10 @@ export const pagePathGetters: { INTEGRATIONS_BASE_PATH, `/detail/${pkgkey}/policies${integration ? `?integration=${integration}` : ''}`, ], + integration_details_assets: ({ pkgkey, integration }) => [ + INTEGRATIONS_BASE_PATH, + `/detail/${pkgkey}/assets${integration ? `?integration=${integration}` : ''}`, + ], integration_details_settings: ({ pkgkey, integration }) => [ INTEGRATIONS_BASE_PATH, `/detail/${pkgkey}/settings${integration ? `?integration=${integration}` : ''}`, From 2c45e7d09578c2ca25b0f20f23b9ff5be4260991 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 11:57:30 +0200 Subject: [PATCH 02/20] Finished styling the assets page layout, need to work on adding links --- .../epm/screens/detail/assets/assets.scss | 3 + .../epm/screens/detail/assets/assets.tsx | 85 ++++++++++++++++--- 2 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.scss diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.scss b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.scss new file mode 100644 index 00000000000000..a430f0d22943ba --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.scss @@ -0,0 +1,3 @@ +.customOuterSplitPanel { + --childBorders: #{$euiBorderThin}; +} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx index 6f8a5bd45c4d10..79b536e3dfce17 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -9,7 +9,17 @@ import React, { useEffect, useState } from 'react'; import { Redirect } from 'react-router-dom'; import type { SimpleSavedObject } from 'src/core/public'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiAccordion } from '@elastic/eui'; +import styled from 'styled-components'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiText, + EuiAccordion, + EuiSpacer, + EuiBadge, + EuiSplitPanel, +} from '@elastic/eui'; import { Loading } from '../../../../../components'; @@ -20,28 +30,39 @@ import { useGetPackageInstallStatus, useLink, useStartServices } from '../../../ import { AssetTitleMap } from '../../../../../constants'; +import './assets.scss'; + interface AssetsPanelProps { packageInfo: PackageInfo; } +const CustomSplitPanelInner = styled(EuiSplitPanel.Inner)``; + +const CustomSplitPanelOuter = styled(EuiSplitPanel.Outer)` + ${CustomSplitPanelInner}:not(:last-child) { + border-bottom: var(--childBorders); + } +`; + const allowedAssetTypes: KibanaAssetType[] = [ KibanaAssetType.dashboard, KibanaAssetType.search, KibanaAssetType.visualization, ]; +type AssetSavedObject = SimpleSavedObject<{ title: string; description?: string }>; + export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { const { name, version } = packageInfo; const { savedObjects: { client: savedObjectsClient }, - http, } = useStartServices(); const { getPath } = useLink(); const getPackageInstallStatus = useGetPackageInstallStatus(); const packageInstallStatus = getPackageInstallStatus(packageInfo.name); - const [assetSavedObjects, setAssetsSavedObjects] = useState(); + const [assetSavedObjects, setAssetsSavedObjects] = useState(); const [fetchError, setFetchError] = useState(); const [isLoading, setIsLoading] = useState(true); @@ -67,7 +88,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { type, })) ); - setAssetsSavedObjects(savedObjects); + setAssetsSavedObjects(savedObjects as AssetSavedObject[]); } catch (e) { setFetchError(e); } finally { @@ -119,16 +140,52 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { } return ( - - {AssetTitleMap[assetType]} {sectionAssetSavedObjects.length} - - } - id={assetType} - > - {sectionAssetSavedObjects.map(({ attributes }) => attributes.title)} - + <> + + + +

{AssetTitleMap[assetType]}

+
+
+ + {sectionAssetSavedObjects.length} + + + } + id={assetType} + > + <> + + + {sectionAssetSavedObjects.map(({ attributes: { title, description } }, idx) => ( + + +

{title}

+
+ + {description && ( + +

{description}

+
+ )} +
+ ))} +
+ +
+ + ); })} From 815d0b62edabb3603a1d0adbe6cce4bef71a329a Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 12:10:36 +0200 Subject: [PATCH 03/20] rather use EuiHorizontalRule --- .../epm/screens/detail/assets/assets.scss | 3 -- .../epm/screens/detail/assets/assets.tsx | 37 +++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) delete mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.scss diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.scss b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.scss deleted file mode 100644 index a430f0d22943ba..00000000000000 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.scss +++ /dev/null @@ -1,3 +0,0 @@ -.customOuterSplitPanel { - --childBorders: #{$euiBorderThin}; -} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx index 79b536e3dfce17..9626be3cab71b3 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -9,7 +9,6 @@ import React, { useEffect, useState } from 'react'; import { Redirect } from 'react-router-dom'; import type { SimpleSavedObject } from 'src/core/public'; import { FormattedMessage } from '@kbn/i18n/react'; -import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, @@ -19,6 +18,7 @@ import { EuiSpacer, EuiBadge, EuiSplitPanel, + EuiHorizontalRule, } from '@elastic/eui'; import { Loading } from '../../../../../components'; @@ -36,14 +36,6 @@ interface AssetsPanelProps { packageInfo: PackageInfo; } -const CustomSplitPanelInner = styled(EuiSplitPanel.Inner)``; - -const CustomSplitPanelOuter = styled(EuiSplitPanel.Outer)` - ${CustomSplitPanelInner}:not(:last-child) { - border-bottom: var(--childBorders); - } -`; - const allowedAssetTypes: KibanaAssetType[] = [ KibanaAssetType.dashboard, KibanaAssetType.search, @@ -163,25 +155,30 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { > <> - {sectionAssetSavedObjects.map(({ attributes: { title, description } }, idx) => ( - - -

{title}

-
- - {description && ( - -

{description}

+ <> + + +

{title}

+ + {description && ( + +

{description}

+
+ )} +
+ {idx + 1 < sectionAssetSavedObjects.length && ( + )} -
+ ))} -
+ From 3c9912b401526a0e285ffb82aaf6d3b237b43673 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 12:48:58 +0200 Subject: [PATCH 04/20] only show the assets tab if installed --- .../integrations/sections/epm/screens/detail/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index fb47d66df5f94d..6d6286932c237f 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -409,7 +409,7 @@ export function Detail() { }); } - if (packageInfo.assets) { + if (packageInfo.assets && packageInstallStatus === InstallStatus.installed) { tabs.push({ id: 'assets', name: ( From 3dfd692663a0d60decfe1812a5f4f97eb5708c92 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 13:43:20 +0200 Subject: [PATCH 05/20] Added hacky version of linking to assets. --- .../epm/screens/detail/assets/assets.tsx | 98 ++++++++++++++----- x-pack/plugins/fleet/public/hooks/index.ts | 2 +- .../fleet/public/hooks/use_kibana_link.ts | 9 +- 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx index 9626be3cab71b3..b8dad865078ab8 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -13,6 +13,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle, + EuiLink, EuiText, EuiAccordion, EuiSpacer, @@ -26,17 +27,26 @@ import { Loading } from '../../../../../components'; import type { PackageInfo } from '../../../../../types'; import { InstallStatus, KibanaAssetType } from '../../../../../types'; -import { useGetPackageInstallStatus, useLink, useStartServices } from '../../../../../hooks'; +import { + useGetPackageInstallStatus, + useLink, + useStartServices, + getKibanaLink, +} from '../../../../../hooks'; import { AssetTitleMap } from '../../../../../constants'; -import './assets.scss'; - interface AssetsPanelProps { packageInfo: PackageInfo; } -const allowedAssetTypes: KibanaAssetType[] = [ +type AllowedAssetTypes = [ + KibanaAssetType.dashboard, + KibanaAssetType.search, + KibanaAssetType.visualization +]; + +const allowedAssetTypes: AllowedAssetTypes = [ KibanaAssetType.dashboard, KibanaAssetType.search, KibanaAssetType.visualization, @@ -44,10 +54,38 @@ const allowedAssetTypes: KibanaAssetType[] = [ type AssetSavedObject = SimpleSavedObject<{ title: string; description?: string }>; +/** + * TODO: This is a temporary solution for getting links to various assets. It is very risky because: + * + * 1. The plugin might not exist/be enabled + * 2. URLs and paths might not always be supported + * + * We should migrate to using the new URL service locators. + */ +const getPathToObjectInApp = ({ + type, + id, +}: { + type: KibanaAssetType; + id: string; +}): undefined | string => { + switch (type) { + case KibanaAssetType.dashboard: + return `/dashboard/${id}`; + case KibanaAssetType.search: + return `/discover/${id}`; + case KibanaAssetType.visualization: + return `/visualize/edit/${id}`; + default: + return undefined; + } +}; + export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { const { name, version } = packageInfo; const { savedObjects: { client: savedObjectsClient }, + http, } = useStartServices(); const { getPath } = useLink(); @@ -160,24 +198,40 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { hasBorder hasShadow={false} > - {sectionAssetSavedObjects.map(({ attributes: { title, description } }, idx) => ( - <> - - -

{title}

-
- - {description && ( - -

{description}

-
- )} -
- {idx + 1 < sectionAssetSavedObjects.length && ( - - )} - - ))} + {sectionAssetSavedObjects.map( + ({ id, type, attributes: { title, description } }, idx) => { + const pathToObjectInApp = getPathToObjectInApp({ + id, + type: type as KibanaAssetType, + }); + return ( + <> + {}} grow={false} key={idx}> + +

+ {pathToObjectInApp ? ( + + {title} + + ) : ( + title + )} +

+
+ + {description && ( + +

{description}

+
+ )} +
+ {idx + 1 < sectionAssetSavedObjects.length && ( + + )} + + ); + } + )} diff --git a/x-pack/plugins/fleet/public/hooks/index.ts b/x-pack/plugins/fleet/public/hooks/index.ts index 9f41e5c7cc92b5..1258a47f95015e 100644 --- a/x-pack/plugins/fleet/public/hooks/index.ts +++ b/x-pack/plugins/fleet/public/hooks/index.ts @@ -11,7 +11,7 @@ export { useConfig, ConfigContext } from './use_config'; export { useKibanaVersion, KibanaVersionContext } from './use_kibana_version'; export { licenseService, useLicense } from './use_license'; export { useLink } from './use_link'; -export { useKibanaLink } from './use_kibana_link'; +export { useKibanaLink, getKibanaLink } from './use_kibana_link'; export { usePackageIconType, UsePackageIconType } from './use_package_icon_type'; export { usePagination, Pagination, PAGE_SIZE_OPTIONS } from './use_pagination'; export { useUrlPagination } from './use_url_pagination'; diff --git a/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts b/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts index 29f4f8748d1a0e..815de79374453a 100644 --- a/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts +++ b/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts @@ -4,12 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { HttpStart } from 'src/core/public'; import { useStartServices } from './'; const KIBANA_BASE_PATH = '/app/kibana'; +export const getKibanaLink = (http: HttpStart, path: string) => { + return http.basePath.prepend(`${KIBANA_BASE_PATH}#${path}`); +}; + export function useKibanaLink(path: string = '/') { - const core = useStartServices(); - return core.http.basePath.prepend(`${KIBANA_BASE_PATH}#${path}`); + const { http } = useStartServices(); + return getKibanaLink(http, path); } From 8669f7e855e2684788a847f989d3d5a0eb12de30 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 14:32:28 +0200 Subject: [PATCH 06/20] added comment about deprecation of current linking functionality --- x-pack/plugins/fleet/kibana.json | 8 +------- x-pack/plugins/fleet/public/hooks/use_kibana_link.ts | 7 +++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/fleet/kibana.json b/x-pack/plugins/fleet/kibana.json index 4a4019e3e9e47f..2fcbef75b9832b 100644 --- a/x-pack/plugins/fleet/kibana.json +++ b/x-pack/plugins/fleet/kibana.json @@ -5,13 +5,7 @@ "ui": true, "configPath": ["xpack", "fleet"], "requiredPlugins": ["licensing", "data", "encryptedSavedObjects"], - "optionalPlugins": [ - "security", - "features", - "cloud", - "usageCollection", - "home" - ], + "optionalPlugins": ["security", "features", "cloud", "usageCollection", "home"], "extraPublicDirs": ["common"], "requiredBundles": ["kibanaReact", "esUiShared", "home", "infra", "kibanaUtils"] } diff --git a/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts b/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts index 815de79374453a..e8ecc9cf5492f4 100644 --- a/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts +++ b/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts @@ -10,10 +10,17 @@ import { useStartServices } from './'; const KIBANA_BASE_PATH = '/app/kibana'; +/** + * @deprecated See comment on {@link useKibanaLink} + */ export const getKibanaLink = (http: HttpStart, path: string) => { return http.basePath.prepend(`${KIBANA_BASE_PATH}#${path}`); }; +/** + * TODO: This functionality needs to be replaced with use of the new URL service locators + * @deprecated + */ export function useKibanaLink(path: string = '/') { const { http } = useStartServices(); return getKibanaLink(http, path); From 646c5836a420c03c1002ab4d044830c99e815402 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 15:35:22 +0200 Subject: [PATCH 07/20] added an initial version of the success toast with a link to the agent flyout --- .../create_package_policy_page/index.tsx | 66 +++++++++++++++---- .../epm/screens/detail/assets/assets.tsx | 2 +- .../sections/epm/screens/detail/index.tsx | 2 +- .../detail/policies/package_policies.tsx | 11 +++- .../agent_enrollment_flyout/steps.tsx | 34 ++++++++++ .../fleet/public/constants/page_paths.ts | 10 +-- 6 files changed, 103 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index 75fc06c1a44945..0f3463a45fa0ae 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -19,10 +19,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, + EuiLink, } from '@elastic/eui'; import type { EuiStepProps } from '@elastic/eui/src/components/steps/step'; import type { ApplicationStart } from 'kibana/public'; +import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; import type { AgentPolicy, PackageInfo, @@ -43,6 +45,7 @@ import { useIntraAppState, useUIExtension } from '../../../hooks'; import { ExtensionWrapper } from '../../../components'; import type { PackagePolicyEditExtensionComponentProps } from '../../../types'; import { PLUGIN_ID } from '../../../../../../common/constants'; +import { pagePathGetters } from '../../../../../constants'; import { pkgKeyFromPackageInfo } from '../../../services'; import { CreatePackagePolicyPageLayout } from './components'; @@ -60,7 +63,7 @@ const StepsWithLessPadding = styled(EuiSteps)` `; const CustomEuiBottomBar = styled(EuiBottomBar)` - // Set a relatively _low_ z-index value here to account for EuiComboBox popover that might appear under the bottom bar + /* A relatively _low_ z-index value here to account for EuiComboBox popover that might appear under the bottom bar */ z-index: 50; `; @@ -280,6 +283,13 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { ); } + const fromPolicyWithoutAgentsAssigned = from === 'policy' && agentPolicy && agentCount === 0; + + const fromPackageWithoutAgentsAssigned = + from === 'package' && packageInfo && agentPolicy && agentCount === 0; + + const hasAgentsAssigned = agentCount && agentPolicy; + notifications.toasts.addSuccess({ title: i18n.translate('xpack.fleet.createPackagePolicy.addedNotificationTitle', { defaultMessage: `'{packagePolicyName}' integration added.`, @@ -287,22 +297,48 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { packagePolicyName: packagePolicy.name, }, }), - text: - agentCount && agentPolicy - ? i18n.translate('xpack.fleet.createPackagePolicy.addedNotificationMessage', { - defaultMessage: `Fleet will deploy updates to all agents that use the '{agentPolicyName}' policy.`, - values: { - agentPolicyName: agentPolicy.name, - }, - }) - : (params as AddToPolicyParams)?.policyId && agentPolicy && agentCount === 0 - ? i18n.translate('xpack.fleet.createPackagePolicy.addAgentNextNotification', { + text: fromPolicyWithoutAgentsAssigned + ? i18n.translate( + 'xpack.fleet.createPackagePolicy.policyContextAddAgentNextNotificationMessage', + { defaultMessage: `The policy has been updated. Add an agent to the '{agentPolicyName}' policy to deploy this policy.`, values: { - agentPolicyName: agentPolicy.name, + agentPolicyName: agentPolicy!.name, }, - }) - : undefined, + } + ) + : fromPackageWithoutAgentsAssigned + ? toMountPoint( + + {i18n.translate( + 'xpack.fleet.createPackagePolicy.integrationsContextAddAgentLinkMessage', + { defaultMessage: 'add an agent' } + )} + + ), + }} + /> + ) + : hasAgentsAssigned + ? i18n.translate('xpack.fleet.createPackagePolicy.addedNotificationMessage', { + defaultMessage: `Fleet will deploy updates to all agents that use the '{agentPolicyName}' policy.`, + values: { + agentPolicyName: agentPolicy!.name, + }, + }) + : undefined, 'data-test-subj': 'packagePolicyCreateSuccessToast', }); } else { @@ -312,6 +348,8 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { setFormState('VALID'); } }, [ + from, + packageInfo, agentCount, agentPolicy, formState, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx index b8dad865078ab8..5f50ecb223ade0 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -206,7 +206,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { }); return ( <> - {}} grow={false} key={idx}> +

{pathToObjectInApp ? ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 6d6286932c237f..cf6007026afebc 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -409,7 +409,7 @@ export function Detail() { }); } - if (packageInfo.assets && packageInstallStatus === InstallStatus.installed) { + if (packageInstallStatus === InstallStatus.installed && packageInfo.assets) { tabs.push({ id: 'assets', name: ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index 7da7328fdebbca..cb6c896cd92691 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -6,7 +6,7 @@ */ import React, { memo, useCallback, useMemo, useState } from 'react'; -import { Redirect } from 'react-router-dom'; +import { Redirect, useLocation } from 'react-router-dom'; import type { CriteriaWithPagination, EuiTableFieldDataColumnType } from '@elastic/eui'; import { EuiButtonIcon, @@ -66,7 +66,14 @@ interface PackagePoliciesPanelProps { version: string; } export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps) => { - const [flyoutOpenForPolicyId, setFlyoutOpenForPolicyId] = useState(null); + const { search } = useLocation(); + const queryParams = useMemo(() => new URLSearchParams(search), [search]); + const agentPolicyIdFromParams = useMemo(() => queryParams.get('addAgentToPolicyId'), [ + queryParams, + ]); + const [flyoutOpenForPolicyId, setFlyoutOpenForPolicyId] = useState( + agentPolicyIdFromParams + ); const { getPath } = useLink(); const getPackageInstallStatus = useGetPackageInstallStatus(); const packageInstallStatus = getPackageInstallStatus(name); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx index ea4fa626afbb6a..a8b8d0b8a57805 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx @@ -138,3 +138,37 @@ export const AgentEnrollmentKeySelectionStep = ({ ), }; }; + +/** + * Send users to assets installed by the package in Kibana so they can + * view their data. + */ +export const ViewDataStep = () => { + return { + title: i18n.translate('xpack.fleet.agentEnrollment.stepViewDataTitle', { + defaultMessage: 'View your data', + }), + children: ( + <> + + + {i18n.translate( + 'xpack.fleet.agentEnrollment.viewDataDescription.pleaseNoteLabel', + { defaultMessage: 'Please note' } + )} + + ), + }} + /> + + + + + ), + }; +}; diff --git a/x-pack/plugins/fleet/public/constants/page_paths.ts b/x-pack/plugins/fleet/public/constants/page_paths.ts index 97f04b41d2cb9e..78dd176aa05dea 100644 --- a/x-pack/plugins/fleet/public/constants/page_paths.ts +++ b/x-pack/plugins/fleet/public/constants/page_paths.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { stringify } from 'query-string'; + export type StaticPage = | 'base' | 'overview' @@ -87,10 +89,10 @@ export const pagePathGetters: { INTEGRATIONS_BASE_PATH, `/detail/${pkgkey}/overview${integration ? `?integration=${integration}` : ''}`, ], - integration_details_policies: ({ pkgkey, integration }) => [ - INTEGRATIONS_BASE_PATH, - `/detail/${pkgkey}/policies${integration ? `?integration=${integration}` : ''}`, - ], + integration_details_policies: ({ pkgkey, integration, addAgentToPolicyId }) => { + const qs = stringify({ integration, addAgentToPolicyId }); + return [INTEGRATIONS_BASE_PATH, `/detail/${pkgkey}/policies${qs ? `?${qs}` : ''}`]; + }, integration_details_assets: ({ pkgkey, integration }) => [ INTEGRATIONS_BASE_PATH, `/detail/${pkgkey}/assets${integration ? `?integration=${integration}` : ''}`, From f381f9100b0e6c134d8966606acd021a9e2d7aee Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 16:38:08 +0200 Subject: [PATCH 08/20] First iteration of end-to-end UX working. Need to add a lot of tests! --- .../create_package_policy_page/index.tsx | 11 +- .../detail/policies/package_policies.tsx | 64 +++++-- .../agent_enrollment_flyout/index.tsx | 13 +- .../managed_instructions.tsx | 167 ++++++++++-------- .../agent_enrollment_flyout/steps.tsx | 25 +-- .../agent_enrollment_flyout/types.ts | 12 +- .../package_policy_actions_menu.tsx | 9 +- 7 files changed, 178 insertions(+), 123 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index 0f3463a45fa0ae..fc6ef809139615 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -315,12 +315,10 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { values={{ link: ( {i18n.translate( 'xpack.fleet.createPackagePolicy.integrationsContextAddAgentLinkMessage', @@ -348,6 +346,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { setFormState('VALID'); } }, [ + getHref, from, packageInfo, agentCount, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index cb6c896cd92691..c672abeb1c9036 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -4,9 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { stringify, parse } from 'query-string'; import React, { memo, useCallback, useMemo, useState } from 'react'; -import { Redirect, useLocation } from 'react-router-dom'; +import { Redirect, useLocation, useHistory } from 'react-router-dom'; import type { CriteriaWithPagination, EuiTableFieldDataColumnType } from '@elastic/eui'; import { EuiButtonIcon, @@ -15,6 +15,9 @@ import { EuiFlexGroup, EuiFlexItem, EuiToolTip, + EuiText, + EuiButton, + EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedRelative, FormattedMessage } from '@kbn/i18n/react'; @@ -67,6 +70,7 @@ interface PackagePoliciesPanelProps { } export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps) => { const { search } = useLocation(); + const history = useHistory(); const queryParams = useMemo(() => new URLSearchParams(search), [search]); const agentPolicyIdFromParams = useMemo(() => queryParams.get('addAgentToPolicyId'), [ queryParams, @@ -74,7 +78,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps const [flyoutOpenForPolicyId, setFlyoutOpenForPolicyId] = useState( agentPolicyIdFromParams ); - const { getPath } = useLink(); + const { getPath, getHref } = useLink(); const getPackageInstallStatus = useGetPackageInstallStatus(); const packageInstallStatus = getPackageInstallStatus(name); const { pagination, pageSizeOptions, setPagination } = useUrlPagination(); @@ -94,6 +98,36 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps [setPagination] ); + const renderViewDataStepContent = useCallback( + () => ( + <> + + + {i18n.translate( + 'xpack.fleet.epm.agentEnrollment.viewDataDescription.pleaseNoteLabel', + { defaultMessage: 'Please note' } + )} + + ), + }} + /> + + + + {i18n.translate('xpack.fleet.epm.agentEnrollment.viewDataAssetsLabel', { + defaultMessage: 'View assets', + })} + + + ), + [name, version, getHref] + ); + const columns: Array> = useMemo( () => [ { @@ -193,12 +227,16 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps align: 'right', render({ agentPolicy, packagePolicy }) { return ( - + ); }, }, ], - [] + [renderViewDataStepContent] ); const noItemsMessage = useMemo(() => { @@ -243,14 +281,18 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps /> - {flyoutOpenForPolicyId && ( + {flyoutOpenForPolicyId && !isLoading && ( setFlyoutOpenForPolicyId(null)} - agentPolicies={ - data?.items - .filter(({ agentPolicy }) => agentPolicy.id === flyoutOpenForPolicyId) - .map(({ agentPolicy }) => agentPolicy) ?? [] + onClose={() => { + setFlyoutOpenForPolicyId(null); + const { addAgentToPolicyId, ...rest } = parse(search); + history.replace({ search: stringify(rest) }); + }} + agentPolicy={ + data?.items.find(({ agentPolicy }) => agentPolicy.id === flyoutOpenForPolicyId) + ?.agentPolicy } + viewDataStepContent={renderViewDataStepContent()} /> )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index b91af80691033e..58362d85e2fb32 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -42,6 +42,7 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ onClose, agentPolicy, agentPolicies, + viewDataStepContent, }) => { const [mode, setMode] = useState<'managed' | 'standalone'>('managed'); @@ -109,9 +110,17 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ } > {fleetServerHosts.length === 0 && mode === 'managed' ? null : mode === 'managed' ? ( - + ) : ( - + )} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx index 2bb8586a11503d..d6888ae76fe87b 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/managed_instructions.tsx @@ -21,7 +21,12 @@ import { useFleetServerInstructions, } from '../../applications/fleet/sections/agents/agent_requirements_page'; -import { DownloadStep, AgentPolicySelectionStep, AgentEnrollmentKeySelectionStep } from './steps'; +import { + DownloadStep, + AgentPolicySelectionStep, + AgentEnrollmentKeySelectionStep, + ViewDataStep, +} from './steps'; import type { BaseProps } from './types'; type Props = BaseProps; @@ -53,83 +58,91 @@ const FleetServerMissingRequirements = () => { return ; }; -export const ManagedInstructions = React.memo(({ agentPolicy, agentPolicies }) => { - const fleetStatus = useFleetStatus(); +export const ManagedInstructions = React.memo( + ({ agentPolicy, agentPolicies, viewDataStepContent }) => { + const fleetStatus = useFleetStatus(); - const [selectedAPIKeyId, setSelectedAPIKeyId] = useState(); - const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); + const [selectedAPIKeyId, setSelectedAPIKeyId] = useState(); + const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); - const apiKey = useGetOneEnrollmentAPIKey(selectedAPIKeyId); - const settings = useGetSettings(); - const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id); + const apiKey = useGetOneEnrollmentAPIKey(selectedAPIKeyId); + const settings = useGetSettings(); + const fleetServerInstructions = useFleetServerInstructions(apiKey?.data?.item?.policy_id); - const steps = useMemo(() => { - const { - serviceToken, - getServiceToken, - isLoadingServiceToken, - installCommand, - platform, - setPlatform, - } = fleetServerInstructions; - const fleetServerHosts = settings.data?.item?.fleet_server_hosts || []; - const baseSteps: EuiContainedStepProps[] = [ - DownloadStep(), - !agentPolicy - ? AgentPolicySelectionStep({ - agentPolicies, - setSelectedAPIKeyId, - setIsFleetServerPolicySelected, - }) - : AgentEnrollmentKeySelectionStep({ agentPolicy, setSelectedAPIKeyId }), - ]; - if (isFleetServerPolicySelected) { - baseSteps.push( - ...[ - ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }), - FleetServerCommandStep({ serviceToken, installCommand, platform, setPlatform }), - ] - ); - } else { - baseSteps.push({ - title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', { - defaultMessage: 'Enroll and start the Elastic Agent', - }), - children: selectedAPIKeyId && apiKey.data && ( - - ), - }); - } - return baseSteps; - }, [ - agentPolicy, - agentPolicies, - selectedAPIKeyId, - apiKey.data, - isFleetServerPolicySelected, - settings.data?.item?.fleet_server_hosts, - fleetServerInstructions, - ]); + const steps = useMemo(() => { + const { + serviceToken, + getServiceToken, + isLoadingServiceToken, + installCommand, + platform, + setPlatform, + } = fleetServerInstructions; + const fleetServerHosts = settings.data?.item?.fleet_server_hosts || []; + const baseSteps: EuiContainedStepProps[] = [ + DownloadStep(), + !agentPolicy + ? AgentPolicySelectionStep({ + agentPolicies, + setSelectedAPIKeyId, + setIsFleetServerPolicySelected, + }) + : AgentEnrollmentKeySelectionStep({ agentPolicy, setSelectedAPIKeyId }), + ]; + if (isFleetServerPolicySelected) { + baseSteps.push( + ...[ + ServiceTokenStep({ serviceToken, getServiceToken, isLoadingServiceToken }), + FleetServerCommandStep({ serviceToken, installCommand, platform, setPlatform }), + ] + ); + } else { + baseSteps.push({ + title: i18n.translate('xpack.fleet.agentEnrollment.stepEnrollAndRunAgentTitle', { + defaultMessage: 'Enroll and start the Elastic Agent', + }), + children: selectedAPIKeyId && apiKey.data && ( + + ), + }); + } - return ( - <> - {fleetStatus.isReady ? ( - <> - - - - - - - ) : fleetStatus.missingRequirements?.length === 1 && - fleetStatus.missingRequirements[0] === 'fleet_server' ? ( - - ) : ( - - )} - - ); -}); + if (viewDataStepContent) { + baseSteps.push(ViewDataStep(viewDataStepContent)); + } + + return baseSteps; + }, [ + agentPolicy, + agentPolicies, + selectedAPIKeyId, + apiKey.data, + isFleetServerPolicySelected, + settings.data?.item?.fleet_server_hosts, + fleetServerInstructions, + viewDataStepContent, + ]); + + return ( + <> + {fleetStatus.isReady ? ( + <> + + + + + + + ) : fleetStatus.missingRequirements?.length === 1 && + fleetStatus.missingRequirements[0] === 'fleet_server' ? ( + + ) : ( + + )} + + ); + } +); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx index a8b8d0b8a57805..03cff88e639695 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps.tsx @@ -143,32 +143,11 @@ export const AgentEnrollmentKeySelectionStep = ({ * Send users to assets installed by the package in Kibana so they can * view their data. */ -export const ViewDataStep = () => { +export const ViewDataStep = (content: JSX.Element) => { return { title: i18n.translate('xpack.fleet.agentEnrollment.stepViewDataTitle', { defaultMessage: 'View your data', }), - children: ( - <> - - - {i18n.translate( - 'xpack.fleet.agentEnrollment.viewDataDescription.pleaseNoteLabel', - { defaultMessage: 'Please note' } - )} - - ), - }} - /> - - - - - ), + children: content, }; }; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts index b9bcf8fb3e4b27..e0c5b040a61fb2 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts @@ -9,12 +9,20 @@ import type { AgentPolicy } from '../../types'; export interface BaseProps { /** - * The user selected policy to be used + * The user selected policy to be used. If this value is `undefined` a value must be provided for `agentPolicies`. */ agentPolicy?: AgentPolicy; /** - * A selection of policies for the user to choose from, will be ignored if `agentPolicy` has been provided + * A selection of policies for the user to choose from, will be ignored if `agentPolicy` has been provided. + * + * If this value is `undefined` a value must be provided for `agentPolicy`. */ agentPolicies?: AgentPolicy[]; + + /** + * There is a step in the agent enrollment process that allows users to see the data from an integration represented in the UI + * in some way. This is an area for consumers to render a button and text explaining how data can be viewed. + */ + viewDataStepContent?: JSX.Element; } diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx index 03bf2095f7f3e0..1f64de27fce392 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx @@ -21,7 +21,8 @@ import { PackagePolicyDeleteProvider } from './package_policy_delete_provider'; export const PackagePolicyActionsMenu: React.FunctionComponent<{ agentPolicy: AgentPolicy; packagePolicy: PackagePolicy; -}> = ({ agentPolicy, packagePolicy }) => { + viewDataStepContent?: JSX.Element; +}> = ({ agentPolicy, packagePolicy, viewDataStepContent }) => { const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); const { getHref } = useLink(); const hasWriteCapabilities = useCapabilities().write; @@ -103,7 +104,11 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ <> {isEnrollmentFlyoutOpen && ( - + )} From 38ec56a49b71d2c7b5b1d5adc9f0cb086517fea3 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 17:28:22 +0200 Subject: [PATCH 09/20] fixed navigation bug and added a comment --- .../create_package_policy_page/index.tsx | 13 ++++++++----- .../package_policies/no_package_policies.tsx | 12 +++++++++--- x-pack/plugins/fleet/public/constants/page_paths.ts | 1 + 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index fc6ef809139615..7d68e060b616a7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -45,7 +45,6 @@ import { useIntraAppState, useUIExtension } from '../../../hooks'; import { ExtensionWrapper } from '../../../components'; import type { PackagePolicyEditExtensionComponentProps } from '../../../types'; import { PLUGIN_ID } from '../../../../../../common/constants'; -import { pagePathGetters } from '../../../../../constants'; import { pkgKeyFromPackageInfo } from '../../../services'; import { CreatePackagePolicyPageLayout } from './components'; @@ -87,11 +86,15 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { const history = useHistory(); const handleNavigateTo = useNavigateToCallback(); const routeState = useIntraAppState(); - const from: CreatePackagePolicyFrom = 'policyId' in params ? 'policy' : 'package'; const { search } = useLocation(); const queryParams = useMemo(() => new URLSearchParams(search), [search]); - const policyId = useMemo(() => queryParams.get('policyId') ?? undefined, [queryParams]); + const queryParamsPolicyId = useMemo(() => queryParams.get('policyId') ?? undefined, [ + queryParams, + ]); + + const from: CreatePackagePolicyFrom = + 'policyId' in params || queryParamsPolicyId ? 'policy' : 'package'; // Agent policy and package info states const [agentPolicy, setAgentPolicy] = useState(); @@ -390,13 +393,13 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { ), - [params, updatePackageInfo, agentPolicy, updateAgentPolicy, policyId] + [params, updatePackageInfo, agentPolicy, updateAgentPolicy, queryParamsPolicyId] ); const ExtensionView = useUIExtension(packagePolicy.package?.name ?? '', 'package-policy-create'); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx index 54adbd78ab75aa..39340a21d349bd 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/package_policies/no_package_policies.tsx @@ -9,10 +9,11 @@ import React, { memo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; -import { useCapabilities, useLink } from '../../../../../hooks'; +import { useCapabilities, useStartServices } from '../../../../../hooks'; +import { pagePathGetters, INTEGRATIONS_PLUGIN_ID } from '../../../../../constants'; export const NoPackagePolicies = memo<{ policyId: string }>(({ policyId }) => { - const { getHref } = useLink(); + const { application } = useStartServices(); const hasWriteCapabilities = useCapabilities().write; return ( @@ -36,7 +37,12 @@ export const NoPackagePolicies = memo<{ policyId: string }>(({ policyId }) => { + application.navigateToApp(INTEGRATIONS_PLUGIN_ID, { + path: `#${pagePathGetters.integrations_all()[1]}`, + state: { forAgentPolicyId: policyId }, + }) + } > [ FLEET_BASE_PATH, `/policies/${policyId}/add-integration`, From 363e749763bc6a01952ed6dc13dbfee3ded53693 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 17 Jun 2021 17:33:26 +0200 Subject: [PATCH 10/20] added a lot more padding to bottom of form --- .../sections/agent_policy/create_package_policy_page/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index 7d68e060b616a7..e0dcd1496e74a7 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -495,7 +495,8 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { )} - + + From 756a871873ef3b37f6e51f86ec9ab4814ebc8fb9 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 18 Jun 2021 10:59:35 +0200 Subject: [PATCH 11/20] restructured code for clarity, updated deprecation comments and moved relevant code closer together --- .../epm/screens/detail/assets/assets.tsx | 47 ++++--------------- x-pack/plugins/fleet/public/hooks/index.ts | 2 +- .../fleet/public/hooks/use_kibana_link.ts | 46 ++++++++++++++++-- 3 files changed, 52 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx index 5f50ecb223ade0..554f8bf1a07743 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -31,7 +31,7 @@ import { useGetPackageInstallStatus, useLink, useStartServices, - getKibanaLink, + getHrefToObjectInKibanaApp, } from '../../../../../hooks'; import { AssetTitleMap } from '../../../../../constants'; @@ -54,33 +54,6 @@ const allowedAssetTypes: AllowedAssetTypes = [ type AssetSavedObject = SimpleSavedObject<{ title: string; description?: string }>; -/** - * TODO: This is a temporary solution for getting links to various assets. It is very risky because: - * - * 1. The plugin might not exist/be enabled - * 2. URLs and paths might not always be supported - * - * We should migrate to using the new URL service locators. - */ -const getPathToObjectInApp = ({ - type, - id, -}: { - type: KibanaAssetType; - id: string; -}): undefined | string => { - switch (type) { - case KibanaAssetType.dashboard: - return `/dashboard/${id}`; - case KibanaAssetType.search: - return `/discover/${id}`; - case KibanaAssetType.visualization: - return `/visualize/edit/${id}`; - default: - return undefined; - } -}; - export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { const { name, version } = packageInfo; const { @@ -112,12 +85,11 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { } try { - const { savedObjects } = await savedObjectsClient.bulkGet( - packageAttributes.installed_kibana.map(({ id, type }) => ({ - id, - type, - })) - ); + const objectsToGet = packageAttributes.installed_kibana.map(({ id, type }) => ({ + id, + type, + })); + const { savedObjects } = await savedObjectsClient.bulkGet(objectsToGet); setAssetsSavedObjects(savedObjects as AssetSavedObject[]); } catch (e) { setFetchError(e); @@ -200,7 +172,8 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { > {sectionAssetSavedObjects.map( ({ id, type, attributes: { title, description } }, idx) => { - const pathToObjectInApp = getPathToObjectInApp({ + const pathToObjectInApp = getHrefToObjectInKibanaApp({ + http, id, type: type as KibanaAssetType, }); @@ -210,9 +183,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {

{pathToObjectInApp ? ( - - {title} - + {title} ) : ( title )} diff --git a/x-pack/plugins/fleet/public/hooks/index.ts b/x-pack/plugins/fleet/public/hooks/index.ts index 1258a47f95015e..a00c0c5dacf111 100644 --- a/x-pack/plugins/fleet/public/hooks/index.ts +++ b/x-pack/plugins/fleet/public/hooks/index.ts @@ -11,7 +11,7 @@ export { useConfig, ConfigContext } from './use_config'; export { useKibanaVersion, KibanaVersionContext } from './use_kibana_version'; export { licenseService, useLicense } from './use_license'; export { useLink } from './use_link'; -export { useKibanaLink, getKibanaLink } from './use_kibana_link'; +export { useKibanaLink, getHrefToObjectInKibanaApp } from './use_kibana_link'; export { usePackageIconType, UsePackageIconType } from './use_package_icon_type'; export { usePagination, Pagination, PAGE_SIZE_OPTIONS } from './use_pagination'; export { useUrlPagination } from './use_url_pagination'; diff --git a/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts b/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts index e8ecc9cf5492f4..3ad01620b9780f 100644 --- a/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts +++ b/x-pack/plugins/fleet/public/hooks/use_kibana_link.ts @@ -6,20 +6,58 @@ */ import type { HttpStart } from 'src/core/public'; +import { KibanaAssetType } from '../types'; + import { useStartServices } from './'; const KIBANA_BASE_PATH = '/app/kibana'; +const getKibanaLink = (http: HttpStart, path: string) => { + return http.basePath.prepend(`${KIBANA_BASE_PATH}#${path}`); +}; + /** - * @deprecated See comment on {@link useKibanaLink} + * TODO: This is a temporary solution for getting links to various assets. It is very risky because: + * + * 1. The plugin might not exist/be enabled + * 2. URLs and paths might not always be supported + * + * We should migrate to using the new URL service locators. + * + * @deprecated {@link Locators} from the new URL service need to be used instead. + */ -export const getKibanaLink = (http: HttpStart, path: string) => { - return http.basePath.prepend(`${KIBANA_BASE_PATH}#${path}`); +export const getHrefToObjectInKibanaApp = ({ + type, + id, + http, +}: { + type: KibanaAssetType; + id: string; + http: HttpStart; +}): undefined | string => { + let kibanaAppPath: undefined | string; + switch (type) { + case KibanaAssetType.dashboard: + kibanaAppPath = `/dashboard/${id}`; + break; + case KibanaAssetType.search: + kibanaAppPath = `/discover/${id}`; + break; + case KibanaAssetType.visualization: + kibanaAppPath = `/visualize/edit/${id}`; + break; + default: + return undefined; + } + + return getKibanaLink(http, kibanaAppPath); }; /** * TODO: This functionality needs to be replaced with use of the new URL service locators - * @deprecated + * + * @deprecated {@link Locators} from the new URL service need to be used instead. */ export function useKibanaLink(path: string = '/') { const { http } = useStartServices(); From 2bf99e0304c843c31a39eb5a5781e7ee5a4ce0ac Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 18 Jun 2021 11:07:25 +0200 Subject: [PATCH 12/20] added a longer form comment about the origin policyId --- .../agent_policy/create_package_policy_page/index.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index e0dcd1496e74a7..d3a31c3828b3f0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -93,6 +93,17 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { queryParams, ]); + /** + * Please note: policyId can come from one of two sources. The URL param (in the URL path) or + * in the query params (?policyId=foo). + * + * Either way, we take this as an indication that a user is "coming from" the fleet policy UI + * since we link them out to packages (a.k.a. integrations) UI when choosing a new package. It is + * no longer possible to choose a package directly in the create package form. + * + * We may want to deprecate the ability to pass in policyId from URL params since there is no package + * creation possible if a user has not chosen one from the packages UI. + */ const from: CreatePackagePolicyFrom = 'policyId' in params || queryParamsPolicyId ? 'policy' : 'package'; From 8f255facdaab6c634ed3acb7de590aae9fd148d6 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 18 Jun 2021 11:13:46 +0200 Subject: [PATCH 13/20] added logic for handling load error --- .../sections/epm/screens/detail/assets/assets.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx index 554f8bf1a07743..3e8eaf99fe19f4 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets.tsx @@ -22,7 +22,7 @@ import { EuiHorizontalRule, } from '@elastic/eui'; -import { Loading } from '../../../../../components'; +import { Loading, Error } from '../../../../../components'; import type { PackageInfo } from '../../../../../types'; import { InstallStatus, KibanaAssetType } from '../../../../../types'; @@ -120,7 +120,17 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { if (isLoading) { content = ; } else if (fetchError) { - content =

TODO HANDLE ERROR

; + content = ( + + } + error={fetchError} + /> + ); } else if (assetSavedObjects === undefined) { content = ( From fdd6fc056f5423a60a503b69dd873351e4dc3090 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 18 Jun 2021 11:41:41 +0200 Subject: [PATCH 14/20] refactor assets accordions out of assets page component --- .../create_package_policy_page/index.tsx | 1 + .../epm/screens/detail/assets/assets.tsx | 137 +++--------------- .../detail/assets/assets_accordion.tsx | 89 ++++++++++++ .../epm/screens/detail/assets/constants.ts | 16 ++ .../epm/screens/detail/assets/types.ts | 20 +++ 5 files changed, 147 insertions(+), 116 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/constants.ts create mode 100644 x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/types.ts diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index d3a31c3828b3f0..b3b0d6ed51cb4d 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -323,6 +323,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { ) : fromPackageWithoutAgentsAssigned ? toMountPoint( + // To render the link below we need to mount this JSX in the success toast ; - export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { const { name, version } = packageInfo; const { savedObjects: { client: savedObjectsClient }, - http, } = useStartServices(); const { getPath } = useLink(); @@ -115,7 +85,7 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { ); } - let content: JSX.Element; + let content: JSX.Element | Array; if (isLoading) { content = ; @@ -143,85 +113,20 @@ export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => { ); } else { - content = ( - <> - {allowedAssetTypes.map((assetType) => { - const sectionAssetSavedObjects = assetSavedObjects.filter((so) => so.type === assetType); - if (!sectionAssetSavedObjects.length) { - return null; - } - - return ( - <> - - - -

{AssetTitleMap[assetType]}

-
-
- - {sectionAssetSavedObjects.length} - - - } - id={assetType} - > - <> - - - {sectionAssetSavedObjects.map( - ({ id, type, attributes: { title, description } }, idx) => { - const pathToObjectInApp = getHrefToObjectInKibanaApp({ - http, - id, - type: type as KibanaAssetType, - }); - return ( - <> - - -

- {pathToObjectInApp ? ( - {title} - ) : ( - title - )} -

-
- - {description && ( - -

{description}

-
- )} -
- {idx + 1 < sectionAssetSavedObjects.length && ( - - )} - - ); - } - )} -
- -
- - - ); - })} - - ); + content = allowedAssetTypes.map((assetType) => { + const sectionAssetSavedObjects = assetSavedObjects.filter((so) => so.type === assetType); + + if (!sectionAssetSavedObjects.length) { + return null; + } + + return ( + <> + + + + ); + }); } return ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx new file mode 100644 index 00000000000000..b9bc53e09982d4 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx @@ -0,0 +1,89 @@ +/* + * 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 React from 'react'; +import type { FunctionComponent } from 'react'; + +import { + EuiAccordion, + EuiFlexGroup, + EuiFlexItem, + EuiSplitPanel, + EuiBadge, + EuiSpacer, + EuiTitle, + EuiText, + EuiLink, + EuiHorizontalRule, +} from '@elastic/eui'; + +import { AssetTitleMap } from '../../../../../constants'; + +import { getHrefToObjectInKibanaApp, useStartServices } from '../../../../../hooks'; + +import type { AllowedAssetType, AssetSavedObject } from './types'; + +interface Props { + type: AllowedAssetType; + savedObjects: AssetSavedObject[]; +} + +export const AssetsAccordion: FunctionComponent = ({ savedObjects, type }) => { + const { http } = useStartServices(); + return ( + + + +

{AssetTitleMap[type]}

+
+
+ + {savedObjects.length} + + + } + id={type} + > + <> + + + {savedObjects.map(({ id, attributes: { title, description } }, idx) => { + const pathToObjectInApp = getHrefToObjectInKibanaApp({ + http, + id, + type, + }); + return ( + <> + + +

+ {pathToObjectInApp ? ( + {title} + ) : ( + title + )} +

+
+ + {description && ( + +

{description}

+
+ )} +
+ {idx + 1 < savedObjects.length && } + + ); + })} +
+ +
+ ); +}; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/constants.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/constants.ts new file mode 100644 index 00000000000000..d6d88f7935eb44 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/constants.ts @@ -0,0 +1,16 @@ +/* + * 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 { KibanaAssetType } from '../../../../../types'; + +import type { AllowedAssetTypes } from './types'; + +export const allowedAssetTypes: AllowedAssetTypes = [ + KibanaAssetType.dashboard, + KibanaAssetType.search, + KibanaAssetType.visualization, +]; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/types.ts b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/types.ts new file mode 100644 index 00000000000000..21efd1cd562e80 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/types.ts @@ -0,0 +1,20 @@ +/* + * 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 { SimpleSavedObject } from 'src/core/public'; + +import type { KibanaAssetType } from '../../../../../types'; + +export type AssetSavedObject = SimpleSavedObject<{ title: string; description?: string }>; + +export type AllowedAssetTypes = [ + KibanaAssetType.dashboard, + KibanaAssetType.search, + KibanaAssetType.visualization +]; + +export type AllowedAssetType = AllowedAssetTypes[number]; From 8a1170ddc49f6867aea3587d033a3932c5429312 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 18 Jun 2021 11:43:03 +0200 Subject: [PATCH 15/20] slightly larger text in badge --- .../sections/epm/screens/detail/assets/assets_accordion.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx index b9bc53e09982d4..b7d3b0454c9d03 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx @@ -44,7 +44,9 @@ export const AssetsAccordion: FunctionComponent = ({ savedObjects, type } - {savedObjects.length} + + {savedObjects.length} + } From 38e836f6f5e98a5a0ae799637cce24ce75db43ea Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 18 Jun 2021 11:54:48 +0200 Subject: [PATCH 16/20] added some basic jest test for view data step in enrollment flyout --- .../agent_enrollment_flyout.test.mocks.ts | 1 + .../agent_enrollment_flyout.test.tsx | 42 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts index f1055e7e2583ee..fcf10785664986 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.mocks.ts @@ -37,6 +37,7 @@ jest.mock('./steps', () => { ...module, AgentPolicySelectionStep: jest.fn(), AgentEnrollmentKeySelectionStep: jest.fn(), + ViewDataStep: jest.fn(), }; }); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx index db9245b11b0f99..65118044e98c56 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_enrollment_flyout.test.tsx @@ -21,7 +21,7 @@ import { FleetStatusProvider, ConfigContext } from '../../hooks'; import { useFleetServerInstructions } from '../../applications/fleet/sections/agents/agent_requirements_page'; -import { AgentEnrollmentKeySelectionStep, AgentPolicySelectionStep } from './steps'; +import { AgentEnrollmentKeySelectionStep, AgentPolicySelectionStep, ViewDataStep } from './steps'; import type { Props } from '.'; import { AgentEnrollmentFlyout } from '.'; @@ -128,6 +128,46 @@ describe('', () => { expect(AgentEnrollmentKeySelectionStep).toHaveBeenCalled(); }); }); + + describe('"View data" extension point', () => { + it('calls the "View data" step when UI extension is provided', async () => { + jest.clearAllMocks(); + await act(async () => { + testBed = await setup({ + agentPolicies: [], + onClose: jest.fn(), + viewDataStepContent:
, + }); + testBed.component.update(); + }); + const { exists, actions } = testBed; + expect(exists('agentEnrollmentFlyout')).toBe(true); + expect(ViewDataStep).toHaveBeenCalled(); + + jest.clearAllMocks(); + actions.goToStandaloneTab(); + expect(ViewDataStep).not.toHaveBeenCalled(); + }); + + it('does not call the "View data" step when UI extension is not provided', async () => { + jest.clearAllMocks(); + await act(async () => { + testBed = await setup({ + agentPolicies: [], + onClose: jest.fn(), + viewDataStepContent: undefined, + }); + testBed.component.update(); + }); + const { exists, actions } = testBed; + expect(exists('agentEnrollmentFlyout')).toBe(true); + expect(ViewDataStep).not.toHaveBeenCalled(); + + jest.clearAllMocks(); + actions.goToStandaloneTab(); + expect(ViewDataStep).not.toHaveBeenCalled(); + }); + }); }); describe('standalone instructions', () => { From f5db4277a85921203c29ad7daaf73e7df62c5b2f Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 18 Jun 2021 11:59:35 +0200 Subject: [PATCH 17/20] adjusted sizing of numbers in badges again, EuiText does not know about size="l" --- .../sections/epm/screens/detail/assets/assets_accordion.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx index b7d3b0454c9d03..ae06a1a77d9686 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx @@ -45,7 +45,9 @@ export const AssetsAccordion: FunctionComponent = ({ savedObjects, type } - {savedObjects.length} + +

{savedObjects.length}

+
From 96e1d14c08de9c6028205b564a59c963614af608 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 21 Jun 2021 09:48:46 +0200 Subject: [PATCH 18/20] updated size limits for fleet --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 9adc075a7983f8..f9127e4629f43e 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -34,7 +34,7 @@ pageLoadAssetSize: indexManagement: 140608 indexPatternManagement: 28222 infra: 184320 - fleet: 450005 + fleet: 465774 ingestPipelines: 58003 inputControlVis: 172675 inspector: 148711 From dabcb2d62f3c426552014795d8a43b5b9e76b220 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 21 Jun 2021 16:00:36 +0200 Subject: [PATCH 19/20] updated styling and layout of assets accordion based on original designs --- .../detail/assets/assets_accordion.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx index ae06a1a77d9686..85a3710f3518fa 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx @@ -13,12 +13,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiSplitPanel, - EuiBadge, EuiSpacer, EuiTitle, EuiText, EuiLink, EuiHorizontalRule, + EuiNotificationBadge, } from '@elastic/eui'; import { AssetTitleMap } from '../../../../../constants'; @@ -39,23 +39,21 @@ export const AssetsAccordion: FunctionComponent = ({ savedObjects, type } buttonContent={ - +

{AssetTitleMap[type]}

-
+
- - -

{savedObjects.length}

-
-
+ +

{savedObjects.length}

+
} id={type} > <> - + {savedObjects.map(({ id, attributes: { title, description } }, idx) => { const pathToObjectInApp = getHrefToObjectInKibanaApp({ @@ -75,11 +73,13 @@ export const AssetsAccordion: FunctionComponent = ({ savedObjects, type } )}

- {description && ( - -

{description}

-
+ <> + + +

{description}

+
+ )} {idx + 1 < savedObjects.length && } From 7ebf29160f0ef9f3a7f8f85130939354a9326b01 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 21 Jun 2021 16:54:26 +0200 Subject: [PATCH 20/20] remove unused EuiTitle --- .../sections/epm/screens/detail/assets/assets_accordion.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx index 85a3710f3518fa..abfdd88d271622 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/assets/assets_accordion.tsx @@ -14,7 +14,6 @@ import { EuiFlexItem, EuiSplitPanel, EuiSpacer, - EuiTitle, EuiText, EuiLink, EuiHorizontalRule,