Skip to content

Commit

Permalink
[SIEM] Fix Detections page breadcrumbs (elastic#55173) (elastic#55448)
Browse files Browse the repository at this point in the history
  • Loading branch information
patrykkopycinski authored Jan 21, 2020
1 parent 3abcab9 commit ce34962
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 93 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,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

0 comments on commit ce34962

Please sign in to comment.