[Fleet] Stop uninstall of integrations with attached policies (#224960)

## Summary

Closes #223375 

- Added conditional disabling of the `uninstall` button when an
integration has attached policies for single integrations
- Added conditional disabling of the `uninstall` bulk action when one or
more chosen integration has attached agent policies
- Also added conditional tooltips for both cases 



https://github.com/user-attachments/assets/e097c869-e59a-4301-acd8-6a790cbdade5


### Checklist

Check the PR satisfies following conditions. 

Reviewers should verify this PR satisfies this list as well.

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Identify risks

N/A

---------

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
Co-authored-by: Colleen McGinnis <colleen.j.mcginnis@gmail.com>
This commit is contained in:
Mason Herron 2025-06-24 07:28:09 -06:00 committed by GitHub
parent cfab3f6b25
commit 61551e3044
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 56 additions and 16 deletions

View file

@ -6,7 +6,13 @@
*/
import React, { useState, useMemo, useCallback } from 'react';
import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
import {
EuiButton,
EuiContextMenuItem,
EuiContextMenuPanel,
EuiPopover,
EuiToolTip,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import type { InstalledPackageUIPackageListItem } from '../types';
@ -54,7 +60,7 @@ export const InstalledIntegrationsActionMenu: React.FunctionComponent<{
);
const hasUninstallableIntegrations = selectedItems.some(
(item) => (item.packagePoliciesInfo?.count ?? 0) === 0
(item) => (item.packagePoliciesInfo?.count ?? 0) > 0
);
return [
@ -75,16 +81,38 @@ export const InstalledIntegrationsActionMenu: React.FunctionComponent<{
<EuiContextMenuItem
key="uninstall"
icon="trash"
disabled={!hasUninstallableIntegrations}
disabled={hasUninstallableIntegrations}
onClick={openUninstallModal}
>
<FormattedMessage
id="xpack.fleet.epmInstalledIntegrations.bulkUninstallButton"
defaultMessage={'Uninstall {count, plural, one {# integration} other {# integrations}}'}
values={{
count: selectedItems.length,
}}
/>
{hasUninstallableIntegrations ? (
<EuiToolTip
position="right"
content={
<FormattedMessage
id="xpack.fleet.epmInstalledIntegrations.uninstallDisabledTooltip"
defaultMessage="Can't uninstall integrations that are attached to agent policies"
/>
}
>
<FormattedMessage
id="xpack.fleet.epmInstalledIntegrations.bulkUninstallButton"
defaultMessage={
'Uninstall {count, plural, one {# integration} other {# integrations}}'
}
values={{
count: selectedItems.length,
}}
/>
</EuiToolTip>
) : (
<FormattedMessage
id="xpack.fleet.epmInstalledIntegrations.bulkUninstallButton"
defaultMessage={'Uninstall {count, plural, one {# integration} other {# integrations}}'}
values={{
count: selectedItems.length,
}}
/>
)}
</EuiContextMenuItem>,
];
}, [selectedItems, openUninstallModal, openUpgradeModal]);

View file

@ -69,6 +69,13 @@ export const InstalledIntegrationsTable: React.FunctionComponent<{
},
[setPagination]
);
const usedByAgentPolicy = (item: InstalledPackageUIPackageListItem) => {
if (!item.packagePoliciesInfo) {
return false;
}
const count = item.packagePoliciesInfo.count;
return count > 0;
};
return (
<>
@ -279,13 +286,18 @@ export const InstalledIntegrationsTable: React.FunctionComponent<{
),
icon: 'trash',
type: 'icon',
description: i18n.translate(
'xpack.fleet.epmInstalledIntegrations.uninstallIntegrationLabel',
{
defaultMessage: 'Uninstall integration',
}
),
onClick: (item) => bulkUninstallIntegrationsWithConfirmModal([item]),
enabled: (item) => !usedByAgentPolicy(item),
description: (item) =>
i18n.translate(
'xpack.fleet.epmInstalledIntegrations.uninstallIntegrationLabel',
{
defaultMessage: usedByAgentPolicy(item)
? "You can't uninstall this integration because it is used by one or more agent policies"
: 'Uninstall integration',
}
),
},
!authz.integrations.removePackages,
i18n.translate(