Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIEM] Fix Detections page breadcrumbs #55173

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@
"**/react": "^16.12.0",
"**/react-test-renderer": "^16.12.0",
"**/deepmerge": "^4.2.2",
"**/serialize-javascript": "^2.1.1"
"**/serialize-javascript": "^2.1.1",
"**/fast-deep-equal": "^3.1.1"
},
"workspaces": {
"packages": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,15 @@ export const RedirectToEditRulePage = ({ location: { search } }: DetectionEngine
);
};

export const getDetectionEngineUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}`;
const baseDetectionEngineUrl = `#/link-to/${DETECTION_ENGINE_PAGE_NAME}`;

export const getDetectionEngineUrl = () => `${baseDetectionEngineUrl}`;
export const getDetectionEngineAlertUrl = () =>
`#/link-to/${DETECTION_ENGINE_PAGE_NAME}/${DetectionEngineTab.alerts}`;
export const getRulesUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules`;
export const getCreateRuleUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/create-rule`;
export const getRuleDetailsUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/rule-details`;
export const getEditRuleUrl = () =>
`#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/rule-details/edit-rule`;
`${baseDetectionEngineUrl}/${DetectionEngineTab.alerts}`;
export const getDetectionEngineTabUrl = (tabPath: string) => `${baseDetectionEngineUrl}/${tabPath}`;
export const getRulesUrl = () => `${baseDetectionEngineUrl}/rules`;
export const getCreateRuleUrl = () => `${baseDetectionEngineUrl}/rules/create`;
export const getRuleDetailsUrl = (detailName: string) =>
`${baseDetectionEngineUrl}/rules/id/${detailName}`;
export const getEditRuleUrl = (detailName: string) =>
`${baseDetectionEngineUrl}/rules/id/${detailName}/edit`;
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getOr, omit } from 'lodash/fp';
import { APP_NAME } from '../../../../common/constants';
import { getBreadcrumbs as getHostDetailsBreadcrumbs } from '../../../pages/hosts/details/utils';
import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../pages/network/ip_details';
import { getBreadcrumbs as getDetectionRulesBreadcrumbs } from '../../../pages/detection_engine/rules/utils';
import { SiemPageName } from '../../../pages/home/types';
import { RouteSpyState, HostRouteSpyState, NetworkRouteSpyState } from '../../../utils/route/types';
import { getOverviewUrl } from '../../link_to';
Expand Down Expand Up @@ -38,6 +39,9 @@ const isNetworkRoutes = (spyState: RouteSpyState): spyState is NetworkRouteSpySt
const isHostsRoutes = (spyState: RouteSpyState): spyState is HostRouteSpyState =>
spyState != null && spyState.pageName === SiemPageName.hosts;

const isDetectionsRoutes = (spyState: RouteSpyState) =>
spyState != null && spyState.pageName === SiemPageName.detections;

export const getBreadcrumbsForRoute = (
object: RouteSpyState & TabNavigationProps
): Breadcrumb[] | null => {
Expand Down Expand Up @@ -76,6 +80,24 @@ export const getBreadcrumbsForRoute = (
),
];
}
if (isDetectionsRoutes(spyState) && object.navTabs) {
const tempNav: SearchNavTab = { urlKey: 'detections', isDetailPage: false };
let urlStateKeys = [getOr(tempNav, spyState.pageName, object.navTabs)];
if (spyState.tabName != null) {
urlStateKeys = [...urlStateKeys, getOr(tempNav, spyState.tabName, object.navTabs)];
}

return [
...siemRootBreadcrumb,
...getDetectionRulesBreadcrumbs(
spyState,
urlStateKeys.reduce(
(acc: string[], item: SearchNavTab) => [...acc, getSearch(item, object)],
[]
)
),
];
}
if (
spyState != null &&
object.navTabs &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ describe('SIEM Navigation', () => {
query: { language: 'kuery', query: '' },
savedQuery: undefined,
search: '',
state: undefined,
tabName: 'authentications',
timeline: { id: '', isOpen: false },
timerange: {
Expand Down
116 changes: 64 additions & 52 deletions x-pack/legacy/plugins/siem/public/components/navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { isEqual } from 'lodash/fp';
import isEqual from 'lodash/fp/isEqual';
import deepEqual from 'fast-deep-equal';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
Expand All @@ -16,67 +17,78 @@ import { setBreadcrumbs } from './breadcrumbs';
import { TabNavigation } from './tab_navigation';
import { SiemNavigationProps, SiemNavigationComponentProps } from './types';

export const SiemNavigationComponent = React.memo<
SiemNavigationComponentProps & SiemNavigationProps & RouteSpyState
>(
({ detailName, display, navTabs, pageName, pathName, search, tabName, urlState, flowTarget }) => {
useEffect(() => {
if (pathName) {
setBreadcrumbs({
query: urlState.query,
detailName,
filters: urlState.filters,
navTabs,
pageName,
pathName,
savedQuery: urlState.savedQuery,
search,
tabName,
flowTarget,
timerange: urlState.timerange,
timeline: urlState.timeline,
});
}
}, [pathName, search, navTabs, urlState]);
export const SiemNavigationComponent: React.FC<SiemNavigationComponentProps &
SiemNavigationProps &
RouteSpyState> = ({
detailName,
display,
navTabs,
pageName,
pathName,
search,
tabName,
urlState,
flowTarget,
state,
}) => {
useEffect(() => {
if (pathName) {
setBreadcrumbs({
query: urlState.query,
detailName,
filters: urlState.filters,
navTabs,
pageName,
pathName,
savedQuery: urlState.savedQuery,
search,
tabName,
flowTarget,
timerange: urlState.timerange,
timeline: urlState.timeline,
state,
});
}
}, [pathName, search, navTabs, urlState, state]);

return (
<TabNavigation
query={urlState.query}
display={display}
filters={urlState.filters}
navTabs={navTabs}
pageName={pageName}
pathName={pathName}
savedQuery={urlState.savedQuery}
tabName={tabName}
timeline={urlState.timeline}
timerange={urlState.timerange}
/>
);
},
(prevProps, nextProps) => {
return (
return (
<TabNavigation
query={urlState.query}
display={display}
filters={urlState.filters}
navTabs={navTabs}
pageName={pageName}
pathName={pathName}
savedQuery={urlState.savedQuery}
tabName={tabName}
timeline={urlState.timeline}
timerange={urlState.timerange}
/>
);
};

export const SiemNavigationRedux = compose<
React.ComponentClass<SiemNavigationProps & RouteSpyState>
>(connect(makeMapStateToProps))(
React.memo(
SiemNavigationComponent,
(prevProps, nextProps) =>
prevProps.pathName === nextProps.pathName &&
prevProps.search === nextProps.search &&
isEqual(prevProps.navTabs, nextProps.navTabs) &&
isEqual(prevProps.urlState, nextProps.urlState)
);
}
isEqual(prevProps.urlState, nextProps.urlState) &&
deepEqual(prevProps.state, nextProps.state)
)
);

SiemNavigationComponent.displayName = 'SiemNavigationComponent';

export const SiemNavigationRedux = compose<
React.ComponentClass<SiemNavigationProps & RouteSpyState>
>(connect(makeMapStateToProps))(SiemNavigationComponent);

export const SiemNavigation = React.memo<SiemNavigationProps>(props => {
const SiemNavigationContainer: React.FC<SiemNavigationProps> = props => {
const [routeProps] = useRouteSpy();
const stateNavReduxProps: RouteSpyState & SiemNavigationProps = {
...routeProps,
...props,
};

return <SiemNavigationRedux {...stateNavReduxProps} />;
});
};

SiemNavigation.displayName = 'SiemNavigation';
export const SiemNavigation = SiemNavigationContainer;
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,13 @@ const makeMapStateToProps = () => {
};
};

export const DetectionEngine = connect(makeMapStateToProps, {
const mapDispatchToProps = {
setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker,
})(DetectionEngineComponent);
};

export const DetectionEngine = connect(
makeMapStateToProps,
mapDispatchToProps
)(DetectionEngineComponent);

DetectionEngine.displayName = 'DetectionEngine';
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const detectionEnginePath = `/:pageName(detections)`;

type Props = Partial<RouteComponentProps<{}>> & { url: string };

export const DetectionEngineContainer = React.memo<Props>(() => (
const DetectionEngineContainerComponent: React.FC<Props> = () => (
<ManageUserInfo>
<Switch>
<Route
Expand All @@ -35,10 +35,10 @@ export const DetectionEngineContainer = React.memo<Props>(() => (
<Route exact path={`${detectionEnginePath}/rules/create`}>
<CreateRuleComponent />
</Route>
<Route exact path={`${detectionEnginePath}/rules/id/:ruleId/`}>
<Route exact path={`${detectionEnginePath}/rules/id/:detailName`}>
<RuleDetails />
</Route>
<Route exact path={`${detectionEnginePath}/rules/id/:ruleId/edit`}>
<Route exact path={`${detectionEnginePath}/rules/id/:detailName/edit`}>
<EditRuleComponent />
</Route>
<Route
Expand All @@ -49,5 +49,6 @@ export const DetectionEngineContainer = React.memo<Props>(() => (
/>
</Switch>
</ManageUserInfo>
));
DetectionEngineContainer.displayName = 'DetectionEngineContainer';
);

export const DetectionEngineContainer = React.memo(DetectionEngineContainerComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
hasIndexWrite,
signalIndexName,
} = useUserInfo();
const { ruleId } = useParams();
const { detailName: ruleId } = useParams();
const [isLoading, rule] = useRule(ruleId);
// This is used to re-trigger api rule status when user de/activate rule
const [ruleEnabled, setRuleEnabled] = useState<boolean | null>(null);
Expand Down Expand Up @@ -381,7 +381,7 @@ const RuleDetailsComponent = memo<RuleDetailsComponentProps>(
}}
</WithSource>

<SpyRoute />
<SpyRoute state={{ ruleName: rule?.name }} />
</>
);
}
Expand All @@ -402,8 +402,10 @@ const makeMapStateToProps = () => {
};
};

export const RuleDetails = connect(makeMapStateToProps, {
const mapDispatchToProps = {
setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker,
})(RuleDetailsComponent);
};

export const RuleDetails = connect(makeMapStateToProps, mapDispatchToProps)(RuleDetailsComponent);

RuleDetails.displayName = 'RuleDetails';
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const EditRuleComponent = memo(() => {
canUserCRUD,
hasManageApiKey,
} = useUserInfo();
const { ruleId } = useParams();
const { detailName: ruleId } = useParams();
const [loading, rule] = useRule(ruleId);

const userHasNoPermissions =
Expand Down Expand Up @@ -347,7 +347,7 @@ export const EditRuleComponent = memo(() => {
</EuiFlexGroup>
</WrapperPage>

<SpyRoute />
<SpyRoute state={{ ruleName: rule?.name }} />
</>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.rules.pageT
defaultMessage: 'Signal detection rules',
});

export const ADD_PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.rules.addPageTitle', {
defaultMessage: 'Create',
});

export const EDIT_PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.rules.editPageTitle', {
defaultMessage: 'Edit',
});

export const REFRESH = i18n.translate('xpack.siem.detectionEngine.rules.allRules.refreshTitle', {
defaultMessage: 'Refresh',
});
Expand Down
Loading