diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts index 43db8f92929e8..c6b54dcb50beb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/action.ts @@ -73,11 +73,6 @@ export interface ServerReturnedEndpointNonExistingPolicies { payload: EndpointState['nonExistingPolicies']; } -export interface ServerReturnedEndpointAgentPolicies { - type: 'serverReturnedEndpointAgentPolicies'; - payload: EndpointState['agentPolicies']; -} - export interface ServerFinishedInitialization { type: 'serverFinishedInitialization'; payload: boolean; @@ -162,7 +157,6 @@ export type EndpointAction = | AppRequestedEndpointList | ServerReturnedEndpointNonExistingPolicies | ServerReturnedAgenstWithEndpointsTotal - | ServerReturnedEndpointAgentPolicies | UserUpdatedEndpointListRefreshOptions | ServerReturnedEndpointsTotal | ServerFailedToReturnAgenstWithEndpointsTotal diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts index a47590023dbc3..b47583aa08b20 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts @@ -29,8 +29,7 @@ export const initialEndpointPageState = (): Immutable => { selectedPolicyId: undefined, policyItemsLoading: false, endpointPackageInfo: createUninitialisedResourceState(), - nonExistingPolicies: {}, - agentPolicies: {}, + nonExistingPolicies: new Set(), endpointsExist: true, patterns: [], patternsError: undefined, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts index ea2e82ac864e1..241445e5fdd02 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts @@ -40,7 +40,7 @@ describe('EndpointList store concerns', () => { }); test('it creates default state', () => { - expect(store.getState()).toEqual({ + const expectedDefaultState: EndpointState = { hosts: [], isInitialized: false, pageSize: 10, @@ -57,8 +57,7 @@ describe('EndpointList store concerns', () => { endpointPackageInfo: { type: 'UninitialisedResourceState', }, - nonExistingPolicies: {}, - agentPolicies: {}, + nonExistingPolicies: new Set(), endpointsExist: true, patterns: [], patternsError: undefined, @@ -68,12 +67,13 @@ describe('EndpointList store concerns', () => { endpointsTotal: 0, agentsWithEndpointsTotalError: undefined, endpointsTotalError: undefined, - queryStrategyVersion: undefined, isolationRequestState: { type: 'UninitialisedResourceState', }, metadataTransformStats: createUninitialisedResourceState(), - }); + }; + + expect(store.getState()).toEqual(expectedDefaultState); }); test('it handles `serverReturnedEndpointList', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index 56b92e4692edc..61a049d2dd99e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -47,7 +47,12 @@ import { sendGetEndpointSecurityPackage, } from '../../../services/policies/ingest'; import type { GetPolicyListResponse } from '../../policy/types'; -import type { EndpointState, PolicyIds, TransformStats, TransformStatsResponse } from '../types'; +import type { + EndpointState, + NonExistingPolicies, + TransformStats, + TransformStatsResponse, +} from '../types'; import type { EndpointPackageInfoStateChanged } from './action'; import { endpointPackageInfo, @@ -130,26 +135,24 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory => { + currentNonExistingPolicies: Immutable +): Promise => { if (hosts.length === 0) { return; } // Create an array of unique policy IDs that are not yet known to be non-existing. const policyIdsToCheck = [ - ...new Set( - hosts.reduce((acc: string[], host) => { - const appliedPolicyId = host.metadata.Endpoint.policy.applied.id; - if (!currentNonExistingPolicies[appliedPolicyId]) { - acc.push(appliedPolicyId); - } - return acc; - }, []) - ), + ...hosts.reduce>((acc, host) => { + const appliedPolicyId = host.metadata.Endpoint.policy.applied.id; + if (!currentNonExistingPolicies.has(appliedPolicyId)) { + acc.add(appliedPolicyId); + } + return acc; + }, new Set()), ]; if (policyIdsToCheck.length === 0) { @@ -158,37 +161,24 @@ const getAgentAndPoliciesForEndpointsList = async ( const policiesFound = ( await sendBulkGetPackagePolicies(http, policyIdsToCheck) - ).items.reduce( - (list, packagePolicy) => { - list.packagePolicy[packagePolicy.id as string] = true; - list.agentPolicy[packagePolicy.id as string] = packagePolicy.policy_ids[0]; // TODO - - return list; - }, - { packagePolicy: {}, agentPolicy: {} } - ); + ).items.reduce((set, packagePolicy) => set.add(packagePolicy.id), new Set()); - // packagePolicy contains non-existing packagePolicy ids whereas agentPolicy contains existing agentPolicy ids - const nonExistingPackagePoliciesAndExistingAgentPolicies = policyIdsToCheck.reduce( - (list, policyId: string) => { - if (policiesFound.packagePolicy[policyId as string]) { - list.agentPolicy[policyId as string] = policiesFound.agentPolicy[policyId]; - return list; + const nonExistingPackagePolicies = policyIdsToCheck.reduce( + (set, policyId) => { + if (!policiesFound.has(policyId)) { + set.add(policyId); } - list.packagePolicy[policyId as string] = true; - return list; + + return set; }, - { packagePolicy: {}, agentPolicy: {} } + new Set() ); - if ( - Object.keys(nonExistingPackagePoliciesAndExistingAgentPolicies.packagePolicy).length === 0 && - Object.keys(nonExistingPackagePoliciesAndExistingAgentPolicies.agentPolicy).length === 0 - ) { + if (!nonExistingPackagePolicies.size) { return; } - return nonExistingPackagePoliciesAndExistingAgentPolicies; + return nonExistingPackagePolicies; }; const endpointsTotal = async (http: HttpStart): Promise => { @@ -330,7 +320,7 @@ async function endpointListMiddleware({ payload: endpointResponse, }); - dispatchIngestPolicies({ http: coreStart.http, hosts: endpointResponse.data, store }); + fetchNonExistingPolicies({ http: coreStart.http, hosts: endpointResponse.data, store }); } catch (error) { dispatch({ type: 'serverFailedToReturnEndpointList', @@ -447,7 +437,7 @@ export async function handleLoadMetadataTransformStats(http: HttpStart, store: E } } -async function dispatchIngestPolicies({ +async function fetchNonExistingPolicies({ store, hosts, http, @@ -458,21 +448,15 @@ async function dispatchIngestPolicies({ }) { const { getState, dispatch } = store; try { - const ingestPolicies = await getAgentAndPoliciesForEndpointsList( + const missingPolicies = await getNonExistingPoliciesForEndpointList( http, hosts, nonExistingPolicies(getState()) ); - if (ingestPolicies?.packagePolicy !== undefined) { + if (missingPolicies !== undefined) { dispatch({ type: 'serverReturnedEndpointNonExistingPolicies', - payload: ingestPolicies.packagePolicy, - }); - } - if (ingestPolicies?.agentPolicy !== undefined) { - dispatch({ - type: 'serverReturnedEndpointAgentPolicies', - payload: ingestPolicies.agentPolicy, + payload: missingPolicies, }); } } catch (error) { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts index 806f66654ca6d..3471d5a373f12 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts @@ -67,18 +67,7 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta } else if (action.type === 'serverReturnedEndpointNonExistingPolicies') { return { ...state, - nonExistingPolicies: { - ...state.nonExistingPolicies, - ...action.payload, - }, - }; - } else if (action.type === 'serverReturnedEndpointAgentPolicies') { - return { - ...state, - agentPolicies: { - ...state.agentPolicies, - ...action.payload, - }, + nonExistingPolicies: new Set([...state.nonExistingPolicies, ...action.payload]), }; } else if (action.type === 'serverReturnedMetadataPatterns') { // handle an error case diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index bd8cff1495be9..fea48a36493b2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -167,13 +167,6 @@ export const nonExistingPolicies: ( state: Immutable ) => Immutable = (state) => state.nonExistingPolicies; -/** - * returns the list of known existing agent policies - */ -export const agentPolicies: ( - state: Immutable -) => Immutable = (state) => state.agentPolicies; - /** * Return boolean that indicates whether endpoints exist * @param state diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts index 4b46d772a155f..415f05c417ca8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts @@ -48,9 +48,7 @@ export interface EndpointState { /** Endpoint package info */ endpointPackageInfo: AsyncResourceState; /** Tracks the list of policy IDs used in Host metadata that may no longer exist */ - nonExistingPolicies: PolicyIds['packagePolicy']; - /** List of Package Policy Ids mapped to an associated Fleet Parent Agent Policy Id*/ - agentPolicies: PolicyIds['agentPolicy']; + nonExistingPolicies: NonExistingPolicies; /** Tracks whether hosts exist and helps control if onboarding should be visible */ endpointsExist: boolean; /** index patterns for query bar */ @@ -79,13 +77,9 @@ export interface EndpointState { export type AgentIdsPendingActions = Map; /** - * packagePolicy contains a list of Package Policy IDs (received via Endpoint metadata policy response) mapped to a boolean whether they exist or not. - * agentPolicy contains a list of existing Package Policy Ids mapped to an associated Fleet parent Agent Config. + * Set containing Package Policy IDs which are used but do not exist anymore */ -export interface PolicyIds { - packagePolicy: Record; - agentPolicy: Record; -} +export type NonExistingPolicies = Set; /** * Query params on the host page parsed from the URL diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/table_row_actions.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/table_row_actions.tsx index 6ebf4eb4e3283..df8381539b683 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/table_row_actions.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/table_row_actions.tsx @@ -10,16 +10,16 @@ import type { EuiContextMenuPanelProps, EuiPopoverProps } from '@elastic/eui'; import { EuiButtonIcon, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ContextMenuItemNavByRouter } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router'; -import type { HostMetadata } from '../../../../../../common/endpoint/types'; +import type { HostInfo } from '../../../../../../common/endpoint/types'; import { useEndpointActionItems } from '../hooks'; export interface TableRowActionProps { - endpointMetadata: HostMetadata; + endpointInfo: HostInfo; } -export const TableRowActions = memo(({ endpointMetadata }) => { +export const TableRowActions = memo(({ endpointInfo }) => { const [isOpen, setIsOpen] = useState(false); - const endpointActions = useEndpointActionItems(endpointMetadata, { isEndpointList: true }); + const endpointActions = useEndpointActionItems(endpointInfo, { isEndpointList: true }); const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx index 66123062dff92..6eb0c573dd5b9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx @@ -56,6 +56,8 @@ describe('When using the Endpoint Details Actions Menu', () => { endpointHost.metadata.host.os.name = 'Windows'; // @ts-expect-error TS2540 endpointHost.metadata.agent.version = '7.14.0'; + // @ts-expect-error TS2540 + endpointHost.policy_info = { agent: { applied: { id: '123' } } }; httpMocks.responseProvider.metadataDetails.mockReturnValue(endpointHost); }; @@ -87,7 +89,7 @@ describe('When using the Endpoint Details Actions Menu', () => { }); render = async () => { - renderResult = mockedContext.render(); + renderResult = mockedContext.render(); const endpointDetailsActionsButton = renderResult.getByTestId('endpointDetailsActionsButton'); endpointDetailsActionsButton.style.pointerEvents = 'all'; await user.click(endpointDetailsActionsButton); @@ -129,10 +131,6 @@ describe('When using the Endpoint Details Actions Menu', () => { 'should navigate via kibana `navigateToApp()` when %s is clicked', async (_, dataTestSubj) => { await render(); - // TODO middlewareSpy.waitForAction() times out after the upgrade to userEvent v14 https://github.com/elastic/kibana/pull/189949 - // await act(async () => { - // await middlewareSpy.waitForAction('serverReturnedEndpointAgentPolicies'); - // }); const takeActionMenuItem = renderResult.getByTestId(dataTestSubj); takeActionMenuItem.style.pointerEvents = 'all'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx index dd0c16a5f5651..bdd2e626d244a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.tsx @@ -8,12 +8,12 @@ import React, { memo, useCallback, useMemo, useState } from 'react'; import { EuiButton, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { HostMetadata } from '../../../../../../../common/endpoint/types'; +import type { HostInfo } from '../../../../../../../common/endpoint/types'; import { useEndpointActionItems } from '../../hooks'; import { ContextMenuItemNavByRouter } from '../../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router'; -export const ActionsMenu = memo<{ hostMetadata: HostMetadata }>(({ hostMetadata }) => { - const menuOptions = useEndpointActionItems(hostMetadata); +export const ActionsMenu = memo<{ hostInfo: HostInfo }>(({ hostInfo }) => { + const menuOptions = useEndpointActionItems(hostInfo); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const closePopoverHandler = useCallback(() => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx index d6ed904b0f24a..dca25055c1b7e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx @@ -141,7 +141,7 @@ export const EndpointDetails = memo(() => { {showFlyoutFooter && ( - + )} diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx index 454b85ad32238..7f458953022c5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details_content.tsx @@ -116,7 +116,7 @@ export const EndpointDetailsContent = memo( policyId={hostInfo.metadata.Endpoint.policy.applied.id} revision={hostInfo.metadata.Endpoint.policy.applied.endpoint_policy_version} isOutdated={isPolicyOutOfDate(hostInfo.metadata.Endpoint.policy.applied, policyInfo)} - policyExists={!missingPolicies[hostInfo.metadata.Endpoint.policy.applied.id]} + policyExists={!missingPolicies.has(hostInfo.metadata.Endpoint.policy.applied.id)} data-test-subj="policyDetailsValue" > {hostInfo.metadata.Endpoint.policy.applied.name} diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx index 9773e8f7eafb6..bb6102e8051c4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx @@ -13,9 +13,9 @@ import { useUserPrivileges } from '../../../../../common/components/user_privile import { useWithShowResponder } from '../../../../hooks'; import { APP_UI_ID } from '../../../../../../common/constants'; import { getEndpointDetailsPath, getEndpointListPath } from '../../../../common/routing'; -import type { HostMetadata, MaybeImmutable } from '../../../../../../common/endpoint/types'; +import type { HostInfo, MaybeImmutable } from '../../../../../../common/endpoint/types'; import { useEndpointSelector } from './hooks'; -import { agentPolicies, uiQueryParams } from '../../store/selectors'; +import { uiQueryParams } from '../../store/selectors'; import { useAppUrl } from '../../../../../common/lib/kibana/hooks'; import type { ContextMenuItemNavByRouterProps } from '../../../../components/context_menu_with_router_support/context_menu_item_nav_by_router'; import { isEndpointHostIsolated } from '../../../../../common/utils/validators'; @@ -27,15 +27,14 @@ interface Options { /** * Returns a list (array) of actions for an individual endpoint - * @param endpointMetadata + * @param endpointInfo * @param options */ export const useEndpointActionItems = ( - endpointMetadata: MaybeImmutable | undefined, + endpointInfo: MaybeImmutable | undefined, options?: Options ): ContextMenuItemNavByRouterProps[] => { const { getAppUrl } = useAppUrl(); - const fleetAgentPolicies = useEndpointSelector(agentPolicies); const allCurrentUrlParams = useEndpointSelector(uiQueryParams); const showEndpointResponseActionsConsole = useWithShowResponder(); const { @@ -49,13 +48,14 @@ export const useEndpointActionItems = ( } = useUserPrivileges().endpointPrivileges; return useMemo(() => { - if (!endpointMetadata) { + if (!endpointInfo) { return []; } + const endpointAgentPolicyId = endpointInfo.policy_info?.agent.applied.id; + const endpointMetadata = endpointInfo.metadata; const isIsolated = isEndpointHostIsolated(endpointMetadata); const endpointId = endpointMetadata.agent.id; - const endpointPolicyId = endpointMetadata.Endpoint.policy.applied.id; const endpointHostName = endpointMetadata.host.hostname; const fleetAgentId = endpointMetadata.elastic.agent.id; const { show, selected_endpoint: _selectedEndpoint, ...currentUrlParams } = allCurrentUrlParams; @@ -182,19 +182,23 @@ export const useEndpointActionItems = ( key: 'agentConfigLink', 'data-test-subj': 'agentPolicyLink', navigateAppId: 'fleet', - navigateOptions: { - path: `${ - pagePathGetters.policy_details({ - policyId: fleetAgentPolicies[endpointPolicyId], - })[1] - }`, - }, - href: `${getAppUrl({ appId: 'fleet' })}${ - pagePathGetters.policy_details({ - policyId: fleetAgentPolicies[endpointPolicyId], - })[1] - }`, - disabled: fleetAgentPolicies[endpointPolicyId] === undefined, + ...(endpointAgentPolicyId + ? { + navigateOptions: { + path: `${ + pagePathGetters.policy_details({ + policyId: endpointAgentPolicyId, + })[1] + }`, + }, + href: `${getAppUrl({ appId: 'fleet' })}${ + pagePathGetters.policy_details({ + policyId: endpointAgentPolicyId, + })[1] + }`, + } + : {}), + disabled: endpointAgentPolicyId === undefined, children: ( { const generator = new EndpointDocGenerator('seed'); let hostInfo: HostInfo[]; let agentId: string; - let agentPolicyId: string; + let agentPolicies: AgentPolicy[]; let endpointActionsButton: HTMLElement; // 2nd endpoint only has isolation capabilities const mockEndpointListApi = () => { + agentPolicies = [generator.generateAgentPolicy(), generator.generateAgentPolicy()]; + const { data: hosts } = mockEndpointResultList({ total: 2 }); hostInfo = [ { @@ -1180,6 +1183,13 @@ describe('when on the endpoint list page', () => { }, }, last_checkin: hosts[0].last_checkin, + policy_info: { + agent: { + applied: { id: agentPolicies[1].id, revision: 13 }, // host is assigned to the 2nd agent policy + configured: { id: 'dont-care', revision: 39 }, + }, + endpoint: { id: 'dont-care', revision: 3 }, + }, }, { host_status: hosts[1].host_status, @@ -1212,15 +1222,13 @@ describe('when on the endpoint list page', () => { const packagePolicy = docGenerator.generatePolicyPackagePolicy(); packagePolicy.id = hosts[0].metadata.Endpoint.policy.applied.id; - const agentPolicy = generator.generateAgentPolicy(); - agentPolicyId = agentPolicy.id; agentId = hosts[0].metadata.elastic.agent.id; - packagePolicy.policy_ids = [agentPolicyId]; + packagePolicy.policy_ids = [agentPolicies[0].id, agentPolicies[1].id]; // package is assigned to two agent policies setEndpointListApiMockImplementation(coreStart.http, { endpointsResults: hostInfo, endpointPackagePolicies: [packagePolicy], - agentPolicy, + agentPolicy: agentPolicies[0], }); }; @@ -1234,7 +1242,6 @@ describe('when on the endpoint list page', () => { render(); await middlewareSpy.waitForAction('serverReturnedEndpointList'); - await middlewareSpy.waitForAction('serverReturnedEndpointAgentPolicies'); endpointActionsButton = (await renderResult.findAllByTestId('endpointTableRowActions'))[0]; @@ -1302,9 +1309,11 @@ describe('when on the endpoint list page', () => { `${APP_PATH}/hosts/${hostInfo[0].metadata.host.hostname}` ); }); - it('navigates to the Ingest Agent Policy page', async () => { + it('navigates to the correct Ingest Agent Policy page', async () => { const agentPolicyLink = await renderResult.findByTestId('agentPolicyLink'); - expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet/policies/${agentPolicyId}`); + expect(agentPolicyLink.getAttribute('href')).toEqual( + `/app/fleet/policies/${agentPolicies[1].id}` + ); }); it('navigates to the Ingest Agent Details page', async () => { const agentDetailsLink = await renderResult.findByTestId('agentDetailsLink'); @@ -1482,7 +1491,6 @@ describe('when on the endpoint list page', () => { render(); await middlewareSpy.waitForAction('serverReturnedEndpointList'); - await middlewareSpy.waitForAction('serverReturnedEndpointAgentPolicies'); const endpointActionsButton: HTMLElement = ( await renderResult.findAllByTestId('endpointTableRowActions') diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 162d05f54ec21..945ae41237416 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -180,7 +180,7 @@ const getEndpointListColumns = ({ policyId={policy.id} revision={policy.endpoint_policy_version} isOutdated={isPolicyOutOfDate(policy, item.policy_info)} - policyExists={!missingPolicies[policy.id]} + policyExists={!missingPolicies.has(policy.id)} data-test-subj="policyNameCellLink" backLink={backToEndpointList} > @@ -301,7 +301,7 @@ const getEndpointListColumns = ({ actions: [ { render: (item: HostInfo) => { - return ; + return ; }, }, ], diff --git a/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts b/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts index 0e823c985c696..3a29063c2d14f 100644 --- a/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts +++ b/x-pack/plugins/security_solution/public/management/services/policies/hooks.ts @@ -98,6 +98,10 @@ export function useBulkGetAgentPolicies({ ['agentPolicies', policyIds], async () => { + if (!policyIds.length) { + return []; + } + return (await sendBulkGetAgentPolicies({ http, requestBody: { ids: policyIds } }))?.items; },