From 32a68c57878c159325a32108a1d67cfefcc23640 Mon Sep 17 00:00:00 2001
From: Kevin Logan <56395104+kevinlog@users.noreply.github.com>
Date: Mon, 29 Jun 2020 21:43:47 -0400
Subject: [PATCH] [Ingest Manager][SECURITY SOLUTION] adjust config reassign
link and add roundtrip to Reassignment flow (#70208)
---
.../components/actions_menu.tsx | 18 +++++++--
.../fleet/agent_details_page/index.tsx | 40 ++++++++++++++++---
.../types/intra_app_route_state.ts | 11 ++++-
.../view/details/host_details.tsx | 34 ++++++++++++++--
.../pages/endpoint_hosts/view/hooks.ts | 17 ++++++++
.../pages/endpoint_hosts/view/index.test.tsx | 29 ++++++++++++++
6 files changed, 135 insertions(+), 14 deletions(-)
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx
index 27e17f6b3df610..75a67fb9288e55 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx
@@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { memo, useState } from 'react';
+import React, { memo, useState, useMemo } from 'react';
import { EuiPortal, EuiContextMenuItem } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { Agent } from '../../../../types';
@@ -14,16 +14,26 @@ import { useAgentRefresh } from '../hooks';
export const AgentDetailsActionMenu: React.FunctionComponent<{
agent: Agent;
-}> = memo(({ agent }) => {
+ assignFlyoutOpenByDefault?: boolean;
+ onCancelReassign?: () => void;
+}> = memo(({ agent, assignFlyoutOpenByDefault = false, onCancelReassign }) => {
const hasWriteCapabilites = useCapabilities().write;
const refreshAgent = useAgentRefresh();
- const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(false);
+ const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(assignFlyoutOpenByDefault);
+
+ const onClose = useMemo(() => {
+ if (onCancelReassign) {
+ return onCancelReassign;
+ } else {
+ return () => setIsReassignFlyoutOpen(false);
+ }
+ }, [onCancelReassign, setIsReassignFlyoutOpen]);
return (
<>
{isReassignFlyoutOpen && (
- setIsReassignFlyoutOpen(false)} />
+
)}
{
sendRequest: sendAgentConfigRequest,
} = useGetOneAgentConfig(agentData?.item?.config_id);
+ const {
+ application: { navigateToApp },
+ } = useCore();
+ const routeState = useIntraAppState();
+ const queryParams = new URLSearchParams(useLocation().search);
+ const openReassignFlyoutOpenByDefault = queryParams.get('openReassignFlyout') === 'true';
+
+ const reassignCancelClickHandler = useCallback(() => {
+ if (routeState && routeState.onDoneNavigateTo) {
+ navigateToApp(routeState.onDoneNavigateTo[0], routeState.onDoneNavigateTo[1]);
+ }
+ }, [routeState, navigateToApp]);
+
const headerLeftContent = useMemo(
() => (
@@ -124,7 +144,17 @@ export const AgentDetailsPage: React.FunctionComponent = () => {
},
{ isDivider: true },
{
- content: ,
+ content: (
+
+ ),
},
].map((item, index) => (
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/intra_app_route_state.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/intra_app_route_state.ts
index b2948686ff6e57..c5833adcded5f9 100644
--- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/intra_app_route_state.ts
+++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/intra_app_route_state.ts
@@ -29,9 +29,18 @@ export interface AgentConfigDetailsDeployAgentAction {
onDoneNavigateTo?: Parameters;
}
+/**
+ * Supported routing state for the agent config details page routes with deploy agents action
+ */
+export interface AgentDetailsReassignConfigAction {
+ /** On done, navigate to the given app */
+ onDoneNavigateTo?: Parameters;
+}
+
/**
* All possible Route states.
*/
export type AnyIntraAppRouteState =
| CreateDatasourceRouteState
- | AgentConfigDetailsDeployAgentAction;
+ | AgentConfigDetailsDeployAgentAction
+ | AgentDetailsReassignConfigAction;
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx
index b7e90c19799c76..80c4e2f379c7c6 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx
@@ -19,7 +19,8 @@ import React, { memo, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { HostMetadata } from '../../../../../../common/endpoint/types';
-import { useHostSelector, useHostLogsUrl, useHostIngestUrl } from '../hooks';
+import { useHostSelector, useHostLogsUrl, useAgentDetailsIngestUrl } from '../hooks';
+import { useNavigateToAppEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
import { policyResponseStatus, uiQueryParams } from '../../store/selectors';
import { POLICY_STATUS_TO_HEALTH_COLOR } from '../host_constants';
import { FormattedDateAndTime } from '../../../../../common/components/endpoint/formatted_date_time';
@@ -28,6 +29,7 @@ import { LinkToApp } from '../../../../../common/components/endpoint/link_to_app
import { getEndpointDetailsPath, getPolicyDetailPath } from '../../../../common/routing';
import { SecurityPageName } from '../../../../../app/types';
import { useFormatUrl } from '../../../../../common/components/link_to';
+import { AgentDetailsReassignConfigAction } from '../../../../../../../ingest_manager/public';
const HostIds = styled(EuiListGroupItem)`
margin-top: 0;
@@ -46,9 +48,16 @@ const LinkToExternalApp = styled.div`
}
`;
+const openReassignFlyoutSearch = '?openReassignFlyout=true';
+
export const HostDetails = memo(({ details }: { details: HostMetadata }) => {
const { url: logsUrl, appId: logsAppId, appPath: logsAppPath } = useHostLogsUrl(details.host.id);
- const { url: ingestUrl, appId: ingestAppId, appPath: ingestAppPath } = useHostIngestUrl();
+ const agentId = details.elastic.agent.id;
+ const {
+ url: agentDetailsUrl,
+ appId: ingestAppId,
+ appPath: agentDetailsAppPath,
+ } = useAgentDetailsIngestUrl(agentId);
const queryParams = useHostSelector(uiQueryParams);
const policyStatus = useHostSelector(
policyResponseStatus
@@ -96,6 +105,22 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => {
];
}, [details.host.id, formatUrl, queryParams]);
+ const agentDetailsWithFlyoutPath = `${agentDetailsAppPath}${openReassignFlyoutSearch}`;
+ const agentDetailsWithFlyoutUrl = `${agentDetailsUrl}${openReassignFlyoutSearch}`;
+ const handleReassignEndpointsClick = useNavigateToAppEventHandler<
+ AgentDetailsReassignConfigAction
+ >(ingestAppId, {
+ path: agentDetailsWithFlyoutPath,
+ state: {
+ onDoneNavigateTo: [
+ 'securitySolution:management',
+ {
+ path: getEndpointDetailsPath({ name: 'endpointDetails', selected_host: details.host.id }),
+ },
+ ],
+ },
+ });
+
const policyStatusClickHandler = useNavigateByRouterEventHandler(policyResponseRoutePath);
const [policyDetailsRoutePath, policyDetailsRouteUrl] = useMemo(() => {
@@ -207,8 +232,9 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => {
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks.ts
index 51aaea20df8431..c072c812edbb5d 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks.ts
@@ -51,3 +51,20 @@ export const useHostIngestUrl = (): { url: string; appId: string; appPath: strin
};
}, [services.application]);
};
+
+/**
+ * Returns an object that contains Ingest app and URL information
+ */
+export const useAgentDetailsIngestUrl = (
+ agentId: string
+): { url: string; appId: string; appPath: string } => {
+ const { services } = useKibana();
+ return useMemo(() => {
+ const appPath = `#/fleet/agents/${agentId}/activity`;
+ return {
+ url: `${services.application.getUrlForApp('ingestManager')}${appPath}`,
+ appId: 'ingestManager',
+ appPath,
+ };
+ }, [services.application, agentId]);
+};
diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx
index 9690ac5c1b9bf9..68943797ea07e1 100644
--- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx
@@ -210,6 +210,7 @@ describe('when on the hosts page', () => {
describe('when there is a selected host in the url', () => {
let hostDetails: HostInfo;
+ let agentId: string;
const dispatchServerReturnedHostPolicyResponse = (
overallStatus: HostPolicyResponseActionStatus = HostPolicyResponseActionStatus.success
) => {
@@ -274,6 +275,8 @@ describe('when on the hosts page', () => {
},
};
+ agentId = hostDetails.metadata.elastic.agent.id;
+
coreStart.http.get.mockReturnValue(Promise.resolve(hostDetails));
coreStart.application.getUrlForApp.mockReturnValue('/app/logs');
@@ -404,6 +407,32 @@ describe('when on the hosts page', () => {
).not.toBeNull();
});
+ it('should include the link to reassignment in Ingest', async () => {
+ coreStart.application.getUrlForApp.mockReturnValue('/app/ingestManager');
+ const renderResult = render();
+ const linkToReassign = await renderResult.findByTestId('hostDetailsLinkToIngest');
+ expect(linkToReassign).not.toBeNull();
+ expect(linkToReassign.textContent).toEqual('Reassign Policy');
+ expect(linkToReassign.getAttribute('href')).toEqual(
+ `/app/ingestManager#/fleet/agents/${agentId}/activity?openReassignFlyout=true`
+ );
+ });
+
+ describe('when link to reassignment in Ingest is clicked', () => {
+ beforeEach(async () => {
+ coreStart.application.getUrlForApp.mockReturnValue('/app/ingestManager');
+ const renderResult = render();
+ const linkToReassign = await renderResult.findByTestId('hostDetailsLinkToIngest');
+ reactTestingLibrary.act(() => {
+ reactTestingLibrary.fireEvent.click(linkToReassign);
+ });
+ });
+
+ it('should navigate to Ingest without full page refresh', () => {
+ expect(coreStart.application.navigateToApp.mock.calls).toHaveLength(1);
+ });
+ });
+
it('should include the link to logs', async () => {
const renderResult = render();
const linkToLogs = await renderResult.findByTestId('hostDetailsLinkToLogs');