[Fleet] Refactor of install package button code (#131015)

* move install integration button to separate component

* move install path generation to util function

* small refactor

* add unit tests

* rorder deps to make diff less noisy
This commit is contained in:
Mark Hopkin 2022-04-27 09:34:29 +01:00 committed by GitHub
parent 349cca2b77
commit f62530bae9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 243 additions and 102 deletions

View file

@ -0,0 +1,60 @@
/*
* 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 { FormattedMessage } from '@kbn/i18n-react';
import { EuiButtonWithTooltip } from '../../../../../components';
interface AddIntegrationButtonProps {
userCanInstallPackages?: boolean;
missingSecurityConfiguration: boolean;
packageName: string;
href: string;
onClick: Function;
}
export function AddIntegrationButton(props: AddIntegrationButtonProps) {
const { userCanInstallPackages, missingSecurityConfiguration, packageName, href, onClick } =
props;
const tooltip = !userCanInstallPackages
? {
content: missingSecurityConfiguration ? (
<FormattedMessage
id="xpack.fleet.epm.addPackagePolicyButtonSecurityRequiredTooltip"
defaultMessage="To add Elastic Agent Integrations, you must have security enabled and have the All privilege for Fleet. Contact your administrator."
/>
) : (
<FormattedMessage
id="xpack.fleet.epm.addPackagePolicyButtonPrivilegesRequiredTooltip"
defaultMessage="Elastic Agent Integrations require the All privilege for Fleet and All privilege for Integrations. Contact your administrator."
/>
),
}
: undefined;
return (
<EuiButtonWithTooltip
fill
isDisabled={!userCanInstallPackages}
iconType="plusInCircle"
href={href}
onClick={(e) => onClick(e)}
data-test-subj="addIntegrationPolicyButton"
tooltip={tooltip}
>
<FormattedMessage
id="xpack.fleet.epm.addPackagePolicyButtonText"
defaultMessage="Add {packageName}"
values={{
packageName,
}}
/>
</EuiButtonWithTooltip>
);
}

View file

@ -4,6 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { AddIntegrationButton } from './add_integration_button';
export { UpdateIcon } from './update_icon';
export { IntegrationAgentPolicyCount } from './integration_agent_policy_count';
export { IconPanel, LoadingIconPanel } from './icon_panel';

View file

@ -35,26 +35,25 @@ import {
useAuthz,
usePermissionCheck,
} from '../../../../hooks';
import {
PLUGIN_ID,
INTEGRATIONS_PLUGIN_ID,
INTEGRATIONS_ROUTING_PATHS,
pagePathGetters,
} from '../../../../constants';
import { INTEGRATIONS_ROUTING_PATHS } from '../../../../constants';
import { useGetPackageInfoByKey, useLink, useAgentPolicyContext } from '../../../../hooks';
import { pkgKeyFromPackageInfo } from '../../../../services';
import type {
CreatePackagePolicyRouteState,
DetailViewPanelName,
PackageInfo,
} from '../../../../types';
import type { DetailViewPanelName, PackageInfo } from '../../../../types';
import { InstallStatus } from '../../../../types';
import { Error, EuiButtonWithTooltip, Loading } from '../../../../components';
import { Error, Loading } from '../../../../components';
import type { WithHeaderLayoutProps } from '../../../../layouts';
import { WithHeaderLayout } from '../../../../layouts';
import { RELEASE_BADGE_DESCRIPTION, RELEASE_BADGE_LABEL } from '../../components/release_badge';
import { IntegrationAgentPolicyCount, UpdateIcon, IconPanel, LoadingIconPanel } from './components';
import { getInstallPkgRouteOptions } from './utils';
import {
IntegrationAgentPolicyCount,
UpdateIcon,
IconPanel,
LoadingIconPanel,
AddIntegrationButton,
} from './components';
import { AssetsPage } from './assets';
import { OverviewPage } from './overview';
import { PackagePoliciesPage } from './policies';
@ -257,7 +256,6 @@ export function Detail() {
const handleAddIntegrationPolicyClick = useCallback<ReactEventHandler>(
(ev) => {
ev.preventDefault();
// The object below, given to `createHref` is explicitly accessing keys of `location` in order
// to ensure that dependencies to this `useCallback` is set correctly (because `location` is mutable)
const currentPath = history.createHref({
@ -266,65 +264,14 @@ export function Detail() {
hash,
});
const path = pagePathGetters.add_integration_to_policy({
const navigateOptions = getInstallPkgRouteOptions({
currentPath,
integration,
agentPolicyId: agentPolicyIdFromContext,
pkgkey,
...(integration ? { integration } : {}),
...(agentPolicyIdFromContext ? { agentPolicyId: agentPolicyIdFromContext } : {}),
})[1];
let redirectToPath: CreatePackagePolicyRouteState['onSaveNavigateTo'] &
CreatePackagePolicyRouteState['onCancelNavigateTo'];
let onSaveQueryParams: CreatePackagePolicyRouteState['onSaveQueryParams'];
if (agentPolicyIdFromContext) {
redirectToPath = [
PLUGIN_ID,
{
path: pagePathGetters.policy_details({
policyId: agentPolicyIdFromContext,
})[1],
},
];
onSaveQueryParams = {
showAddAgentHelp: true,
openEnrollmentFlyout: true,
};
} else {
redirectToPath = [
INTEGRATIONS_PLUGIN_ID,
{
path: pagePathGetters.integration_details_policies({
pkgkey,
...(integration ? { integration } : {}),
})[1],
},
];
onSaveQueryParams = {
showAddAgentHelp: { renameKey: 'showAddAgentHelpForPolicyId', policyIdAsValue: true },
openEnrollmentFlyout: { renameKey: 'addAgentToPolicyId', policyIdAsValue: true },
};
}
const redirectBackRouteState: CreatePackagePolicyRouteState = {
onSaveNavigateTo: redirectToPath,
onSaveQueryParams,
onCancelNavigateTo: [
INTEGRATIONS_PLUGIN_ID,
{
path: pagePathGetters.integration_details_overview({
pkgkey,
...(integration ? { integration } : {}),
})[1],
},
],
onCancelUrl: currentPath,
};
services.application.navigateToApp(PLUGIN_ID, {
path,
state: redirectBackRouteState,
});
services.application.navigateToApp(...navigateOptions);
},
[
history,
@ -375,10 +322,8 @@ export function Detail() {
{ isDivider: true },
{
content: (
<EuiButtonWithTooltip
fill
isDisabled={!userCanInstallPackages}
iconType="plusInCircle"
<AddIntegrationButton
userCanInstallPackages={userCanInstallPackages}
href={getHref('add_integration_to_policy', {
pkgkey,
...(integration ? { integration } : {}),
@ -386,34 +331,10 @@ export function Detail() {
? { agentPolicyId: agentPolicyIdFromContext }
: {}),
})}
missingSecurityConfiguration={missingSecurityConfiguration}
packageName={integrationInfo?.title || packageInfo.title}
onClick={handleAddIntegrationPolicyClick}
data-test-subj="addIntegrationPolicyButton"
tooltip={
!userCanInstallPackages
? {
content: missingSecurityConfiguration ? (
<FormattedMessage
id="xpack.fleet.epm.addPackagePolicyButtonSecurityRequiredTooltip"
defaultMessage="To add Elastic Agent Integrations, you must have security enabled and have the All privilege for Fleet. Contact your administrator."
/>
) : (
<FormattedMessage
id="xpack.fleet.epm.addPackagePolicyButtonPrivilegesRequiredTooltip"
defaultMessage="Elastic Agent Integrations require the All privilege for Fleet and All privilege for Integrations. Contact your administrator."
/>
),
}
: undefined
}
>
<FormattedMessage
id="xpack.fleet.epm.addPackagePolicyButtonText"
defaultMessage="Add {packageName}"
values={{
packageName: integrationInfo?.title || packageInfo.title,
}}
/>
</EuiButtonWithTooltip>
/>
),
},
].map((item, index) => (

View file

@ -0,0 +1,69 @@
/*
* 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 { getInstallPkgRouteOptions } from '.';
// this is always the same
const expectedOnCancelNavigateTo = [
'integrations',
{
path: '/detail/myintegration-1.0.0/overview?integration=myintegration',
},
];
describe('getInstallPkgRouteOptions', () => {
it('should redirect to integrations app on save if no agentPolicyId present', () => {
const opts = {
currentPath: 'currentPath',
integration: 'myintegration',
pkgkey: 'myintegration-1.0.0',
};
const expectedRedirectURl = '/detail/myintegration-1.0.0/policies?integration=myintegration';
const expectedOptions = {
path: '/integrations/myintegration-1.0.0/add-integration/myintegration',
state: {
onCancelUrl: 'currentPath',
onCancelNavigateTo: expectedOnCancelNavigateTo,
onSaveNavigateTo: ['integrations', { path: expectedRedirectURl }],
onSaveQueryParams: {
showAddAgentHelp: { renameKey: 'showAddAgentHelpForPolicyId', policyIdAsValue: true },
openEnrollmentFlyout: { renameKey: 'addAgentToPolicyId', policyIdAsValue: true },
},
},
};
expect(getInstallPkgRouteOptions(opts)).toEqual(['fleet', expectedOptions]);
});
it('should redirect to fleet app on save if agentPolicyId present', () => {
const opts = {
currentPath: 'currentPath',
integration: 'myintegration',
pkgkey: 'myintegration-1.0.0',
agentPolicyId: '12345',
};
const expectedRedirectURl = '/policies/12345';
const expectedOptions = {
path: '/integrations/myintegration-1.0.0/add-integration/myintegration?policyId=12345',
state: {
onCancelUrl: 'currentPath',
onCancelNavigateTo: expectedOnCancelNavigateTo,
onSaveNavigateTo: ['fleet', { path: expectedRedirectURl }],
onSaveQueryParams: {
showAddAgentHelp: true,
openEnrollmentFlyout: true,
},
},
};
expect(getInstallPkgRouteOptions(opts)).toEqual(['fleet', expectedOptions]);
});
});

View file

@ -0,0 +1,82 @@
/*
* 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 { CreatePackagePolicyRouteState } from '../../../../../types';
import { PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, pagePathGetters } from '../../../../../constants';
/*
* When the install package button is pressed, this fn decides which page to navigate to
* by generating the options to be passed to `services.application.navigateToApp`.
*/
export const getInstallPkgRouteOptions = ({
currentPath,
integration,
agentPolicyId,
pkgkey,
}: {
currentPath: string;
integration: string | null;
agentPolicyId?: string;
pkgkey: string;
}): [string, { path: string; state: unknown }] => {
const integrationOpts: { integration?: string } = integration ? { integration } : {};
const path = pagePathGetters.add_integration_to_policy({
pkgkey,
...integrationOpts,
...(agentPolicyId ? { agentPolicyId } : {}),
})[1];
let redirectToPath: CreatePackagePolicyRouteState['onSaveNavigateTo'] &
CreatePackagePolicyRouteState['onCancelNavigateTo'];
let onSaveQueryParams: CreatePackagePolicyRouteState['onSaveQueryParams'];
if (agentPolicyId) {
redirectToPath = [
PLUGIN_ID,
{
path: pagePathGetters.policy_details({
policyId: agentPolicyId,
})[1],
},
];
onSaveQueryParams = {
showAddAgentHelp: true,
openEnrollmentFlyout: true,
};
} else {
redirectToPath = [
INTEGRATIONS_PLUGIN_ID,
{
path: pagePathGetters.integration_details_policies({
pkgkey,
...integrationOpts,
})[1],
},
];
onSaveQueryParams = {
showAddAgentHelp: { renameKey: 'showAddAgentHelpForPolicyId', policyIdAsValue: true },
openEnrollmentFlyout: { renameKey: 'addAgentToPolicyId', policyIdAsValue: true },
};
}
const state: CreatePackagePolicyRouteState = {
onSaveNavigateTo: redirectToPath,
onSaveQueryParams,
onCancelNavigateTo: [
INTEGRATIONS_PLUGIN_ID,
{
path: pagePathGetters.integration_details_overview({
pkgkey,
...integrationOpts,
})[1],
},
],
onCancelUrl: currentPath,
};
return [PLUGIN_ID, { path, state }];
};

View file

@ -0,0 +1,8 @@
/*
* 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 { getInstallPkgRouteOptions } from './get_install_route_options';