[Fleet] Fix disabling logic for "View Agents" button in agent activity (#202968)

## Summary

I came across a small bug while testing Fleet agents activity: the "View
Agents" button is currently always disabled for agent policy changes.
This is because agent policy data has been modified to be fetched with
`noAgentCount: true` by default.

As getting the agent count involves a performance concern, this PR fixes
the logic that disables the "View Agents" button for policy change
actions instead. The behaviour is not as follows:
* For tag updates actions: button not showed (no change)
* For policy change actions
* If `action.nbAgentsActionCreated > 10000`: disable button and show
tooltip explaining why it's disabled
* Otherwise: enable button and show tooltip saying that these are the
agents _currently_ assigned to the policy (existing behaviour, known
limitation)
* For other types of actions (no change)
* If `action.nbAgentsActionCreated > 10000`: disable button and show
tooltip explaining why it's disabled
   * Otherwise: enable button, no tooltip

### Screenshots

![Screenshot 2024-12-05 at 10 56
40](https://github.com/user-attachments/assets/c5f4f868-cdac-4de7-a96d-f11afd803d87)

![Screenshot 2024-12-05 at 10 57
13](https://github.com/user-attachments/assets/91195e3a-4f5c-4a91-b9ff-ffb62818647f)

![Screenshot 2024-12-05 at 10 57
20](https://github.com/user-attachments/assets/9029b1b5-6983-4509-9b62-15e073546d42)
This commit is contained in:
Jill Guyonnet 2024-12-05 15:43:40 +00:00 committed by GitHub
parent b210a3e6aa
commit 7caa33993a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 29 additions and 60 deletions

View file

@ -18,7 +18,7 @@ import {
EuiSpacer,
} from '@elastic/eui';
import type { ActionStatus, AgentPolicy } from '../../../../../types';
import type { ActionStatus } from '../../../../../types';
import { ViewErrors } from '../view_errors';
@ -34,8 +34,7 @@ import { ViewAgentsButton } from './view_agents_button';
export const ActivityItem: React.FunctionComponent<{
action: ActionStatus;
onClickViewAgents: (action: ActionStatus) => void;
agentPolicies: AgentPolicy[];
}> = ({ action, onClickViewAgents, agentPolicies }) => {
}> = ({ action, onClickViewAgents }) => {
const completeTitle =
action.type === 'POLICY_CHANGE' && action.nbAgentsActioned === 0 ? (
<EuiText>
@ -248,11 +247,7 @@ export const ActivityItem: React.FunctionComponent<{
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xs" />
<ViewAgentsButton
action={action}
onClickViewAgents={onClickViewAgents}
agentPolicies={agentPolicies}
/>
<ViewAgentsButton action={action} onClickViewAgents={onClickViewAgents} />
</EuiPanel>
);
};

View file

@ -9,7 +9,7 @@ import type { ReactNode } from 'react';
import React from 'react';
import { EuiText, EuiPanel } from '@elastic/eui';
import type { ActionStatus, AgentPolicy } from '../../../../../types';
import type { ActionStatus } from '../../../../../types';
import { UpgradeInProgressActivityItem } from './upgrade_in_progress_activity_item';
import { ActivityItem } from './activity_item';
@ -19,8 +19,7 @@ export const ActivitySection: React.FunctionComponent<{
actions: ActionStatus[];
abortUpgrade: (action: ActionStatus) => Promise<void>;
onClickViewAgents: (action: ActionStatus) => void;
agentPolicies: AgentPolicy[];
}> = ({ title, actions, abortUpgrade, onClickViewAgents, agentPolicies }) => {
}> = ({ title, actions, abortUpgrade, onClickViewAgents }) => {
return (
<>
<EuiPanel color="subdued" hasBorder={true} borderRadius="none">
@ -41,7 +40,6 @@ export const ActivitySection: React.FunctionComponent<{
action={currentAction}
key={currentAction.actionId}
onClickViewAgents={onClickViewAgents}
agentPolicies={agentPolicies}
/>
)
)}

View file

@ -17,7 +17,7 @@ import styled from 'styled-components';
import { FormattedDate, FormattedMessage } from '@kbn/i18n-react';
import type { ActionStatus, AgentPolicy } from '../../../../../types';
import type { ActionStatus } from '../../../../../types';
import { Loading } from '../../../components';
@ -51,7 +51,6 @@ export const FlyoutBody: React.FunctionComponent<{
onClickShowMore: () => void;
dateFilter: moment.Moment | null;
onChangeDateFilter: (date: moment.Moment | null) => void;
agentPolicies: AgentPolicy[];
}> = ({
isFirstLoading,
currentActions,
@ -61,7 +60,6 @@ export const FlyoutBody: React.FunctionComponent<{
onClickShowMore,
dateFilter,
onChangeDateFilter,
agentPolicies,
}) => {
const scrollToTopRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
@ -164,7 +162,6 @@ export const FlyoutBody: React.FunctionComponent<{
actions={inProgressActions}
abortUpgrade={abortUpgrade}
onClickViewAgents={onClickViewAgents}
agentPolicies={agentPolicies}
/>
) : null}
{todayActions.length > 0 ? (
@ -178,7 +175,6 @@ export const FlyoutBody: React.FunctionComponent<{
actions={todayActions}
abortUpgrade={abortUpgrade}
onClickViewAgents={onClickViewAgents}
agentPolicies={agentPolicies}
/>
) : null}
{Object.keys(otherDays).map((day) => (
@ -188,7 +184,6 @@ export const FlyoutBody: React.FunctionComponent<{
actions={otherDays[day]}
abortUpgrade={abortUpgrade}
onClickViewAgents={onClickViewAgents}
agentPolicies={agentPolicies}
/>
))}
</EuiFlexGroup>

View file

@ -50,7 +50,6 @@ describe('AgentActivityFlyout', () => {
refreshAgentActivity={refreshAgentActivity}
setSearch={mockSetSearch}
setSelectedStatus={mockSetSelectedStatus}
agentPolicies={[]}
/>
</IntlProvider>
);

View file

@ -34,8 +34,6 @@ import { getKuery } from '../../utils/get_kuery';
import { AGENT_STATUSES } from '../../../services/agent_status';
import type { AgentPolicy } from '../../../../../types';
import { FlyoutBody } from './flyout_body';
const FlyoutFooterWPadding = styled(EuiFlyoutFooter)`
@ -48,15 +46,7 @@ export const AgentActivityFlyout: React.FunctionComponent<{
refreshAgentActivity: boolean;
setSearch: (search: string) => void;
setSelectedStatus: (status: string[]) => void;
agentPolicies: AgentPolicy[];
}> = ({
onClose,
onAbortSuccess,
refreshAgentActivity,
setSearch,
setSelectedStatus,
agentPolicies,
}) => {
}> = ({ onClose, onAbortSuccess, refreshAgentActivity, setSearch, setSelectedStatus }) => {
const { notifications } = useStartServices();
const { data: agentPoliciesData } = useGetAgentPolicies({
perPage: SO_SEARCH_LIMIT,
@ -167,7 +157,6 @@ export const AgentActivityFlyout: React.FunctionComponent<{
onClickShowMore={onClickShowMore}
dateFilter={dateFilter}
onChangeDateFilter={onChangeDateFilter}
agentPolicies={agentPolicies}
/>
<FlyoutFooterWPadding>
<EuiFlexGroup justifyContent="flexStart">

View file

@ -9,23 +9,17 @@ import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
import type { ActionStatus, AgentPolicy } from '../../../../../types';
import type { ActionStatus } from '../../../../../types';
const MAX_VIEW_AGENTS_COUNT = 1000;
export const ViewAgentsButton: React.FunctionComponent<{
action: ActionStatus;
onClickViewAgents: (action: ActionStatus) => void;
agentPolicies?: AgentPolicy[];
}> = ({ action, onClickViewAgents, agentPolicies }) => {
}> = ({ action, onClickViewAgents }) => {
const isDisabled = useMemo(() => {
if (action.type !== 'POLICY_CHANGE') {
return action.nbAgentsActionCreated > MAX_VIEW_AGENTS_COUNT;
}
const actionPolicyId = action.actionId.split(':')[0];
return agentPolicies?.find((agentPolicy) => agentPolicy.id === actionPolicyId)?.agents === 0;
}, [action, agentPolicies]);
return action.nbAgentsActionCreated > MAX_VIEW_AGENTS_COUNT;
}, [action]);
if (action.type === 'UPDATE_TAGS') {
return null;
@ -46,8 +40,22 @@ export const ViewAgentsButton: React.FunctionComponent<{
</EuiButtonEmpty>
);
if (action.type !== 'POLICY_CHANGE' && !isDisabled) {
return button;
if (isDisabled) {
return (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentActivityFlyout.viewAgentsButtonDisabledMaxTooltip"
defaultMessage="The view agents feature is only available for action impacting less than {agentCount} agents"
values={{
agentCount: MAX_VIEW_AGENTS_COUNT,
}}
/>
}
>
{button}
</EuiToolTip>
);
}
if (action.type === 'POLICY_CHANGE') {
@ -65,19 +73,5 @@ export const ViewAgentsButton: React.FunctionComponent<{
);
}
return (
<EuiToolTip
content={
<FormattedMessage
id="xpack.fleet.agentActivityFlyout.viewAgentsButtonDisabledMaxTooltip"
defaultMessage="The view agents feature is only available for action impacting less than {agentCount} agents"
values={{
agentCount: MAX_VIEW_AGENTS_COUNT,
}}
/>
}
>
{button}
</EuiToolTip>
);
return button;
};

View file

@ -288,7 +288,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => {
refreshAgentActivity={isLoading}
setSearch={setSearch}
setSelectedStatus={setSelectedStatus}
agentPolicies={allAgentPolicies}
/>
</EuiPortal>
) : null}

View file

@ -46,7 +46,7 @@ export const GetAgentPoliciesRequestSchema = {
schema.boolean({
meta: { description: 'use withAgentCount instead', deprecated: true },
})
), //
),
withAgentCount: schema.maybe(
schema.boolean({
meta: { description: 'get policies with agent count' },