[Fleet] disable upgrade action when package does not have upgrade available (#111510) (#111940)

* fix: consolidate hasUpgrade logic

* tidy: move type defs to top of file

* test: add unit tests for actions menu

* tidy: PR feedback

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Mark Hopkin <mark.hopkin@elastic.co>
This commit is contained in:
Kibana Machine 2021-09-13 09:25:43 -04:00 committed by GitHub
parent 1d8ef4c268
commit 94a74f5b47
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 152 additions and 27 deletions

View file

@ -45,16 +45,25 @@ import {
PackagePolicyActionsMenu,
} from '../../../../../components';
import type { PackagePolicyAndAgentPolicy } from './use_package_policies_with_agent_policy';
import { usePackagePoliciesWithAgentPolicy } from './use_package_policies_with_agent_policy';
import { Persona } from './persona';
interface PackagePoliciesPanelProps {
name: string;
version: string;
}
interface InMemoryPackagePolicyAndAgentPolicy {
packagePolicy: InMemoryPackagePolicy;
agentPolicy: GetAgentPoliciesResponseItem;
}
const AddAgentButton = styled(EuiButtonIcon)`
margin-left: ${(props) => props.theme.eui.euiSizeS};
`;
const IntegrationDetailsLink = memo<{
packagePolicy: PackagePolicyAndAgentPolicy['packagePolicy'];
packagePolicy: InMemoryPackagePolicyAndAgentPolicy['packagePolicy'];
}>(({ packagePolicy }) => {
const { getHref } = useLink();
return (
@ -71,10 +80,6 @@ const IntegrationDetailsLink = memo<{
);
});
interface PackagePoliciesPanelProps {
name: string;
version: string;
}
export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps) => {
const { search } = useLocation();
const history = useHistory();
@ -109,12 +114,12 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
const updatableIntegrationRecord = updatableIntegrations.get(
packagePolicy.package?.name ?? ''
);
const hasUpgrade =
!!updatableIntegrationRecord &&
updatableIntegrationRecord.policiesToUpgrade.some(
({ id }) => id === packagePolicy.policy_id
({ pkgPolicyId }) => pkgPolicyId === packagePolicy.id
);
return {
agentPolicy,
packagePolicy: {
@ -143,7 +148,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
}, [history]);
const handleTableOnChange = useCallback(
({ page }: CriteriaWithPagination<PackagePolicyAndAgentPolicy>) => {
({ page }: CriteriaWithPagination<InMemoryPackagePolicyAndAgentPolicy>) => {
setPagination({
currentPage: page.index + 1,
pageSize: page.size,
@ -196,7 +201,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
};
}, [name, version, getHref, agentEnrollmentFlyoutExtension]);
const columns: Array<EuiTableFieldDataColumnType<PackagePolicyAndAgentPolicy>> = useMemo(
const columns: Array<EuiTableFieldDataColumnType<InMemoryPackagePolicyAndAgentPolicy>> = useMemo(
() => [
{
field: 'packagePolicy.name',
@ -213,16 +218,6 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
defaultMessage: 'Version',
}),
render(_version, { agentPolicy, packagePolicy }) {
const updatableIntegrationRecord = updatableIntegrations.get(
packagePolicy.package?.name ?? ''
);
const hasUpgrade =
!!updatableIntegrationRecord &&
updatableIntegrationRecord.policiesToUpgrade.some(
({ pkgPolicyId }) => pkgPolicyId === packagePolicy.id
);
return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
@ -235,7 +230,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
</EuiText>
</EuiFlexItem>
{hasUpgrade && (
{packagePolicy.hasUpgrade && (
<EuiFlexItem grow={false}>
<EuiButton
size="s"
@ -274,7 +269,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
truncateText: true,
align: 'left',
width: '8ch',
render({ packagePolicy, agentPolicy }: PackagePolicyAndAgentPolicy) {
render({ packagePolicy, agentPolicy }: InMemoryPackagePolicyAndAgentPolicy) {
const count = agentPolicy?.agents ?? 0;
return (
@ -327,7 +322,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
defaultMessage: 'Last Updated',
}),
truncateText: true,
render(updatedAt: PackagePolicyAndAgentPolicy['packagePolicy']['updated_at']) {
render(updatedAt: InMemoryPackagePolicyAndAgentPolicy['packagePolicy']['updated_at']) {
return (
<span className="eui-textTruncate" title={updatedAt}>
<FormattedRelative value={updatedAt} />
@ -358,7 +353,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps
},
},
],
[getHref, updatableIntegrations, viewDataStep]
[getHref, viewDataStep]
);
const noItemsMessage = useMemo(() => {

View file

@ -0,0 +1,121 @@
/*
* 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 { act } from '@testing-library/react';
import type { AgentPolicy, InMemoryPackagePolicy } from '../types';
import { createIntegrationsTestRendererMock } from '../mock';
import { PackagePolicyActionsMenu } from './package_policy_actions_menu';
function renderMenu({
agentPolicy,
packagePolicy,
showAddAgent = false,
defaultIsOpen = true,
}: {
agentPolicy: AgentPolicy;
packagePolicy: InMemoryPackagePolicy;
showAddAgent?: boolean;
defaultIsOpen?: boolean;
}) {
const renderer = createIntegrationsTestRendererMock();
const utils = renderer.render(
<PackagePolicyActionsMenu
agentPolicy={agentPolicy}
packagePolicy={packagePolicy}
showAddAgent={showAddAgent}
upgradePackagePolicyHref=""
defaultIsOpen={defaultIsOpen}
key="test1"
/>
);
return { utils };
}
function createMockAgentPolicy(props: Partial<AgentPolicy> = {}): AgentPolicy {
return {
id: 'some-uuid1',
namespace: 'default',
monitoring_enabled: [],
name: 'Test Policy',
description: '',
is_default: false,
is_preconfigured: false,
status: 'active',
is_managed: false,
revision: 1,
updated_at: '',
updated_by: 'elastic',
package_policies: [],
...props,
};
}
function createMockPackagePolicy(
props: Partial<InMemoryPackagePolicy> = {}
): InMemoryPackagePolicy {
return {
id: 'some-uuid2',
name: 'mock-package-policy',
description: '',
created_at: '',
created_by: '',
updated_at: '',
updated_by: '',
policy_id: '',
enabled: true,
output_id: '',
namespace: 'default',
inputs: [],
revision: 1,
hasUpgrade: false,
...props,
};
}
test('Should disable upgrade button if package does not have upgrade', async () => {
const agentPolicy = createMockAgentPolicy();
const packagePolicy = createMockPackagePolicy({ hasUpgrade: false });
const { utils } = renderMenu({ agentPolicy, packagePolicy });
await act(async () => {
const upgradeButton = utils.getByText('Upgrade integration policy').closest('button');
expect(upgradeButton).toBeDisabled();
});
});
test('Should enable upgrade button if package has upgrade', async () => {
const agentPolicy = createMockAgentPolicy();
const packagePolicy = createMockPackagePolicy({ hasUpgrade: true });
const { utils } = renderMenu({ agentPolicy, packagePolicy });
await act(async () => {
const upgradeButton = utils.getByText('Upgrade integration policy').closest('button');
expect(upgradeButton).not.toBeDisabled();
});
});
test('Should not be able to delete integration from a managed policy', async () => {
const agentPolicy = createMockAgentPolicy({ is_managed: true });
const packagePolicy = createMockPackagePolicy();
const { utils } = renderMenu({ agentPolicy, packagePolicy });
await act(async () => {
expect(utils.queryByText('Delete integration')).toBeNull();
});
});
test('Should be able to delete integration from a non-managed policy', async () => {
const agentPolicy = createMockAgentPolicy({ is_managed: false });
const packagePolicy = createMockPackagePolicy();
const { utils } = renderMenu({ agentPolicy, packagePolicy });
await act(async () => {
expect(utils.queryByText('Delete integration')).not.toBeNull();
});
});

View file

@ -24,13 +24,21 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
packagePolicy: InMemoryPackagePolicy;
viewDataStep?: EuiStepProps;
showAddAgent?: boolean;
defaultIsOpen?: boolean;
upgradePackagePolicyHref: string;
}> = ({ agentPolicy, packagePolicy, viewDataStep, showAddAgent, upgradePackagePolicyHref }) => {
}> = ({
agentPolicy,
packagePolicy,
viewDataStep,
showAddAgent,
upgradePackagePolicyHref,
defaultIsOpen = false,
}) => {
const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false);
const { getHref } = useLink();
const hasWriteCapabilities = useCapabilities().write;
const refreshAgentPolicy = useAgentPolicyRefresh();
const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(false);
const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(defaultIsOpen);
const onEnrollmentFlyoutClose = useMemo(() => {
return () => setIsEnrollmentFlyoutOpen(false);
@ -84,6 +92,7 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
disabled={!packagePolicy.hasUpgrade}
icon="refresh"
href={upgradePackagePolicyHref}
key="packagePolicyUpgrade"
>
<FormattedMessage
id="xpack.fleet.policyDetails.packagePoliciesTable.upgradeActionTitle"
@ -135,7 +144,7 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{
<ContextMenuActions
isOpen={isActionsMenuOpen}
items={menuItems}
onChange={(isOpen) => setIsActionsMenuOpen(isOpen)}
onChange={(open) => setIsActionsMenuOpen(open)}
/>
</>
);