Skip to content

Commit

Permalink
[Fleet] Refactor of install package button code (#131015)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
hop-dev authored Apr 27, 2022
1 parent 349cca2 commit f62530b
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -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>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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({
Expand All @@ -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,
Expand Down Expand Up @@ -375,45 +322,19 @@ export function Detail() {
{ isDivider: true },
{
content: (
<EuiButtonWithTooltip
fill
isDisabled={!userCanInstallPackages}
iconType="plusInCircle"
<AddIntegrationButton
userCanInstallPackages={userCanInstallPackages}
href={getHref('add_integration_to_policy', {
pkgkey,
...(integration ? { integration } : {}),
...(agentPolicyIdFromContext
? { 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) => (
Expand Down
Original file line number Diff line number Diff line change
@@ -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]);
});
});
Original file line number Diff line number Diff line change
@@ -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 }];
};
Loading

0 comments on commit f62530b

Please sign in to comment.