Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fleet] Add assets tab #102517

Merged
merged 23 commits into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e81acb8
very wip
jloleysens Jun 17, 2021
2c45e7d
Finished styling the assets page layout, need to work on adding
jloleysens Jun 17, 2021
815d0b6
rather use EuiHorizontalRule
jloleysens Jun 17, 2021
3c9912b
only show the assets tab if installed
jloleysens Jun 17, 2021
3dfd692
Added hacky version of linking to assets.
jloleysens Jun 17, 2021
8669f7e
added comment about deprecation of current linking functionality
jloleysens Jun 17, 2021
646c583
added an initial version of the success toast with a link to the agen…
jloleysens Jun 17, 2021
f381f91
First iteration of end-to-end UX working. Need to add a lot of tests!
jloleysens Jun 17, 2021
38ec56a
fixed navigation bug and added a comment
jloleysens Jun 17, 2021
363e749
added a lot more padding to bottom of form
jloleysens Jun 17, 2021
756a871
restructured code for clarity, updated deprecation comments and moved…
jloleysens Jun 18, 2021
2bf99e0
added a longer form comment about the origin policyId
jloleysens Jun 18, 2021
8f255fa
added logic for handling load error
jloleysens Jun 18, 2021
fdd6fc0
refactor assets accordions out of assets page component
jloleysens Jun 18, 2021
8a1170d
slightly larger text in badge
jloleysens Jun 18, 2021
38e836f
added some basic jest test for view data step in enrollment flyout
jloleysens Jun 18, 2021
f5db427
adjusted sizing of numbers in badges again, EuiText does not know abo…
jloleysens Jun 18, 2021
5800c2a
Merge branch 'master' of github.com:elastic/kibana into fleet/add-ass…
jloleysens Jun 18, 2021
79a508e
Merge branch 'master' into fleet/add-assets-tab
kibanamachine Jun 21, 2021
96e1d14
updated size limits for fleet
jloleysens Jun 21, 2021
dabcb2d
updated styling and layout of assets accordion based on original
jloleysens Jun 21, 2021
7ebf291
remove unused EuiTitle
jloleysens Jun 21, 2021
27dc4d1
Merge branch 'master' into fleet/add-assets-tab
kibanamachine Jun 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pageLoadAssetSize:
indexManagement: 140608
indexPatternManagement: 28222
infra: 184320
fleet: 450005
fleet: 465774
ingestPipelines: 58003
inputControlVis: 172675
inspector: 148711
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/fleet/common/types/models/epm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -60,7 +62,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;
`;

Expand All @@ -84,11 +86,26 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => {
const history = useHistory();
const handleNavigateTo = useNavigateToCallback();
const routeState = useIntraAppState<CreatePackagePolicyRouteState>();
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,
]);

/**
* 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';

// Agent policy and package info states
const [agentPolicy, setAgentPolicy] = useState<AgentPolicy | undefined>();
Expand Down Expand Up @@ -280,29 +297,61 @@ 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.`,
values: {
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(
jloleysens marked this conversation as resolved.
Show resolved Hide resolved
// To render the link below we need to mount this JSX in the success toast
<FormattedMessage
id="xpack.fleet.createPackagePolicy.integrationsContextaddAgentNextNotificationMessage"
defaultMessage="Next, {link} to start ingesting data."
values={{
link: (
<EuiLink
href={getHref('integration_details_policies', {
pkgkey: `${packageInfo!.name}-${packageInfo!.version}`,
addAgentToPolicyId: agentPolicy!.id,
})}
>
{i18n.translate(
'xpack.fleet.createPackagePolicy.integrationsContextAddAgentLinkMessage',
{ defaultMessage: 'add an agent' }
)}
</EuiLink>
),
}}
/>
)
: 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 {
Expand All @@ -312,6 +361,9 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => {
setFormState('VALID');
}
}, [
getHref,
from,
packageInfo,
agentCount,
agentPolicy,
formState,
Expand Down Expand Up @@ -353,13 +405,13 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => {
<StepSelectAgentPolicy
pkgkey={(params as AddToPolicyParams).pkgkey}
updatePackageInfo={updatePackageInfo}
defaultAgentPolicyId={policyId}
defaultAgentPolicyId={queryParamsPolicyId}
agentPolicy={agentPolicy}
updateAgentPolicy={updateAgentPolicy}
setIsLoadingSecondStep={setIsLoadingAgentPolicyStep}
/>
),
[params, updatePackageInfo, agentPolicy, updateAgentPolicy, policyId]
[params, updatePackageInfo, agentPolicy, updateAgentPolicy, queryParamsPolicyId]
);

const ExtensionView = useUIExtension(packagePolicy.package?.name ?? '', 'package-policy-create');
Expand Down Expand Up @@ -455,7 +507,8 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => {
<PolicyBreadcrumb policyName={agentPolicy.name} policyId={agentPolicy.id} />
)}
<StepsWithLessPadding steps={steps} />
<EuiSpacer size="l" />
<EuiSpacer size="xl" />
<EuiSpacer size="xl" />
<CustomEuiBottomBar data-test-subj="integrationsBottomBar">
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -36,7 +37,12 @@ export const NoPackagePolicies = memo<{ policyId: string }>(({ policyId }) => {
<EuiButton
isDisabled={!hasWriteCapabilities}
fill
href={getHref('add_integration_from_policy', { policyId })}
onClick={() =>
application.navigateToApp(INTEGRATIONS_PLUGIN_ID, {
path: `#${pagePathGetters.integrations_all()[1]}`,
state: { forAgentPolicyId: policyId },
})
}
>
<FormattedMessage
id="xpack.fleet.policyDetailsPackagePolicies.createFirstButtonText"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* 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 { FormattedMessage } from '@kbn/i18n/react';
import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer } from '@elastic/eui';

import { Loading, Error } from '../../../../../components';

import type { PackageInfo } from '../../../../../types';
import { InstallStatus } from '../../../../../types';

import { useGetPackageInstallStatus, useLink, useStartServices } from '../../../../../hooks';

import type { AssetSavedObject } from './types';
import { allowedAssetTypes } from './constants';
import { AssetsAccordion } from './assets_accordion';

interface AssetsPanelProps {
packageInfo: PackageInfo;
}

export const AssetsPage = ({ packageInfo }: AssetsPanelProps) => {
const { name, version } = packageInfo;
const {
savedObjects: { client: savedObjectsClient },
} = useStartServices();

const { getPath } = useLink();
const getPackageInstallStatus = useGetPackageInstallStatus();
const packageInstallStatus = getPackageInstallStatus(packageInfo.name);

const [assetSavedObjects, setAssetsSavedObjects] = useState<undefined | AssetSavedObject[]>();
const [fetchError, setFetchError] = useState<undefined | Error>();
const [isLoading, setIsLoading] = useState<boolean>(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 objectsToGet = packageAttributes.installed_kibana.map(({ id, type }) => ({
id,
type,
}));
const { savedObjects } = await savedObjectsClient.bulkGet(objectsToGet);
setAssetsSavedObjects(savedObjects as AssetSavedObject[]);
} 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 (
<Redirect
to={getPath('integration_details_overview', {
pkgkey: `${name}-${version}`,
})}
/>
);
}

let content: JSX.Element | Array<JSX.Element | null>;

if (isLoading) {
content = <Loading />;
} else if (fetchError) {
content = (
<Error
title={
<FormattedMessage
id="xpack.fleet.epm.packageDetails.assets.fetchAssetsErrorTitle"
defaultMessage="Error loading assets"
/>
}
error={fetchError}
/>
);
} else if (assetSavedObjects === undefined) {
content = (
<EuiTitle>
<h2>
<FormattedMessage
id="xpack.fleet.epm.packageDetails.assets.noAssetsFoundLabel"
defaultMessage="No assets found"
/>
</h2>
</EuiTitle>
);
} else {
content = allowedAssetTypes.map((assetType) => {
const sectionAssetSavedObjects = assetSavedObjects.filter((so) => so.type === assetType);

if (!sectionAssetSavedObjects.length) {
return null;
}

return (
<>
<AssetsAccordion savedObjects={sectionAssetSavedObjects} type={assetType} />
<EuiSpacer size="l" />
</>
);
});
}

return (
<EuiFlexGroup alignItems="flexStart">
<EuiFlexItem grow={1} />
<EuiFlexItem grow={6}>{content}</EuiFlexItem>
</EuiFlexGroup>
);
};
Loading