diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts index a8dc6f7fc3270..fceb52df2487c 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts @@ -6,13 +6,11 @@ import chrome from 'ui/chrome'; import '../../../mock/match_media'; import { encodeIpv6 } from '../../../lib/helpers'; -import { getBreadcrumbs as getHostDetailsBreadcrumbs } from '../../../pages/hosts/details/utils'; -import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../pages/network/ip_details'; -import { TIMELINES_PAGE_NAME } from '../../link_to/redirect_to_timelines'; -import { getBreadcrumbsForRoute, rootBreadcrumbs, setBreadcrumbs } from '.'; +import { getBreadcrumbsForRoute, setBreadcrumbs } from '.'; import { HostsTableType } from '../../../store/hosts/model'; -import { SiemPageName } from '../../../pages/home/home_navigations'; +import { RouteSpyState } from '../../../utils/route/spy_routes'; +import { TabNavigationProps } from '../type'; jest.mock('ui/chrome', () => ({ getBasePath: () => { @@ -26,96 +24,184 @@ jest.mock('ui/chrome', () => ({ }), })); +const getMockObject = ( + pageName: string, + pathName: string, + detailName: string | undefined +): RouteSpyState & TabNavigationProps => ({ + detailName, + hostDetails: { filterQuery: null, queryLocation: null }, + hosts: { filterQuery: null, queryLocation: null }, + navTabs: { + hosts: { + disabled: false, + href: '#/link-to/hosts', + id: 'hosts', + name: 'Hosts', + urlKey: 'host', + }, + network: { + disabled: false, + href: '#/link-to/network', + id: 'network', + name: 'Network', + urlKey: 'network', + }, + overview: { + disabled: false, + href: '#/link-to/overview', + id: 'overview', + name: 'Overview', + urlKey: 'overview', + }, + timelines: { + disabled: false, + href: '#/link-to/timelines', + id: 'timelines', + name: 'Timelines', + urlKey: 'timeline', + }, + }, + network: { filterQuery: null, queryLocation: null }, + pageName, + pathName, + search: '', + tabName: HostsTableType.authentications, + timelineId: '', + timerange: { + global: { + linkTo: ['timeline'], + timerange: { + from: 1558048243696, + fromStr: 'now-24h', + kind: 'relative', + to: 1558134643697, + toStr: 'now', + }, + }, + timeline: { + linkTo: ['global'], + timerange: { + from: 1558048243696, + fromStr: 'now-24h', + kind: 'relative', + to: 1558134643697, + toStr: 'now', + }, + }, + }, +}); + describe('Navigation Breadcrumbs', () => { const hostName = 'siem-kibana'; - const hostDetailsParams = { - pageName: SiemPageName.hosts, - hostName, - tabName: HostsTableType.authentications, - }; - const hostBreadcrumbs = [ - ...rootBreadcrumbs.overview, - ...getHostDetailsBreadcrumbs(hostDetailsParams), - ]; + const ipv4 = '192.0.2.255'; - const ipv4Breadcrumbs = [...rootBreadcrumbs.overview, ...getIPDetailsBreadcrumbs(ipv4)]; const ipv6 = '2001:db8:ffff:ffff:ffff:ffff:ffff:ffff'; const ipv6Encoded = encodeIpv6(ipv6); - const ipv6Breadcrumbs = [...rootBreadcrumbs.overview, ...getIPDetailsBreadcrumbs(ipv6Encoded)]; - describe('getBreadcrumbsForRoute', () => { - test('should return Host breadcrumbs when supplied link-to host pathname', () => { - const pathname = '/link-to/hosts'; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(rootBreadcrumbs.hosts); - }); + describe('getBreadcrumbsForRoute', () => { test('should return Host breadcrumbs when supplied host pathname', () => { - const pathname = '/hosts'; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(rootBreadcrumbs.hosts); - }); - - test('should return Host breadcrumbs when supplied host pathname with trailing slash', () => { - const pathname = '/hosts/'; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(rootBreadcrumbs.hosts); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('hosts', '/hosts', undefined)); + expect(breadcrumbs).toEqual([ + { + href: '#/link-to/overview', + text: 'SIEM', + }, + { + href: + '#/link-to/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + text: 'Hosts', + }, + { + href: '', + text: 'Authentications', + }, + ]); }); test('should return Network breadcrumbs when supplied network pathname', () => { - const pathname = '/network'; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(rootBreadcrumbs.network); - }); - - test('should return Timelines breadcrumbs when supplied link-to timelines pathname', () => { - const pathname = `/link-to/${TIMELINES_PAGE_NAME}`; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(rootBreadcrumbs.timelines); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/network', undefined)); + expect(breadcrumbs).toEqual([ + { text: 'SIEM', href: '#/link-to/overview' }, + { + text: 'Network', + href: + '#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + }, + ]); }); test('should return Timelines breadcrumbs when supplied timelines pathname', () => { - const pathname = '/timelines'; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(rootBreadcrumbs.timelines); - }); - - test('should return Host Details breadcrumbs when supplied link-to pathname with hostName', () => { - const pathname = `/link-to/hosts/${hostName}`; - - const breadcrumbs = getBreadcrumbsForRoute(pathname, hostDetailsParams); - expect(breadcrumbs).toEqual(hostBreadcrumbs); + const breadcrumbs = getBreadcrumbsForRoute( + getMockObject('timelines', '/timelines', undefined) + ); + expect(breadcrumbs).toEqual([ + { text: 'SIEM', href: '#/link-to/overview' }, + { text: 'Timelines', href: '' }, + ]); }); test('should return Host Details breadcrumbs when supplied a pathname with hostName', () => { - const pathname = `/hosts/${hostName}`; - - const breadcrumbs = getBreadcrumbsForRoute(pathname, hostDetailsParams); - expect(breadcrumbs).toEqual(hostBreadcrumbs); - }); - - test('should return IP Details breadcrumbs when supplied link-to pathname with ipv4', () => { - const pathname = `link-to/network/ip/${ipv4}`; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(ipv4Breadcrumbs); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('hosts', '/hosts', hostName)); + expect(breadcrumbs).toEqual([ + { text: 'SIEM', href: '#/link-to/overview' }, + { + text: 'Hosts', + href: + '#/link-to/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + }, + { + text: 'siem-kibana', + href: + '#/link-to/hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + }, + { text: 'Authentications', href: '' }, + ]); }); test('should return IP Details breadcrumbs when supplied pathname with ipv4', () => { - const pathname = `/network/ip/${ipv4}`; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(ipv4Breadcrumbs); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/network', ipv4)); + expect(breadcrumbs).toEqual([ + { text: 'SIEM', href: '#/link-to/overview' }, + { + text: 'Network', + href: + '#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + }, + { text: '192.0.2.255', href: '' }, + ]); }); test('should return IP Details breadcrumbs when supplied pathname with ipv6', () => { - const pathname = `/network/ip/${ipv6Encoded}`; - const breadcrumbs = getBreadcrumbsForRoute(pathname); - expect(breadcrumbs).toEqual(ipv6Breadcrumbs); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/network', ipv6Encoded)); + expect(breadcrumbs).toEqual([ + { text: 'SIEM', href: '#/link-to/overview' }, + { + text: 'Network', + href: + '#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + }, + { text: '2001:db8:ffff:ffff:ffff:ffff:ffff:ffff', href: '' }, + ]); }); }); describe('setBreadcrumbs()', () => { test('should call chrome breadcrumb service with correct breadcrumbs', () => { - const pathname = `/hosts/${hostName}`; - setBreadcrumbs(pathname, hostDetailsParams); - expect(chrome.breadcrumbs.set).toBeCalledWith(hostBreadcrumbs); + setBreadcrumbs(getMockObject('hosts', '/hosts', hostName)); + expect(chrome.breadcrumbs.set).toBeCalledWith([ + { text: 'SIEM', href: '#/link-to/overview' }, + { + text: 'Hosts', + href: + '#/link-to/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + }, + { + text: 'siem-kibana', + href: + '#/link-to/hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + }, + { text: 'Authentications', href: '' }, + ]); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts index e4fb423b6788a..57573884c0566 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts @@ -6,23 +6,18 @@ import chrome, { Breadcrumb } from 'ui/chrome'; +import { getOr } 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 { getNetworkUrl, getOverviewUrl, getTimelinesUrl } from '../../link_to'; -import * as i18n from '../translations'; -import { getHostsUrl } from '../../link_to/redirect_to_hosts'; -import { HostsTableType } from '../../../store/hosts/model'; +import { getOverviewUrl } from '../../link_to'; import { SiemPageName } from '../../../pages/home/home_navigations'; +import { TabNavigationProps, SearchNavTab } from '../type'; +import { getSearch } from '../helpers'; +import { RouteSpyState } from '../../../utils/route/spy_routes'; -export interface NavigationParams { - pageName?: SiemPageName; - hostName?: string; - tabName?: HostsTableType; -} - -export const setBreadcrumbs = (pathname: string, params?: NavigationParams) => { - const breadcrumbs = getBreadcrumbsForRoute(pathname, params); +export const setBreadcrumbs = (object: RouteSpyState & TabNavigationProps) => { + const breadcrumbs = getBreadcrumbsForRoute(object); if (breadcrumbs) { chrome.breadcrumbs.set(breadcrumbs); } @@ -35,48 +30,49 @@ export const siemRootBreadcrumb: Breadcrumb[] = [ }, ]; -export const rootBreadcrumbs: { [name: string]: Breadcrumb[] } = { - overview: siemRootBreadcrumb, - hosts: [ - ...siemRootBreadcrumb, - { - text: i18n.HOSTS, - href: getHostsUrl(), - }, - ], - network: [ - ...siemRootBreadcrumb, - { - text: i18n.NETWORK, - href: getNetworkUrl(), - }, - ], - timelines: [ - ...siemRootBreadcrumb, - { - text: i18n.TIMELINES, - href: getTimelinesUrl(), - }, - ], -}; - export const getBreadcrumbsForRoute = ( - pathname: string, - params?: NavigationParams + object: RouteSpyState & TabNavigationProps ): Breadcrumb[] | null => { - const removeSlash = pathname.replace(/\/$/, ''); - const trailingPath = removeSlash.match(/([^\/]+$)/); - - if (trailingPath !== null) { - if (params != null && params.pageName === SiemPageName.hosts) { - return [...siemRootBreadcrumb, ...getHostDetailsBreadcrumbs(params)]; - } - if (Object.keys(rootBreadcrumbs).includes(trailingPath[0])) { - return rootBreadcrumbs[trailingPath[0]]; - } - if (pathname.match(/network\/ip\/.*?/)) { - return [...siemRootBreadcrumb, ...getIPDetailsBreadcrumbs(trailingPath[0])]; + if (object != null && object.navTabs && object.pageName === SiemPageName.hosts) { + const tempNav: SearchNavTab = { urlKey: 'host', isDetailPage: false }; + let urlStateKeys = [getOr(tempNav, object.pageName, object.navTabs)]; + if (object.tabName != null) { + urlStateKeys = [...urlStateKeys, getOr(tempNav, object.tabName, object.navTabs)]; } + return [ + ...siemRootBreadcrumb, + ...getHostDetailsBreadcrumbs( + object, + urlStateKeys.reduce((acc: string[], item: SearchNavTab) => { + acc = [...acc, getSearch(item, object)]; + return acc; + }, []) + ), + ]; + } + if (object != null && object.navTabs && object.pageName === SiemPageName.network) { + const tempNav: SearchNavTab = { urlKey: 'network', isDetailPage: false }; + const urlStateKeys = [getOr(tempNav, object.pageName, object.navTabs)]; + return [ + ...siemRootBreadcrumb, + ...getIPDetailsBreadcrumbs( + object.detailName, + urlStateKeys.reduce((acc: string[], item) => { + acc = [...acc, getSearch(item, object)]; + return acc; + }, []) + ), + ]; } + if (object != null && object.navTabs && object.pageName && object.navTabs[object.pageName]) { + return [ + ...siemRootBreadcrumb, + { + text: object.navTabs[object.pageName].name, + href: '', + }, + ]; + } + return null; }; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts new file mode 100644 index 0000000000000..d6d6c9b261702 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Location } from 'history'; + +import { UrlInputsModel } from '../../store/inputs/model'; +import { CONSTANTS } from '../url_state/constants'; +import { KqlQuery, URL_STATE_KEYS, KeyUrlState } from '../url_state/types'; + +import { + replaceQueryStringInLocation, + replaceStateKeyInQueryString, + getQueryStringFromLocation, +} from '../url_state/helpers'; +import { SearchNavTab, TabNavigationProps } from './type'; + +export const getSearch = (tab: SearchNavTab, urlState: TabNavigationProps): string => { + if (tab && tab.urlKey != null && URL_STATE_KEYS[tab.urlKey] != null) { + return URL_STATE_KEYS[tab.urlKey].reduce( + (myLocation: Location, urlKey: KeyUrlState) => { + let urlStateToReplace: UrlInputsModel | KqlQuery | string = urlState[CONSTANTS.timelineId]; + if (urlKey === CONSTANTS.kqlQuery && tab.urlKey === 'host') { + urlStateToReplace = tab.isDetailPage ? urlState.hostDetails : urlState.hosts; + } else if (urlKey === CONSTANTS.kqlQuery && tab.urlKey === 'network') { + urlStateToReplace = urlState.network; + } else if (urlKey === CONSTANTS.timerange) { + urlStateToReplace = urlState[CONSTANTS.timerange]; + } + myLocation = replaceQueryStringInLocation( + myLocation, + replaceStateKeyInQueryString(urlKey, urlStateToReplace)( + getQueryStringFromLocation(myLocation) + ) + ); + return myLocation; + }, + { + pathname: urlState.pathName, + hash: '', + search: '', + state: '', + } + ).search; + } + return ''; +}; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx index e15646d80ffb5..c138bc1cb087c 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx @@ -6,51 +6,27 @@ import { shallow } from 'enzyme'; import * as React from 'react'; -import { RouteComponentProps } from 'react-router'; import { CONSTANTS } from '../url_state/constants'; import { SiemNavigationComponent } from './'; import { setBreadcrumbs } from './breadcrumbs'; import { navTabs } from '../../pages/home/home_navigations'; import { TabNavigationProps } from './type'; +import { HostsTableType } from '../../store/hosts/model'; +import { RouteSpyState } from '../../utils/route/spy_routes'; jest.mock('./breadcrumbs', () => ({ setBreadcrumbs: jest.fn(), })); -type Action = 'PUSH' | 'POP' | 'REPLACE'; -type Props = RouteComponentProps & TabNavigationProps; -const pop: Action = 'POP'; describe('SIEM Navigation', () => { - const location = { - pathname: '/hosts', + const mockProps: TabNavigationProps & RouteSpyState = { + pageName: 'hosts', + pathName: '/hosts', + detailName: undefined, search: '', - state: '', - hash: '', - }; - - const mockProps: Props = { - location, - match: { - isExact: true, - params: {}, - path: '', - url: '', - }, + tabName: HostsTableType.authentications, navTabs, - history: { - length: 2, - location, - action: pop, - push: jest.fn(), - replace: jest.fn(), - go: jest.fn(), - goBack: jest.fn(), - goForward: jest.fn(), - block: jest.fn(), - createHref: jest.fn(), - listen: jest.fn(), - }, [CONSTANTS.timerange]: { global: { [CONSTANTS.timerange]: { @@ -87,13 +63,141 @@ describe('SIEM Navigation', () => { }, [CONSTANTS.timelineId]: '', }; - const wrapper = shallow(); + const wrapper = shallow(); test('it calls setBreadcrumbs with correct path on mount', () => { - expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, '/hosts', {}); + expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, { + detailName: undefined, + hostDetails: { filterQuery: null, queryLocation: null }, + hosts: { filterQuery: null, queryLocation: null }, + navTabs: { + hosts: { + disabled: false, + href: '#/link-to/hosts', + id: 'hosts', + name: 'Hosts', + urlKey: 'host', + }, + network: { + disabled: false, + href: '#/link-to/network', + id: 'network', + name: 'Network', + urlKey: 'network', + }, + overview: { + disabled: false, + href: '#/link-to/overview', + id: 'overview', + name: 'Overview', + urlKey: 'overview', + }, + timelines: { + disabled: false, + href: '#/link-to/timelines', + id: 'timelines', + name: 'Timelines', + urlKey: 'timeline', + }, + }, + network: { filterQuery: null, queryLocation: null }, + pageName: 'hosts', + pathName: '/hosts', + search: '', + tabName: 'authentications', + timelineId: '', + timerange: { + global: { + linkTo: ['timeline'], + timerange: { + from: 1558048243696, + fromStr: 'now-24h', + kind: 'relative', + to: 1558134643697, + toStr: 'now', + }, + }, + timeline: { + linkTo: ['global'], + timerange: { + from: 1558048243696, + fromStr: 'now-24h', + kind: 'relative', + to: 1558134643697, + toStr: 'now', + }, + }, + }, + }); }); test('it calls setBreadcrumbs with correct path on update', () => { - wrapper.setProps({ location: { pathname: '/network' } }); + wrapper.setProps({ + pageName: 'network', + pathName: '/network', + tabName: undefined, + }); wrapper.update(); - expect(setBreadcrumbs).toHaveBeenNthCalledWith(2, '/network', {}); + expect(setBreadcrumbs).toHaveBeenNthCalledWith(2, { + detailName: undefined, + hostDetails: { filterQuery: null, queryLocation: null }, + hosts: { filterQuery: null, queryLocation: null }, + navTabs: { + hosts: { + disabled: false, + href: '#/link-to/hosts', + id: 'hosts', + name: 'Hosts', + urlKey: 'host', + }, + network: { + disabled: false, + href: '#/link-to/network', + id: 'network', + name: 'Network', + urlKey: 'network', + }, + overview: { + disabled: false, + href: '#/link-to/overview', + id: 'overview', + name: 'Overview', + urlKey: 'overview', + }, + timelines: { + disabled: false, + href: '#/link-to/timelines', + id: 'timelines', + name: 'Timelines', + urlKey: 'timeline', + }, + }, + network: { filterQuery: null, queryLocation: null }, + pageName: 'network', + pathName: '/network', + search: '', + tabName: undefined, + timelineId: '', + timerange: { + global: { + linkTo: ['timeline'], + timerange: { + from: 1558048243696, + fromStr: 'now-24h', + kind: 'relative', + to: 1558134643697, + toStr: 'now', + }, + }, + timeline: { + linkTo: ['global'], + timerange: { + from: 1558048243696, + fromStr: 'now-24h', + kind: 'relative', + to: 1558134643697, + toStr: 'now', + }, + }, + }, + }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx index cbfb53f13779f..a5378445158c0 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { compose } from 'redux'; import { connect } from 'react-redux'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; +import { isEqual } from 'lodash/fp'; import { setBreadcrumbs } from './breadcrumbs'; import { TabNavigation } from './tab_navigation'; import { TabNavigationProps, SiemNavigationComponentProps } from './type'; @@ -22,14 +22,19 @@ import { networkModel, } from '../../store'; import { CONSTANTS } from '../url_state/constants'; +import { RouteSpyState, useRouteSpy } from '../../utils/route/spy_routes'; -export class SiemNavigationComponent extends React.Component< - RouteComponentProps & TabNavigationProps -> { - public shouldComponentUpdate(nextProps: Readonly): boolean { +export class SiemNavigationComponent extends React.Component { + public shouldComponentUpdate(nextProps: Readonly): boolean { if ( - this.props.location.pathname === nextProps.location.pathname && - this.props.location.search === nextProps.location.search + this.props.pathName === nextProps.pathName && + this.props.search === nextProps.search && + isEqual(this.props.hosts, nextProps.hosts) && + isEqual(this.props.hostDetails, nextProps.hostDetails) && + isEqual(this.props.network, nextProps.network) && + isEqual(this.props.navTabs, nextProps.navTabs) && + isEqual(this.props.timerange, nextProps.timerange) && + isEqual(this.props.timelineId, nextProps.timelineId) ) { return false; } @@ -38,40 +43,99 @@ export class SiemNavigationComponent extends React.Component< public componentWillMount(): void { const { - location, - match: { params }, + detailName, + hosts, + hostDetails, + navTabs, + network, + pageName, + pathName, + search, + tabName, + timerange, + timelineId, } = this.props; - if (location.pathname) { - setBreadcrumbs(location.pathname, params); + if (pathName) { + setBreadcrumbs({ + detailName, + hosts, + hostDetails, + navTabs, + network, + pageName, + pathName, + search, + tabName, + timerange, + timelineId, + }); } } - public componentWillReceiveProps(nextProps: Readonly): void { - if (this.props.location.pathname !== nextProps.location.pathname) { - setBreadcrumbs(nextProps.location.pathname, nextProps.match.params); + public componentWillReceiveProps(nextProps: Readonly): void { + if ( + this.props.pathName !== nextProps.pathName || + this.props.search !== nextProps.search || + !isEqual(this.props.hosts, nextProps.hosts) || + !isEqual(this.props.hostDetails, nextProps.hostDetails) || + !isEqual(this.props.network, nextProps.network) || + !isEqual(this.props.navTabs, nextProps.navTabs) || + !isEqual(this.props.timerange, nextProps.timerange) || + !isEqual(this.props.timelineId, nextProps.timelineId) + ) { + const { + detailName, + hosts, + hostDetails, + navTabs, + network, + pageName, + pathName, + search, + tabName, + timerange, + timelineId, + } = nextProps; + if (pathName) { + setBreadcrumbs({ + detailName, + hosts, + hostDetails, + navTabs, + network, + pageName, + pathName, + search, + tabName, + timerange, + timelineId, + }); + } } } public render() { const { display, - location, + pageName, + pathName, hosts, hostDetails, - match, navTabs, network, showBorder, + tabName, timerange, timelineId, } = this.props; return ( { return mapStateToProps; }; -export const SiemNavigation = compose>( - withRouter, - connect(makeMapStateToProps) -)(SiemNavigationComponent); +export const SiemNavigationRedux = compose< + React.ComponentClass +>(connect(makeMapStateToProps))(SiemNavigationComponent); + +export const SiemNavigation = React.memo(props => { + const [routeProps] = useRouteSpy(); + const stateNavReduxProps: RouteSpyState & SiemNavigationComponentProps = { + ...routeProps, + ...props, + }; + return ; +}); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx index 7216d825f9c3d..461d5c743b6f5 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx @@ -13,27 +13,21 @@ import { navTabs, SiemPageName } from '../../../pages/home/home_navigations'; import { HostsTableType } from '../../../store/hosts/model'; import { navTabsHostDetails } from '../../../pages/hosts/hosts_navigations'; import { CONSTANTS } from '../../url_state/constants'; +import { RouteSpyState } from '../../../utils/route/spy_routes'; describe('Tab Navigation', () => { const pageName = SiemPageName.hosts; const hostName = 'siem-window'; const tabName = HostsTableType.authentications; const pathName = `/${pageName}/${hostName}/${tabName}`; - const mockMatch = { - params: { + + describe('Page Navigation', () => { + const mockProps: TabNavigationProps & RouteSpyState = { pageName, - hostName, + pathName, + detailName: undefined, + search: '', tabName, - }, - }; - describe('Page Navigation', () => { - const mockProps: TabNavigationProps = { - location: { - pathname: pathName, - search: '', - state: '', - hash: '', - }, navTabs, [CONSTANTS.timerange]: { global: { @@ -77,7 +71,6 @@ describe('Tab Navigation', () => { test('it mounts with correct tab highlighted', () => { const wrapper = shallow(); const hostsTab = wrapper.find('[data-test-subj="navigation-hosts"]'); - expect(hostsTab.prop('isSelected')).toBeTruthy(); }); test('it changes active tab when nav changes by props', () => { @@ -85,12 +78,9 @@ describe('Tab Navigation', () => { const networkTab = () => wrapper.find('[data-test-subj="navigation-network"]'); expect(networkTab().prop('isSelected')).toBeFalsy(); wrapper.setProps({ - location: { - pathname: '/network', - search: '', - state: '', - hash: '', - }, + pageName: 'network', + pathName: '/network', + tabName: undefined, }); wrapper.update(); expect(networkTab().prop('isSelected')).toBeTruthy(); @@ -105,15 +95,13 @@ describe('Tab Navigation', () => { }); describe('Table Navigation', () => { - const mockProps: TabNavigationProps = { - location: { - pathname: pathName, - search: '', - state: '', - hash: '', - }, + const mockProps: TabNavigationProps & RouteSpyState = { + pageName: 'hosts', + pathName: '/hosts', + detailName: undefined, + search: '', + tabName: HostsTableType.authentications, navTabs: navTabsHostDetails(hostName), - match: mockMatch, [CONSTANTS.timerange]: { global: { [CONSTANTS.timerange]: { @@ -162,18 +150,15 @@ describe('Tab Navigation', () => { expect(tableNavigationTab.prop('isSelected')).toBeTruthy(); }); test('it changes active tab when nav changes by props', () => { - const newMatch = { - params: { - pageName: SiemPageName.hosts, - hostName, - tabName: HostsTableType.events, - }, - }; const wrapper = shallow(); const tableNavigationTab = () => wrapper.find(`[data-test-subj="navigation-${HostsTableType.events}"]`); expect(tableNavigationTab().prop('isSelected')).toBeFalsy(); - wrapper.setProps({ location: `/${SiemPageName.hosts}`, match: newMatch }); + wrapper.setProps({ + pageName: SiemPageName.hosts, + pathName: `/${SiemPageName.hosts}`, + tabName: HostsTableType.events, + }); wrapper.update(); expect(tableNavigationTab().prop('isSelected')).toBeTruthy(); }); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx index 5c831bf51e23d..d2e49ee926a88 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx @@ -5,23 +5,15 @@ */ import { EuiTab, EuiTabs, EuiLink } from '@elastic/eui'; import { get, getOr } from 'lodash/fp'; -import { Location } from 'history'; + import * as React from 'react'; import styled from 'styled-components'; import classnames from 'classnames'; import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/track_usage'; import { HostsTableType } from '../../../store/hosts/model'; -import { UrlInputsModel } from '../../../store/inputs/model'; -import { CONSTANTS } from '../../url_state/constants'; -import { KqlQuery, URL_STATE_KEYS, KeyUrlState } from '../../url_state/types'; -import { NavTab, NavMatchParams, TabNavigationProps } from '../type'; - -import { - replaceQueryStringInLocation, - replaceStateKeyInQueryString, - getQueryStringFromLocation, -} from '../../url_state/helpers'; +import { NavTab, TabNavigationProps } from '../type'; +import { getSearch } from '../helpers'; const TabContainer = styled.div` .euiLink { @@ -42,15 +34,11 @@ interface TabNavigationState { export class TabNavigation extends React.PureComponent { constructor(props: TabNavigationProps) { super(props); - const pathname = props.location.pathname; - const match = props.match; - const selectedTabId = this.mapLocationToTab(pathname, match); + const selectedTabId = this.mapLocationToTab(props.pageName, props.tabName); this.state = { selectedTabId }; } public componentWillReceiveProps(nextProps: TabNavigationProps): void { - const pathname = nextProps.location.pathname; - const match = nextProps.match; - const selectedTabId = this.mapLocationToTab(pathname, match); + const selectedTabId = this.mapLocationToTab(nextProps.pageName, nextProps.tabName); if (this.state.selectedTabId !== selectedTabId) { this.setState(prevState => ({ @@ -68,12 +56,11 @@ export class TabNavigation extends React.PureComponent { + public mapLocationToTab = (pageName: string, tabName?: HostsTableType): string => { const { navTabs } = this.props; - const tabName: HostsTableType | undefined = get('params.tabName', match); const myNavTab = Object.keys(navTabs) .map(tab => get(tab, navTabs)) - .filter((item: NavTab) => (tabName || pathname).includes(item.id))[0]; + .filter((item: NavTab) => tabName === item.id || pageName === item.id)[0]; return getOr('', 'id', myNavTab); }; @@ -88,7 +75,7 @@ export class TabNavigation extends React.PureComponent { - return URL_STATE_KEYS[tab.urlKey].reduce( - (myLocation: Location, urlKey: KeyUrlState) => { - let urlStateToReplace: UrlInputsModel | KqlQuery | string = this.props[ - CONSTANTS.timelineId - ]; - if (urlKey === CONSTANTS.kqlQuery && tab.urlKey === 'host') { - urlStateToReplace = tab.isDetailPage ? this.props.hostDetails : this.props.hosts; - } else if (urlKey === CONSTANTS.kqlQuery && tab.urlKey === 'network') { - urlStateToReplace = this.props.network; - } else if (urlKey === CONSTANTS.timerange) { - urlStateToReplace = this.props[CONSTANTS.timerange]; - } - myLocation = replaceQueryStringInLocation( - myLocation, - replaceStateKeyInQueryString(urlKey, urlStateToReplace)( - getQueryStringFromLocation(myLocation) - ) - ); - return myLocation; - }, - { - ...this.props.location, - search: '', - } - ).search; - }; } diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/type.ts b/x-pack/legacy/plugins/siem/public/components/navigation/type.ts index ff96f28ed815e..e9e4f715ff798 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/type.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/type.ts @@ -3,12 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Location } from 'history'; import { UrlInputsModel } from '../../store/inputs/model'; import { CONSTANTS } from '../url_state/constants'; import { KqlQuery, UrlStateType } from '../url_state/types'; -import { NavigationParams } from './breadcrumbs'; +import { HostsTableType } from '../../store/hosts/model'; export interface NavTab { id: string; @@ -19,10 +18,6 @@ export interface NavTab { isDetailPage?: boolean; } -export interface NavMatchParams { - params: NavigationParams; -} - export interface SiemNavigationComponentProps { display?: 'default' | 'condensed'; navTabs: Record; @@ -30,11 +25,14 @@ export interface SiemNavigationComponentProps { } export interface TabNavigationProps extends SiemNavigationComponentProps { - location: Location; + pathName: string; + pageName: string; + tabName: HostsTableType | undefined; hosts: KqlQuery; hostDetails: KqlQuery; network: KqlQuery; [CONSTANTS.timerange]: UrlInputsModel; [CONSTANTS.timelineId]: string; - match?: NavMatchParams; } + +export type SearchNavTab = NavTab | { urlKey: UrlStateType; isDetailPage: boolean }; diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.test.tsx index f2f37c78807a0..c08b877076cbe 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.test.tsx @@ -90,7 +90,7 @@ describe('AddToKql Component', () => { activePage: 0, limit: 10, }, - hosts: { + allHosts: { activePage: 0, limit: 10, direction: 'desc', diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.test.tsx index a4399a16dbf05..700340b8c9dd2 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.test.tsx @@ -21,6 +21,7 @@ import { createStore, hostsModel, State } from '../../../../store'; import { HostsTable } from './index'; import { mockData } from './mock'; +import { HostsTableType } from '../../../../store/hosts/model'; describe('Hosts Table', () => { const loadPage = jest.fn(); @@ -100,7 +101,7 @@ describe('Hosts Table', () => { ); }); test('Initial value of the store', () => { - expect(store.getState().hosts.page.queries.hosts).toEqual({ + expect(store.getState().hosts.page.queries[HostsTableType.hosts]).toEqual({ activePage: 0, direction: 'desc', sortField: 'lastSeen', @@ -128,7 +129,7 @@ describe('Hosts Table', () => { wrapper.update(); - expect(store.getState().hosts.page.queries.hosts).toEqual({ + expect(store.getState().hosts.page.queries[HostsTableType.hosts]).toEqual({ activePage: 0, direction: 'asc', sortField: 'hostName', diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/url_state/__snapshots__/index.test.tsx.snap index e3195baf20fd3..7bc1c34a182da 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/url_state/__snapshots__/index.test.tsx.snap @@ -333,28 +333,11 @@ exports[`UrlStateContainer mounts and renders 1`] = ` "state": "", }, "push": [MockFunction], - "replace": [MockFunction] { - "calls": Array [ - Array [ - Object { - "hash": "", - "pathname": "/network", - "search": "?timerange=(global:(linkTo:!(timeline),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)),timeline:(linkTo:!(global),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)))", - "state": "", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, + "replace": [MockFunction], } } > - - - + - - - - + } + /> + + diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.test.ts index 7a0f1402d765a..3b9056fa4cca3 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.test.ts @@ -3,45 +3,66 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { SiemPageName } from '../../pages/home/home_navigations'; -import { isKqlForRoute } from './helpers'; +import { isKqlForRoute, getTitle } from './helpers'; import { CONSTANTS } from './constants'; -describe('isKqlForRoute', () => { - test('host page and host page kuery', () => { - const result = isKqlForRoute('/hosts', CONSTANTS.hostsPage); - expect(result).toBeTruthy(); +describe('Helpers Url_State', () => { + describe('isKqlForRoute', () => { + test('host page and host page kuery', () => { + const result = isKqlForRoute(SiemPageName.hosts, undefined, CONSTANTS.hostsPage); + expect(result).toBeTruthy(); + }); + test('host page and host details kuery', () => { + const result = isKqlForRoute(SiemPageName.hosts, undefined, CONSTANTS.hostsDetails); + expect(result).toBeFalsy(); + }); + test('host details and host details kuery', () => { + const result = isKqlForRoute(SiemPageName.hosts, 'siem-kibana', CONSTANTS.hostsDetails); + expect(result).toBeTruthy(); + }); + test('host details and host page kuery', () => { + const result = isKqlForRoute(SiemPageName.hosts, 'siem-kibana', CONSTANTS.hostsPage); + expect(result).toBeFalsy(); + }); + test('network page and network page kuery', () => { + const result = isKqlForRoute(SiemPageName.network, undefined, CONSTANTS.networkPage); + expect(result).toBeTruthy(); + }); + test('network page and network details kuery', () => { + const result = isKqlForRoute(SiemPageName.network, undefined, CONSTANTS.networkDetails); + expect(result).toBeFalsy(); + }); + test('network details and network details kuery', () => { + const result = isKqlForRoute(SiemPageName.network, '10.100.7.198', CONSTANTS.networkDetails); + expect(result).toBeTruthy(); + }); + test('network details and network page kuery', () => { + const result = isKqlForRoute(SiemPageName.network, '123.234.34', CONSTANTS.networkPage); + expect(result).toBeFalsy(); + }); }); - test('host page and host details kuery', () => { - const result = isKqlForRoute('/hosts', CONSTANTS.hostsDetails); - expect(result).toBeFalsy(); - }); - test('works when there is a trailing slash', () => { - const result = isKqlForRoute('/hosts/', CONSTANTS.hostsPage); - expect(result).toBeTruthy(); - }); - test('host details and host details kuery', () => { - const result = isKqlForRoute('/hosts/siem-kibana', CONSTANTS.hostsDetails); - expect(result).toBeTruthy(); - }); - test('host details and host page kuery', () => { - const result = isKqlForRoute('/hosts/siem-kibana', CONSTANTS.hostsPage); - expect(result).toBeFalsy(); - }); - test('network page and network page kuery', () => { - const result = isKqlForRoute('/network', CONSTANTS.networkPage); - expect(result).toBeTruthy(); - }); - test('network page and network details kuery', () => { - const result = isKqlForRoute('/network', CONSTANTS.networkDetails); - expect(result).toBeFalsy(); - }); - test('network details and network details kuery', () => { - const result = isKqlForRoute('/network/ip/10.100.7.198', CONSTANTS.networkDetails); - expect(result).toBeTruthy(); - }); - test('network details and network page kuery', () => { - const result = isKqlForRoute('/network/ip/123.234.34', CONSTANTS.networkPage); - expect(result).toBeFalsy(); + describe('getTitle', () => { + test('host page name', () => { + const result = getTitle('hosts', undefined); + expect(result).toEqual('Hosts'); + }); + test('network page name', () => { + const result = getTitle('network', undefined); + expect(result).toEqual('Network'); + }); + test('overview page name', () => { + const result = getTitle('overview', undefined); + expect(result).toEqual('Overview'); + }); + test('timelines page name', () => { + const result = getTitle('timelines', undefined); + expect(result).toEqual('Timelines'); + }); + test('details page name', () => { + const result = getTitle('hosts', 'details'); + expect(result).toEqual('details'); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index 3a70b2d230c95..3acdf60ba6692 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -87,7 +87,8 @@ export const getUrlType = (pageName: string): UrlStateType => { return 'overview'; }; -export const getTitle = (pageName: string): string => { +export const getTitle = (pageName: string, detailName: string | undefined): string => { + if (detailName != null) return detailName; if (pageName === SiemPageName.hosts) { return i18n.HOSTS; } else if (pageName === SiemPageName.network) { diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx index a6c744d4affca..f1258b7182aff 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx @@ -19,6 +19,8 @@ import { defaultProps, getMockPropsObj, mockHistory, testCases } from './test_de import { UrlStateContainerPropTypes } from './types'; import { useUrlStateHooks } from './use_url_state'; import { CONSTANTS } from './constants'; +import { RouteSpyState } from '../../utils/route/spy_routes'; +import { SiemPageName } from '../../pages/home/home_navigations'; let mockProps: UrlStateContainerPropTypes; @@ -33,6 +35,19 @@ const indexPattern: StaticIndexPattern = { }, ], }; + +// const mockUseRouteSpy: jest.Mock = useRouteSpy as jest.Mock; +const mockRouteSpy: RouteSpyState = { + pageName: SiemPageName.network, + detailName: undefined, + tabName: undefined, + search: '', + pathName: '/network', +}; +jest.mock('../../utils/route/spy_routes', () => ({ + useRouteSpy: () => [mockRouteSpy], +})); + describe('UrlStateContainer', () => { const state: State = mockGlobalState; @@ -62,53 +77,64 @@ describe('UrlStateContainer', () => { describe('handleInitialize', () => { describe('URL state updates redux', () => { describe('relative timerange actions are called with correct data on component mount', () => { - test.each(testCases)('%o', (page, namespaceLower, namespaceUpper, examplePath) => { - mockProps = getMockPropsObj({ page, examplePath, namespaceLower }).relativeTimeSearch - .undefinedQuery; - mount( useUrlStateHooks(args)} />); - - // @ts-ignore property mock does not exists - expect(defaultProps.setRelativeTimerange.mock.calls[1][0]).toEqual({ - from: 1558591200000, - fromStr: 'now-1d/d', - kind: 'relative', - to: 1558677599999, - toStr: 'now-1d/d', - id: 'global', - }); - // @ts-ignore property mock does not exists - expect(defaultProps.setRelativeTimerange.mock.calls[0][0]).toEqual({ - from: 1558732849370, - fromStr: 'now-15m', - kind: 'relative', - to: 1558733749370, - toStr: 'now', - id: 'timeline', - }); - }); + test.each(testCases)( + '%o', + (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { + mockProps = getMockPropsObj({ + page, + examplePath, + namespaceLower, + pageName, + detailName, + }).relativeTimeSearch.undefinedQuery; + mount( useUrlStateHooks(args)} />); + + // @ts-ignore property mock does not exists + expect(defaultProps.setRelativeTimerange.mock.calls[1][0]).toEqual({ + from: 1558591200000, + fromStr: 'now-1d/d', + kind: 'relative', + to: 1558677599999, + toStr: 'now-1d/d', + id: 'global', + }); + // @ts-ignore property mock does not exists + expect(defaultProps.setRelativeTimerange.mock.calls[0][0]).toEqual({ + from: 1558732849370, + fromStr: 'now-15m', + kind: 'relative', + to: 1558733749370, + toStr: 'now', + id: 'timeline', + }); + } + ); }); describe('absolute timerange actions are called with correct data on component mount', () => { - test.each(testCases)('%o', (page, namespaceLower, namespaceUpper, examplePath) => { - mockProps = getMockPropsObj({ page, examplePath, namespaceLower }).absoluteTimeSearch - .undefinedQuery; - mount( useUrlStateHooks(args)} />); - - // @ts-ignore property mock does not exists - expect(defaultProps.setAbsoluteTimerange.mock.calls[1][0]).toEqual({ - from: 1556736012685, - kind: 'absolute', - to: 1556822416082, - id: 'global', - }); - // @ts-ignore property mock does not exists - expect(defaultProps.setAbsoluteTimerange.mock.calls[0][0]).toEqual({ - from: 1556736012685, - kind: 'absolute', - to: 1556822416082, - id: 'timeline', - }); - }); + test.each(testCases)( + '%o', + (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { + mockProps = getMockPropsObj({ page, examplePath, namespaceLower, pageName, detailName }) + .absoluteTimeSearch.undefinedQuery; + mount( useUrlStateHooks(args)} />); + + // @ts-ignore property mock does not exists + expect(defaultProps.setAbsoluteTimerange.mock.calls[1][0]).toEqual({ + from: 1556736012685, + kind: 'absolute', + to: 1556822416082, + id: 'global', + }); + // @ts-ignore property mock does not exists + expect(defaultProps.setAbsoluteTimerange.mock.calls[0][0]).toEqual({ + from: 1556736012685, + kind: 'absolute', + to: 1556822416082, + id: 'timeline', + }); + } + ); }); describe('kqlQuery action is called with correct data on component mount', () => { @@ -122,9 +148,9 @@ describe('UrlStateContainer', () => { }; test.each(testCases.slice(0, 4))( ' %o', - (page, namespaceLower, namespaceUpper, examplePath, type) => { - mockProps = getMockPropsObj({ page, examplePath, namespaceLower }).relativeTimeSearch - .undefinedQuery; + (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { + mockProps = getMockPropsObj({ page, examplePath, namespaceLower, pageName, detailName }) + .relativeTimeSearch.undefinedQuery; mount( useUrlStateHooks(args)} />); const functionName = namespaceUpper === 'Network' ? defaultProps.setNetworkKql : defaultProps.setHostsKql; @@ -138,36 +164,53 @@ describe('UrlStateContainer', () => { }); describe('kqlQuery action is not called called when the queryLocation does not match the router location', () => { - test.each(testCases)('%o', (page, namespaceLower, namespaceUpper, examplePath) => { - mockProps = getMockPropsObj({ page, examplePath, namespaceLower }) - .oppositeQueryLocationSearch.undefinedQuery; - mount( useUrlStateHooks(args)} />); - const functionName = - namespaceUpper === 'Network' ? defaultProps.setNetworkKql : defaultProps.setHostsKql; - // @ts-ignore property mock does not exists - expect(functionName.mock.calls.length).toEqual(0); - }); + test.each(testCases)( + '%o', + (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { + mockProps = getMockPropsObj({ + page, + examplePath, + namespaceLower, + pageName, + detailName, + }).oppositeQueryLocationSearch.undefinedQuery; + mount( useUrlStateHooks(args)} />); + const functionName = + namespaceUpper === 'Network' ? defaultProps.setNetworkKql : defaultProps.setHostsKql; + // @ts-ignore property mock does not exists + expect(functionName.mock.calls.length).toEqual(0); + } + ); }); }); describe('Redux updates URL state', () => { describe('kqlQuery url state is set from redux data on component mount', () => { - test.each(testCases)('%o', (page, namespaceLower, namespaceUpper, examplePath) => { - mockProps = getMockPropsObj({ page, examplePath, namespaceLower }).noSearch.definedQuery; - mount( useUrlStateHooks(args)} />); - - // @ts-ignore property mock does not exists - expect( - mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0] - ).toEqual({ - hash: '', - pathname: examplePath, - search: [CONSTANTS.overviewPage, CONSTANTS.timelinePage].includes(page) - ? '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))' - : `?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:${page})&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, - state: '', - }); - }); + test.each(testCases)( + '%o', + (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { + mockProps = getMockPropsObj({ + page, + examplePath, + namespaceLower, + pageName, + detailName, + }).noSearch.definedQuery; + mount( useUrlStateHooks(args)} />); + + // @ts-ignore property mock does not exists + expect( + mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0] + ).toEqual({ + hash: '', + pathname: examplePath, + search: [CONSTANTS.overviewPage, CONSTANTS.timelinePage].includes(page) + ? '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))' + : `?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:${page})&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, + state: '', + }); + } + ); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx index f194e4cc6f425..6598bd491f73b 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx @@ -9,6 +9,7 @@ import { difference } from 'lodash/fp'; import * as React from 'react'; import { HookWrapper } from '../../mock/hook_wrapper'; +import { SiemPageName } from '../../pages/home/home_navigations'; import { CONSTANTS } from './constants'; import { getFilterQuery, getMockPropsObj, mockHistory, testCases } from './test_dependencies'; @@ -36,6 +37,8 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => page: CONSTANTS.networkPage, examplePath: '/network', namespaceLower: 'network', + pageName: SiemPageName.network, + detailName: undefined, }).noSearch.definedQuery; const wrapper = mount( useUrlStateHooks(args)} /> @@ -85,6 +88,8 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => page: CONSTANTS.networkPage, examplePath: '/network', namespaceLower: 'network', + pageName: SiemPageName.network, + detailName: undefined, }).noSearch.undefinedQuery; const wrapper = mount( useUrlStateHooks(args)} /> @@ -117,6 +122,8 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => page: CONSTANTS.networkPage, examplePath: '/network', namespaceLower: 'network', + pageName: SiemPageName.network, + detailName: undefined, }).noSearch.undefinedQuery; const wrapper = mount( useUrlStateHooks(args)} /> @@ -145,39 +152,46 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => describe('handleInitialize', () => { describe('Redux updates URL state', () => { describe('Timerange url state is set when not defined on component mount', () => { - test.each(testCases)('%o', (page, namespaceLower, namespaceUpper, examplePath) => { - mockProps = getMockPropsObj({ page, examplePath, namespaceLower }).noSearch - .undefinedQuery; - mount( useUrlStateHooks(args)} />); - - expect(mockHistory.replace.mock.calls[0][0]).toEqual({ - hash: '', - pathname: examplePath, - search: '?_g=()', - state: '', - }); - - expect( - mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0] - ).toEqual({ - hash: '', - pathname: examplePath, - search: - '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', - state: '', - }); - }); + test.each(testCases)( + '%o', + (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { + mockProps = getMockPropsObj({ page, examplePath, namespaceLower, pageName, detailName }) + .noSearch.undefinedQuery; + mount( useUrlStateHooks(args)} />); + + expect(mockHistory.replace.mock.calls[0][0]).toEqual({ + hash: '', + pathname: examplePath, + search: '?_g=()', + state: '', + }); + + expect( + mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0] + ).toEqual({ + hash: '', + pathname: examplePath, + search: + '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + state: '', + }); + } + ); test('url state is set from redux data when location updates and initialization', () => { mockProps = getMockPropsObj({ page: CONSTANTS.hostsPage, examplePath: '/hosts', namespaceLower: 'hosts', + pageName: SiemPageName.hosts, + detailName: undefined, }).noSearch.undefinedQuery; const updatedProps = getMockPropsObj({ page: CONSTANTS.networkPage, examplePath: '/network', namespaceLower: 'network', + pageName: SiemPageName.network, + detailName: undefined, }).noSearch.definedQuery; const wrapper = mount( useUrlStateHooks(args)} /> diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts b/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts index d1f171b0f2d35..7e61a15eb852b 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts @@ -132,7 +132,9 @@ export const defaultProps: UrlStateContainerPropTypes = { export const getMockProps = ( location = defaultLocation, kqlQueryKey = CONSTANTS.networkPage, - kqlQueryValue: KqlQuery | null + kqlQueryValue: KqlQuery | null, + pageName: string, + detailName: string | undefined ): UrlStateContainerPropTypes => ({ ...defaultProps, urlState: { @@ -143,15 +145,27 @@ export const getMockProps = ( ...mockHistory, location, }, + detailName, + pageName, + pathName: location.pathname, + search: location.search, }); interface GetMockPropsObj { examplePath: string; namespaceLower: string; page: LocationTypes; + pageName: string; + detailName: string | undefined; } -export const getMockPropsObj = ({ page, examplePath, namespaceLower }: GetMockPropsObj) => ({ +export const getMockPropsObj = ({ + page, + examplePath, + namespaceLower, + pageName, + detailName, +}: GetMockPropsObj) => ({ noSearch: { undefinedQuery: getMockProps( { @@ -161,7 +175,9 @@ export const getMockPropsObj = ({ page, examplePath, namespaceLower }: GetMockPr state: '', }, page, - null + null, + pageName, + detailName ), definedQuery: getMockProps( { @@ -171,7 +187,9 @@ export const getMockPropsObj = ({ page, examplePath, namespaceLower }: GetMockPr state: '', }, page, - getFilterQuery(page) + getFilterQuery(page), + pageName, + detailName ), }, relativeTimeSearch: { @@ -183,7 +201,9 @@ export const getMockPropsObj = ({ page, examplePath, namespaceLower }: GetMockPr state: '', }, page, - null + null, + pageName, + detailName ), definedQuery: getMockProps( { @@ -193,7 +213,9 @@ export const getMockPropsObj = ({ page, examplePath, namespaceLower }: GetMockPr state: '', }, page, - getFilterQuery(page) + getFilterQuery(page), + pageName, + detailName ), }, absoluteTimeSearch: { @@ -206,7 +228,9 @@ export const getMockPropsObj = ({ page, examplePath, namespaceLower }: GetMockPr state: '', }, page, - null + null, + pageName, + detailName ), definedQuery: getMockProps( { @@ -217,7 +241,9 @@ export const getMockPropsObj = ({ page, examplePath, namespaceLower }: GetMockPr state: '', }, page, - getFilterQuery(page) + getFilterQuery(page), + pageName, + detailName ), }, oppositeQueryLocationSearch: { @@ -231,7 +257,9 @@ export const getMockPropsObj = ({ page, examplePath, namespaceLower }: GetMockPr state: '', }, page, - null + null, + pageName, + detailName ), }, }); @@ -243,40 +271,54 @@ export const testCases = [ /* page */ CONSTANTS.networkPage, /* namespaceLower */ 'network', /* namespaceUpper */ 'Network', - /* examplePath */ '/network', + /* pathName */ '/network', /* type */ networkModel.NetworkType.page, + /* pageName */ SiemPageName.network, + /* detailName */ undefined, ], [ /* page */ CONSTANTS.hostsPage, /* namespaceLower */ 'hosts', /* namespaceUpper */ 'Hosts', - /* examplePath */ '/hosts', + /* pathName */ '/hosts', /* type */ hostsModel.HostsType.page, + /* pageName */ SiemPageName.hosts, + /* detailName */ undefined, ], [ /* page */ CONSTANTS.hostsDetails, /* namespaceLower */ 'hosts', /* namespaceUpper */ 'Hosts', - /* examplePath */ '/hosts/siem-es', + /* pathName */ '/hosts/siem-es', /* type */ hostsModel.HostsType.details, + /* pageName */ SiemPageName.hosts, + /* detailName */ 'host-test', ], [ /* page */ CONSTANTS.networkDetails, /* namespaceLower */ 'network', /* namespaceUpper */ 'Network', - /* examplePath */ '/network/ip/100.90.80', + /* pathName */ '/network/ip/100.90.80', /* type */ networkModel.NetworkType.details, + /* pageName */ SiemPageName.network, + /* detailName */ '100.90.80', ], [ /* page */ CONSTANTS.overviewPage, /* namespaceLower */ 'overview', /* namespaceUpper */ 'Overview', - /* examplePath */ '/overview', + /* pathName */ '/overview', + /* type */ null, + /* pageName */ SiemPageName.overview, + /* detailName */ undefined, ], [ /* page */ CONSTANTS.timelinePage, /* namespaceLower */ 'timeline', /* namespaceUpper */ 'Timeline', - /* examplePath */ '/timeline', + /* pathName */ '/timeline', + /* type */ null, + /* pageName */ SiemPageName.timelines, + /* detailName */ undefined, ], ]; diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx index b5facc71cfb80..6481251745c95 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx @@ -279,7 +279,7 @@ export const useUrlStateHooks = ({ }); useEffect(() => { - document.title = `${getTitle(pageName)} - Kibana`; + document.title = `${getTitle(pageName, detailName)} - Kibana`; }, [pageName]); return null; diff --git a/x-pack/legacy/plugins/siem/public/mock/global_state.ts b/x-pack/legacy/plugins/siem/public/mock/global_state.ts index 7a19c65ec9f34..83fa30c97145f 100644 --- a/x-pack/legacy/plugins/siem/public/mock/global_state.ts +++ b/x-pack/legacy/plugins/siem/public/mock/global_state.ts @@ -38,7 +38,7 @@ export const mockGlobalState: State = { page: { queries: { authentications: { activePage: 0, limit: 10 }, - hosts: { + allHosts: { activePage: 0, limit: 10, direction: Direction.desc, @@ -54,7 +54,7 @@ export const mockGlobalState: State = { details: { queries: { authentications: { activePage: 0, limit: 10 }, - hosts: { + allHosts: { activePage: 0, limit: 10, direction: Direction.desc, diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx index 0900024c389d9..d05c59c791c9a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx @@ -140,7 +140,7 @@ export const HomePage = pure(() => ( - + } /> diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts index 9ed80ef7fd2e6..5a2ecb65888e3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get, isEmpty } from 'lodash/fp'; +import { isEmpty } from 'lodash/fp'; import { Breadcrumb } from 'ui/chrome'; import { StaticIndexPattern } from 'ui/index_patterns'; @@ -13,11 +13,11 @@ import { ESTermQuery } from '../../../../common/typed_json'; import { hostsModel, hostsSelectors } from '../../../store/hosts'; import { HostsTableType } from '../../../store/hosts/model'; import { State } from '../../../store'; -import { NavigationParams } from '../../../components/navigation/breadcrumbs'; import { getHostsUrl, getHostDetailsUrl } from '../../../components/link_to/redirect_to_hosts'; import * as i18n from '../translations'; import { convertKueryToElasticSearchQuery, escapeQueryValue } from '../../../lib/keury'; +import { RouteSpyState } from '../../../utils/route/spy_routes'; export const type = hostsModel.HostsType.details; @@ -36,29 +36,27 @@ const TabNameMappedToI18nKey = { [HostsTableType.events]: i18n.NAVIGATION_EVENTS_TITLE, }; -export const getBreadcrumbs = (params: NavigationParams): Breadcrumb[] => { - const hostName = get('detailName', params); - const tabName = get('tabName', params); +export const getBreadcrumbs = (params: RouteSpyState, search: string[]): Breadcrumb[] => { let breadcrumb = [ { text: i18n.PAGE_TITLE, - href: getHostsUrl(), + href: `${getHostsUrl()}${search && search[0] ? search[0] : ''}`, }, ]; - if (hostName) { + if (params.detailName != null) { breadcrumb = [ ...breadcrumb, { - text: hostName, - href: getHostDetailsUrl(hostName), + text: params.detailName, + href: `${getHostDetailsUrl(params.detailName)}${search && search[1] ? search[1] : ''}`, }, ]; } - if (tabName) { + if (params.tabName != null) { breadcrumb = [ ...breadcrumb, { - text: TabNameMappedToI18nKey[tabName], + text: TabNameMappedToI18nKey[params.tabName], href: '', }, ]; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_navigations.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_navigations.tsx index d0fa9ac0b492f..52416bf6997a8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_navigations.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_navigations.tsx @@ -25,9 +25,9 @@ import { ESTermQuery } from '../../../common/typed_json'; import { HostsTableType } from '../../store/hosts/model'; import { StatefulEventsViewer } from '../../components/events_viewer'; -const getTabsOnHostsUrl = (tabName: HostsTableType) => `#/link-to/hosts/${tabName}`; +const getTabsOnHostsUrl = (tabName: HostsTableType) => `#/hosts/${tabName}`; const getTabsOnHostDetailsUrl = (hostName: string, tabName: HostsTableType) => { - return `#/link-to/hosts/${hostName}/${tabName}`; + return `#/hosts/${hostName}/${tabName}`; }; export type KeyHostsNavTab = diff --git a/x-pack/legacy/plugins/siem/public/pages/network/__snapshots__/ip_details.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/network/__snapshots__/ip_details.test.tsx.snap index 633b68e9359be..08766051ec8b2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/__snapshots__/ip_details.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/pages/network/__snapshots__/ip_details.test.tsx.snap @@ -36,7 +36,7 @@ exports[`Ip Details it matches the snapshot 1`] = ` Object { "isExact": true, "params": Object { - "ip": "123.456.78.90", + "detailName": "123.456.78.90", "search": "", }, "path": "", diff --git a/x-pack/legacy/plugins/siem/public/pages/network/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/index.tsx index b9986c06a3608..9e75abcf67111 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/index.tsx @@ -9,37 +9,17 @@ import { Redirect, Route, Switch } from 'react-router-dom'; import { pure } from 'recompose'; -import { i18n } from '@kbn/i18n'; import { NetworkComponentProps } from '../../components/link_to/redirect_to_network'; import { IPDetails } from './ip_details'; import { Network } from './network'; -import { PageRoute } from '../../components/page_route/pageroute'; const networkPath = `/:pageName(network)`; export const NetworkContainer = pure(({ match }) => ( <> - ( - - )} - /> - ( - - )} - /> + } /> + } /> diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details.tsx index a4645a7c7aeee..66b87f04d5425 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details.tsx @@ -297,11 +297,11 @@ export const IPDetails = connect( } )(IPDetailsComponent); -export const getBreadcrumbs = (ip: string): Breadcrumb[] => { +export const getBreadcrumbs = (ip: string | undefined, search: string[]): Breadcrumb[] => { const breadcrumbs = [ { text: i18n.PAGE_TITLE, - href: getNetworkUrl(), + href: `${getNetworkUrl()}${search && search[0] ? search[0] : ''}`, }, ]; if (ip) { diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/overview.test.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/overview.test.tsx index 160379567133f..833030e0dc8a1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/overview.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/overview.test.tsx @@ -5,15 +5,16 @@ */ import { mount } from 'enzyme'; +import { cloneDeep } from 'lodash/fp'; import * as React from 'react'; +import { MockedProvider } from 'react-apollo/test-utils'; +import { MemoryRouter } from 'react-router-dom'; import { Overview } from './index'; import '../../mock/ui_settings'; import { mocksSource } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; -import { MockedProvider } from 'react-apollo/test-utils'; -import { cloneDeep } from 'lodash/fp'; jest.mock('ui/documentation_links', () => ({ documentationLinks: { @@ -45,7 +46,9 @@ describe('Overview', () => { const wrapper = mount( - + + + ); @@ -60,7 +63,9 @@ describe('Overview', () => { const wrapper = mount( - + + + ); diff --git a/x-pack/legacy/plugins/siem/public/store/hosts/model.ts b/x-pack/legacy/plugins/siem/public/store/hosts/model.ts index 29469c129c23f..69efa404d2eee 100644 --- a/x-pack/legacy/plugins/siem/public/store/hosts/model.ts +++ b/x-pack/legacy/plugins/siem/public/store/hosts/model.ts @@ -14,7 +14,7 @@ export enum HostsType { export enum HostsTableType { authentications = 'authentications', - hosts = 'hosts', + hosts = 'allHosts', events = 'events', uncommonProcesses = 'uncommonProcesses', anomalies = 'anomalies', diff --git a/x-pack/legacy/plugins/siem/public/store/hosts/reducer.ts b/x-pack/legacy/plugins/siem/public/store/hosts/reducer.ts index 80d7876902203..a597386942cb1 100644 --- a/x-pack/legacy/plugins/siem/public/store/hosts/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/hosts/reducer.ts @@ -106,8 +106,8 @@ export const hostsReducer = reducerWithInitialState(initialHostsState) ...state[hostsType], queries: { ...state[hostsType].queries, - hosts: { - ...state[hostsType].queries.hosts, + [HostsTableType.hosts]: { + ...state[hostsType].queries[HostsTableType.hosts], direction: sort.direction, sortField: sort.field, }, diff --git a/x-pack/legacy/plugins/siem/public/store/hosts/selectors.ts b/x-pack/legacy/plugins/siem/public/store/hosts/selectors.ts index dcd6cd8e67006..a4cf0715ef6da 100644 --- a/x-pack/legacy/plugins/siem/public/store/hosts/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/store/hosts/selectors.ts @@ -10,7 +10,7 @@ import { createSelector } from 'reselect'; import { isFromKueryExpressionValid } from '../../lib/keury'; import { State } from '../reducer'; -import { GenericHostsModel, HostsType } from './model'; +import { GenericHostsModel, HostsType, HostsTableType } from './model'; const selectHosts = (state: State, hostsType: HostsType): GenericHostsModel => get(hostsType, state.hosts); @@ -24,7 +24,7 @@ export const authenticationsSelector = () => export const hostsSelector = () => createSelector( selectHosts, - hosts => hosts.queries.hosts + hosts => hosts.queries[HostsTableType.hosts] ); export const eventsSelector = () =>