Skip to content

Commit 478ba73

Browse files
committed
allow Policy Details to support route state
1 parent 2c27bad commit 478ba73

File tree

5 files changed

+115
-33
lines changed

5 files changed

+115
-33
lines changed

x-pack/plugins/security_solution/common/endpoint/types.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,28 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { PackageConfig, NewPackageConfig } from '../../../ingest_manager/common';
7+
import { ApplicationStart } from 'kibana/public';
8+
import { NewPackageConfig, PackageConfig } from '../../../ingest_manager/common';
89
import { ManifestSchema } from './schema/manifest';
910

11+
/**
12+
* Supported React-Router state for the Policy Details page
13+
*/
14+
export interface PolicyDetailsRouteState {
15+
/**
16+
* Where the user should be redirected to when the `Save` button is clicked and the update was successful
17+
*/
18+
onSaveNavigateTo?: Parameters<ApplicationStart['navigateToApp']>;
19+
/**
20+
* Where the user should be redirected to when the `Cancel` button is clicked
21+
*/
22+
onCancelNavigateTo?: Parameters<ApplicationStart['navigateToApp']>;
23+
/**
24+
* The URL to be used for the `Cancel` button (should use opt to open it in a different tab)
25+
*/
26+
cancelUrl?: string;
27+
}
28+
1029
/**
1130
* Object that allows you to maintain stateful information in the location object across navigation events
1231
*
@@ -17,9 +36,11 @@ export interface AppLocation {
1736
search: string;
1837
hash: string;
1938
key?: string;
20-
state?: {
21-
isTabChange?: boolean;
22-
};
39+
state?:
40+
| {
41+
isTabChange?: boolean;
42+
}
43+
| PolicyDetailsRouteState;
2344
}
2445

2546
/**

x-pack/plugins/security_solution/public/app/routes.tsx

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,50 @@
55
*/
66

77
import { History } from 'history';
8-
import React, { FC, memo } from 'react';
8+
import React, { FC, memo, useEffect } from 'react';
99
import { Route, Router, Switch } from 'react-router-dom';
1010

11+
import { useDispatch } from 'react-redux';
1112
import { NotFoundPage } from './404';
1213
import { HomePage } from './home';
1314
import { ManageRoutesSpy } from '../common/utils/route/manage_spy_routes';
1415
import { RouteCapture } from '../common/components/endpoint/route_capture';
16+
import { AppAction } from '../common/store/actions';
1517

1618
interface RouterProps {
1719
children: React.ReactNode;
1820
history: History;
1921
}
2022

21-
const PageRouterComponent: FC<RouterProps> = ({ history, children }) => (
22-
<ManageRoutesSpy>
23-
<Router history={history}>
24-
<RouteCapture>
25-
<Switch>
26-
<Route path="/">
27-
<HomePage>{children}</HomePage>
28-
</Route>
29-
<Route>
30-
<NotFoundPage />
31-
</Route>
32-
</Switch>
33-
</RouteCapture>
34-
</Router>
35-
</ManageRoutesSpy>
36-
);
23+
const PageRouterComponent: FC<RouterProps> = ({ history, children }) => {
24+
const dispatch = useDispatch<(action: AppAction) => void>();
25+
useEffect(() => {
26+
return () => {
27+
// When app is dismounted via a non-router method (ex. using Kibana's `services.application.navigateToApp()`)
28+
// ensure that one last `userChangedUrl` store action is dispatched, which will help trigger state reset logic
29+
dispatch({
30+
type: 'userChangedUrl',
31+
payload: { pathname: '', search: '', hash: '' },
32+
});
33+
};
34+
}, [dispatch]);
35+
36+
return (
37+
<ManageRoutesSpy>
38+
<Router history={history}>
39+
<RouteCapture>
40+
<Switch>
41+
<Route path="/">
42+
<HomePage>{children}</HomePage>
43+
</Route>
44+
<Route>
45+
<NotFoundPage />
46+
</Route>
47+
</Switch>
48+
</RouteCapture>
49+
</Router>
50+
</ManageRoutesSpy>
51+
);
52+
};
3753

3854
export const PageRouter = memo(PageRouterComponent);

x-pack/plugins/security_solution/public/management/pages/policy/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
*/
66

77
import {
8-
PolicyData,
8+
AppLocation,
99
Immutable,
1010
MalwareFields,
11+
PolicyData,
1112
UIPolicyConfig,
12-
AppLocation,
1313
} from '../../../../common/endpoint/types';
1414
import { ServerApiError } from '../../../common/types';
1515
import {
1616
GetAgentStatusResponse,
17-
GetPackageConfigsResponse,
1817
GetOnePackageConfigResponse,
18+
GetPackageConfigsResponse,
1919
GetPackagesResponse,
2020
UpdatePackageConfigResponse,
2121
} from '../../../../../ingest_manager/common';

x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_package_config.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React, { memo } from 'react';
7+
import React, { memo, useMemo } from 'react';
88
import { FormattedMessage } from '@kbn/i18n/react';
99
import { EuiCallOut, EuiText, EuiTitle, EuiSpacer } from '@elastic/eui';
1010
import { i18n } from '@kbn/i18n';
@@ -15,18 +15,39 @@ import {
1515
} from '../../../../../../../ingest_manager/public';
1616
import { getPolicyDetailPath } from '../../../../common/routing';
1717
import { MANAGEMENT_APP_ID } from '../../../../common/constants';
18+
import { PolicyDetailsRouteState } from '../../../../../../common/endpoint/types';
1819

1920
/**
2021
* Exports Endpoint-specific package config instructions
2122
* for use in the Ingest app create / edit package config
2223
*/
2324
export const ConfigureEndpointPackageConfig = memo<CustomConfigurePackageConfigContent>(
24-
({ from, packageConfigId }: CustomConfigurePackageConfigProps) => {
25+
({
26+
from,
27+
packageConfigId,
28+
packageConfig: { config_id: agentConfigId },
29+
}: CustomConfigurePackageConfigProps) => {
2530
let policyUrl = '';
2631
if (from === 'edit' && packageConfigId) {
32+
// Cannot use formalUrl here since the code is called in Ingest, which does not use redux
2733
policyUrl = getPolicyDetailPath(packageConfigId);
2834
}
2935

36+
const policyDetailRouteState = useMemo((): undefined | PolicyDetailsRouteState => {
37+
if (from !== 'edit') {
38+
return undefined;
39+
}
40+
const navigateTo = [
41+
'ingestManager',
42+
{ path: `#/configs/${agentConfigId}/edit-integration/${packageConfigId}` },
43+
];
44+
return {
45+
onSaveNavigateTo: navigateTo,
46+
onCancelNavigateTo: navigateTo,
47+
cancelUrl: '',
48+
};
49+
}, [agentConfigId, from, packageConfigId]);
50+
3051
return (
3152
<>
3253
<EuiTitle size="xs">
@@ -63,7 +84,7 @@ export const ConfigureEndpointPackageConfig = memo<CustomConfigurePackageConfigC
6384
appId={MANAGEMENT_APP_ID}
6485
className="editLinkToPolicyDetails"
6586
appPath={policyUrl}
66-
// Cannot use formalUrl here since the code is called in Ingest, which does not use redux
87+
appState={policyDetailRouteState}
6788
>
6889
<FormattedMessage
6990
id="xpack.securitySolution.endpoint.ingestManager.editPackageConfig.configurePolicyLink"

x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
import { FormattedMessage } from '@kbn/i18n/react';
2121
import { i18n } from '@kbn/i18n';
2222
import { useDispatch } from 'react-redux';
23+
import { useLocation } from 'react-router-dom';
2324
import { usePolicyDetailsSelector } from './policy_hooks';
2425
import {
2526
policyDetails,
@@ -41,11 +42,20 @@ import { SpyRoute } from '../../../../common/utils/route/spy_routes';
4142
import { SecurityPageName } from '../../../../app/types';
4243
import { getPoliciesPath } from '../../../common/routing';
4344
import { useFormatUrl } from '../../../../common/components/link_to';
45+
import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
46+
import { MANAGEMENT_APP_ID } from '../../../common/constants';
47+
import { PolicyDetailsRouteState } from '../../../../../common/endpoint/types';
4448

4549
export const PolicyDetails = React.memo(() => {
4650
const dispatch = useDispatch<(action: AppAction) => void>();
47-
const { notifications } = useKibana();
51+
const {
52+
notifications,
53+
services: {
54+
application: { navigateToApp },
55+
},
56+
} = useKibana();
4857
const { formatUrl, search } = useFormatUrl(SecurityPageName.management);
58+
const { state: locationRouteState } = useLocation<PolicyDetailsRouteState>();
4959

5060
// Store values
5161
const policyItem = usePolicyDetailsSelector(policyDetails);
@@ -56,6 +66,7 @@ export const PolicyDetails = React.memo(() => {
5666

5767
// Local state
5868
const [showConfirm, setShowConfirm] = useState<boolean>(false);
69+
const [routeState, setRouteState] = useState<PolicyDetailsRouteState>();
5970
const policyName = policyItem?.name ?? '';
6071

6172
// Handle showing update statuses
@@ -80,6 +91,10 @@ export const PolicyDetails = React.memo(() => {
8091
</span>
8192
),
8293
});
94+
95+
if (routeState && routeState.onSaveNavigateTo) {
96+
navigateToApp(...routeState.onSaveNavigateTo);
97+
}
8398
} else {
8499
notifications.toasts.danger({
85100
toastLifeTimeMs: 10000,
@@ -90,10 +105,16 @@ export const PolicyDetails = React.memo(() => {
90105
});
91106
}
92107
}
93-
}, [notifications.toasts, policyName, policyUpdateStatus]);
108+
}, [navigateToApp, notifications.toasts, policyName, policyUpdateStatus, routeState]);
94109

95110
const handleBackToListOnClick = useNavigateByRouterEventHandler(getPoliciesPath());
96111

112+
const handleCancelOnClick = useNavigateToAppEventHandler(
113+
...(routeState && routeState.onCancelNavigateTo
114+
? routeState.onCancelNavigateTo
115+
: [MANAGEMENT_APP_ID, { path: getPoliciesPath() }]) // << FIXME: PT - cache this array or input to the `useNavigateToAppEventHandler()`
116+
);
117+
97118
const handleSaveOnClick = useCallback(() => {
98119
setShowConfirm(true);
99120
}, []);
@@ -109,6 +130,12 @@ export const PolicyDetails = React.memo(() => {
109130
setShowConfirm(false);
110131
}, []);
111132

133+
useEffect(() => {
134+
if (!routeState && locationRouteState) {
135+
setRouteState(locationRouteState);
136+
}
137+
}, [locationRouteState, routeState]);
138+
112139
// Before proceeding - check if we have a policy data.
113140
// If not, and we are still loading, show spinner.
114141
// Else, if we have an error, then show error on the page.
@@ -159,10 +186,7 @@ export const PolicyDetails = React.memo(() => {
159186
<VerticalDivider spacing="l" />
160187
</EuiFlexItem>
161188
<EuiFlexItem grow={false}>
162-
<EuiButtonEmpty
163-
onClick={handleBackToListOnClick}
164-
data-test-subj="policyDetailsCancelButton"
165-
>
189+
<EuiButtonEmpty onClick={handleCancelOnClick} data-test-subj="policyDetailsCancelButton">
166190
<FormattedMessage
167191
id="xpack.securitySolution.endpoint.policy.details.cancel"
168192
defaultMessage="Cancel"

0 commit comments

Comments
 (0)