From af21c66d5c19889a151dcaf2a5d59bb8d76dd68c Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Tue, 25 Jun 2019 14:41:44 -0400 Subject: [PATCH] happy merging back to legacy (#39166) --- .../components/scroll_to_top/index.test.tsx | 9 +- .../__snapshots__/index.test.tsx.snap | 14 +- .../components/url_state/index.test.tsx | 197 +++++++++---- .../public/components/url_state/index.tsx | 221 +-------------- .../url_state/index_mocked.test.tsx | 116 ++++---- .../components/url_state/test_dependencies.ts | 1 + .../siem/public/components/url_state/types.ts | 13 +- .../components/url_state/use_url_state.tsx | 246 ++++++++++++++++ .../containers/authentications/index.tsx | 2 + .../siem/public/containers/domains/index.tsx | 2 + .../siem/public/containers/events/index.tsx | 2 + .../siem/public/containers/hosts/index.tsx | 2 + .../containers/hosts/overview/index.tsx | 2 + .../public/containers/ip_overview/index.tsx | 3 +- .../public/containers/kpi_hosts/index.tsx | 3 +- .../public/containers/kpi_network/index.tsx | 3 +- .../public/containers/network_dns/index.tsx | 2 + .../containers/network_top_n_flow/index.tsx | 2 + .../siem/public/containers/query_template.tsx | 5 +- .../siem/public/containers/tls/index.tsx | 2 + .../containers/uncommon_processes/index.tsx | 2 + .../siem/public/containers/users/index.tsx | 2 + .../plugins/siem/public/mock/hook_wrapper.tsx | 2 +- .../legacy/plugins/siem/public/mock/index.ts | 2 + .../legacy/plugins/siem/public/mock/utils.ts | 12 + .../plugins/siem/public/pages/home/index.tsx | 2 - .../siem/public/pages/hosts/host_details.tsx | 223 ++++++++------- .../plugins/siem/public/pages/hosts/hosts.tsx | 267 ++++++++++-------- .../siem/public/pages/network/ip_details.tsx | 211 +++++++------- .../siem/public/pages/network/network.tsx | 142 +++++----- 30 files changed, 971 insertions(+), 741 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx create mode 100644 x-pack/legacy/plugins/siem/public/mock/utils.ts diff --git a/x-pack/legacy/plugins/siem/public/components/scroll_to_top/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/scroll_to_top/index.test.tsx index 3c0cb3096e915..66d7d2764511f 100644 --- a/x-pack/legacy/plugins/siem/public/components/scroll_to_top/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/scroll_to_top/index.test.tsx @@ -7,16 +7,9 @@ import { mount } from 'enzyme'; import * as React from 'react'; -import { HookWrapper } from '../../mock/hook_wrapper'; +import { globalNode, HookWrapper } from '../../mock'; import { scrollToTop } from '.'; -interface Global extends NodeJS.Global { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - window?: any; -} - -const globalNode: Global = global; - const spyScroll = jest.fn(); const spyScrollTo = jest.fn(); 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 e5ac56086af37..34b1175bcb2fc 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 @@ -353,7 +353,7 @@ exports[`UrlStateContainer mounts and renders 1`] = ` } } > - - - - + > + + + - + 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 fe0e3d077a3e1..2b3b1f463daa0 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 @@ -3,16 +3,24 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { mount, shallow } from 'enzyme'; +import { Location } from 'history'; +import { mount } from 'enzyme'; import toJson from 'enzyme-to-json'; import * as React from 'react'; import { Router } from 'react-router-dom'; import { MockedProvider } from 'react-apollo/test-utils'; import { StaticIndexPattern } from 'ui/index_patterns'; -import { UrlStateContainer, UrlStateContainerLifecycle } from './'; -import { UrlStateContainerPropTypes } from './types'; +import { + apolloClientObservable, + globalNode, + HookWrapper, + mockGlobalState, + TestProviders, +} from '../../mock'; +import { createStore, State } from '../../store'; + +import { UseUrlState } from './'; import { defaultProps, getMockPropsObj, @@ -20,8 +28,9 @@ import { serializedFilterQuery, testCases, } from './test_dependencies'; -import { apolloClientObservable, mockGlobalState, TestProviders } from '../../mock'; -import { createStore, State } from '../../store'; +import { UrlStateContainerPropTypes } from './types'; +import { useUrlStateHooks, initializeLocation } from './use_url_state'; +import { wait } from 'react-testing-library'; let mockProps: UrlStateContainerPropTypes; @@ -52,7 +61,9 @@ describe('UrlStateContainer', () => { - + + {({ isInitializing }) => {isInitializing}} + @@ -68,24 +79,27 @@ describe('UrlStateContainer', () => { test.each(testCases)('%o', (page, namespaceLower, namespaceUpper, examplePath, type) => { mockProps = getMockPropsObj({ page, examplePath, namespaceLower, type }) .relativeTimeSearch.undefinedQuery; - shallow(); - // @ts-ignore property mock does not exists - expect(defaultProps.setRelativeTimerange.mock.calls[0][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[1][0]).toEqual({ - from: 1558732849370, - fromStr: 'now-15m', - kind: 'relative', - to: 1558733749370, - toStr: 'now', - id: 'timeline', + mount( useUrlStateHooks(mockProps)} />); + + wait(() => { + // @ts-ignore property mock does not exists + expect(defaultProps.setRelativeTimerange.mock.calls[0][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[1][0]).toEqual({ + from: 1558732849370, + fromStr: 'now-15m', + kind: 'relative', + to: 1558733749370, + toStr: 'now', + id: 'timeline', + }); }); }); }); @@ -93,20 +107,23 @@ describe('UrlStateContainer', () => { test.each(testCases)('%o', (page, namespaceLower, namespaceUpper, examplePath, type) => { mockProps = getMockPropsObj({ page, examplePath, namespaceLower, type }) .absoluteTimeSearch.undefinedQuery; - shallow(); - // @ts-ignore property mock does not exists - expect(defaultProps.setAbsoluteTimerange.mock.calls[0][0]).toEqual({ - from: 1556736012685, - kind: 'absolute', - to: 1556822416082, - id: 'global', - }); - // @ts-ignore property mock does not exists - expect(defaultProps.setAbsoluteTimerange.mock.calls[1][0]).toEqual({ - from: 1556736012685, - kind: 'absolute', - to: 1556822416082, - id: 'timeline', + mount( useUrlStateHooks(mockProps)} />); + + wait(() => { + // @ts-ignore property mock does not exists + expect(defaultProps.setAbsoluteTimerange.mock.calls[0][0]).toEqual({ + from: 1556736012685, + kind: 'absolute', + to: 1556822416082, + id: 'global', + }); + // @ts-ignore property mock does not exists + expect(defaultProps.setAbsoluteTimerange.mock.calls[1][0]).toEqual({ + from: 1556736012685, + kind: 'absolute', + to: 1556822416082, + id: 'timeline', + }); }); }); }); @@ -114,13 +131,15 @@ describe('UrlStateContainer', () => { test.each(testCases)(' %o', (page, namespaceLower, namespaceUpper, examplePath, type) => { mockProps = getMockPropsObj({ page, examplePath, namespaceLower, type }) .relativeTimeSearch.undefinedQuery; - shallow(); - const functionName = - namespaceUpper === 'Network' ? defaultProps.setNetworkKql : defaultProps.setHostsKql; - // @ts-ignore property mock does not exists - expect(functionName.mock.calls[0][0]).toEqual({ - filterQuery: serializedFilterQuery, - [`${namespaceLower}Type`]: type, + mount( useUrlStateHooks(mockProps)} />); + wait(() => { + const functionName = + namespaceUpper === 'Network' ? defaultProps.setNetworkKql : defaultProps.setHostsKql; + // @ts-ignore property mock does not exists + expect(functionName.mock.calls[0][0]).toEqual({ + filterQuery: serializedFilterQuery, + [`${namespaceLower}Type`]: type, + }); }); }); }); @@ -130,11 +149,15 @@ describe('UrlStateContainer', () => { async (page, namespaceLower, namespaceUpper, examplePath, type) => { mockProps = getMockPropsObj({ page, examplePath, namespaceLower, type }) .oppositeQueryLocationSearch.undefinedQuery; - shallow(); - const functionName = - namespaceUpper === 'Network' ? defaultProps.setNetworkKql : defaultProps.setHostsKql; - // @ts-ignore property mock does not exists - expect(functionName.mock.calls.length).toEqual(0); + mount( useUrlStateHooks(mockProps)} />); + wait(() => { + const functionName = + namespaceUpper === 'Network' + ? defaultProps.setNetworkKql + : defaultProps.setHostsKql; + // @ts-ignore property mock does not exists + expect(functionName.mock.calls.length).toEqual(0); + }); } ); }); @@ -147,18 +170,76 @@ describe('UrlStateContainer', () => { async (page, namespaceLower, namespaceUpper, examplePath, type) => { mockProps = getMockPropsObj({ page, examplePath, namespaceLower, type }).noSearch .definedQuery; - shallow(); + mount( useUrlStateHooks(mockProps)} />); - // @ts-ignore property mock does not exists - expect(mockHistory.replace.mock.calls[0][0]).toEqual({ - hash: '', - pathname: examplePath, - search: `?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:${page},type:${type})`, - state: '', + wait(() => { + // @ts-ignore property mock does not exists + expect(mockHistory.replace.mock.calls[0][0]).toEqual({ + hash: '', + pathname: examplePath, + search: `?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:${page},type:${type})`, + state: '', + }); }); } ); }); }); }); + + describe('initializeLocation', () => { + test('basic functionality with no search', () => { + Object.defineProperty(globalNode.window, 'location', { + value: { + href: 'http://localhost:5601/app/siem#/hosts?_g=()', + }, + writable: true, + }); + const location: Location = { + hash: '', + pathname: '/hosts', + search: '?_g=()', + state: null, + }; + expect(initializeLocation(location).search).toEqual('?_g=()'); + }); + test('basic functionality with search', () => { + Object.defineProperty(globalNode.window, 'location', { + value: { + href: + "http://localhost:5601/app/siem#/hosts?_g=()&kqlQuery=(filterQuery:(expression:'%20host.name:%20%22beats-ci-immutable-ubuntu-1604-1560801145745062645%22%20and%20process.name:*',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)))", + }, + writable: true, + }); + const location: Location = { + hash: '', + pathname: '/hosts', + search: + "?_g=()&kqlQuery=(filterQuery:(expression:'%2Bhost.name:%2B%22beats-ci-immutable-ubuntu-1604-1560801145745062645%22%2Band%2Bprocess.name:*',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)))", + state: null, + }; + expect(initializeLocation(location).search).toEqual( + "?_g=()&kqlQuery=(filterQuery:(expression:'%20host.name:%20%22beats-ci-immutable-ubuntu-1604-1560801145745062645%22%20and%20process.name:*',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)))" + ); + }); + test('If hash and pathname do not match href from the hash, do not do anything', () => { + Object.defineProperty(globalNode.window, 'location', { + value: { + href: + "http://localhost:5601/app/siem#/hosts?_g=()&kqlQuery=(filterQuery:(expression:'%20host.name:%20%22beats-ci-immutable-ubuntu-1604-1560801145745062645%22%20and%20process.name:*',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)))", + }, + writable: true, + }); + const location: Location = { + hash: '', + pathname: '/network', + search: + "?_g=()&kqlQuery=(filterQuery:(expression:'%2Bhost.name:%2B%22beats-ci-immutable-ubuntu-1604-1560801145745062645%22%2Band%2Bprocess.name:*',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)))", + state: null, + }; + expect(initializeLocation(location).search).toEqual( + "?_g=()&kqlQuery=(filterQuery:(expression:'%2Bhost.name:%2B%22beats-ci-immutable-ubuntu-1604-1560801145745062645%22%2Band%2Bprocess.name:*',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1560714985274,fromStr:now-24h,kind:relative,to:1560801385274,toStr:now)))" + ); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx index f46d8bf8f8b3a..23b02bdb90220 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx @@ -4,14 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Location } from 'history'; -import { get, throttle } from 'lodash/fp'; import React from 'react'; import { compose } from 'redux'; import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; -import { hostsActions, inputsActions, networkActions } from '../../store/actions'; import { hostsModel, hostsSelectors, @@ -20,214 +17,16 @@ import { networkSelectors, State, } from '../../store'; -import { - AbsoluteTimeRange, - LinkTo, - RelativeTimeRange, - UrlInputsModel, -} from '../../store/inputs/model'; -import { convertKueryToElasticSearchQuery } from '../../lib/keury'; -import { URL_STATE_KEYS, LOCATION_MAPPED_TO_MODEL, LOCATION_KEYS } from './types'; -import { - decodeRisonUrlState, - getCurrentLocation, - getParamFromQueryString, - getQueryStringFromLocation, - isKqlForRoute, - replaceQueryStringInLocation, - replaceStateKeyInQueryString, -} from './helpers'; -import { - KeyUrlState, - KqlQuery, - KqlQueryObject, - LocationKeysType, - LocationTypes, - UrlStateContainerPropTypes, - UrlStateProps, -} from './types'; -import { CONSTANTS } from './constants'; -import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants'; -import { normalizeTimeRange } from './normalize_time_range'; - -export class UrlStateContainerLifecycle extends React.Component { - public render() { - return null; - } - - public componentDidUpdate({ - location: prevLocation, - urlState: prevUrlState, - }: UrlStateContainerPropTypes) { - const { location, urlState } = this.props; - - if (JSON.stringify(urlState) !== JSON.stringify(prevUrlState)) { - URL_STATE_KEYS.forEach((urlKey: KeyUrlState) => { - if ( - urlState[urlKey] && - JSON.stringify(urlState[urlKey]) !== JSON.stringify(prevUrlState[urlKey]) - ) { - if (urlKey === CONSTANTS.kqlQuery) { - LOCATION_KEYS.forEach((queryLocation: LocationKeysType) => { - if ( - !!urlState[CONSTANTS.kqlQuery][queryLocation] && - JSON.stringify(urlState[CONSTANTS.kqlQuery][queryLocation]) !== - JSON.stringify(prevUrlState[CONSTANTS.kqlQuery][queryLocation]) - ) { - this.replaceStateInLocation( - urlState[CONSTANTS.kqlQuery][queryLocation], - CONSTANTS.kqlQuery - ); - } - }); - } else { - this.replaceStateInLocation(urlState[urlKey], urlKey); - } - } - }); - } else if (location.pathname !== prevLocation.pathname) { - this.handleInitialize(location); - } - } - - public componentWillMount() { - const { location } = this.props; - this.handleInitialize(location); - } - - private replaceStateInLocation = throttle( - 1000, - (urlState: UrlInputsModel | KqlQuery, urlStateKey: string) => { - const { history, location } = this.props; - const newLocation = replaceQueryStringInLocation( - location, - replaceStateKeyInQueryString(urlStateKey, urlState)(getQueryStringFromLocation(location)) - ); - if (newLocation !== location) { - history.replace(newLocation); - } - } - ); +import { hostsActions, inputsActions, networkActions } from '../../store/actions'; - private setInitialStateFromUrl = ( - urlKey: KeyUrlState, - newUrlStateString: string, - location: Location - ) => { - if (urlKey === CONSTANTS.timerange) { - const timerangeStateData: UrlInputsModel = decodeRisonUrlState(newUrlStateString); - const globalId: InputsModelId = 'global'; - const globalLinkTo: LinkTo = { linkTo: get('global.linkTo', timerangeStateData) }; - const globalType: TimeRangeKinds = get('global.timerange.kind', timerangeStateData); - if (globalType) { - if (globalLinkTo.linkTo.length === 0) { - this.props.toggleTimelineLinkTo({ linkToId: 'global' }); - } - if (globalType === 'absolute') { - const absoluteRange = normalizeTimeRange( - get('global.timerange', timerangeStateData) - ); - this.props.setAbsoluteTimerange({ - ...absoluteRange, - id: globalId, - }); - } - if (globalType === 'relative') { - const relativeRange = normalizeTimeRange( - get('global.timerange', timerangeStateData) - ); - this.props.setRelativeTimerange({ - ...relativeRange, - id: globalId, - }); - } - } - const timelineId: InputsModelId = 'timeline'; - const timelineLinkTo: LinkTo = { linkTo: get('timeline.linkTo', timerangeStateData) }; - const timelineType: TimeRangeKinds = get('timeline.timerange.kind', timerangeStateData); - if (timelineType) { - if (timelineLinkTo.linkTo.length === 0) { - this.props.toggleTimelineLinkTo({ linkToId: 'timeline' }); - } - if (timelineType === 'absolute') { - const absoluteRange = normalizeTimeRange( - get('timeline.timerange', timerangeStateData) - ); - this.props.setAbsoluteTimerange({ - ...absoluteRange, - id: timelineId, - }); - } - if (timelineType === 'relative') { - const relativeRange = normalizeTimeRange( - get('timeline.timerange', timerangeStateData) - ); - this.props.setRelativeTimerange({ - ...relativeRange, - id: timelineId, - }); - } - } - } - if (urlKey === CONSTANTS.kqlQuery) { - const kqlQueryStateData: KqlQuery = decodeRisonUrlState(newUrlStateString); - if (isKqlForRoute(location.pathname, kqlQueryStateData)) { - const filterQuery = { - kuery: kqlQueryStateData.filterQuery, - serializedQuery: convertKueryToElasticSearchQuery( - kqlQueryStateData.filterQuery ? kqlQueryStateData.filterQuery.expression : '', - this.props.indexPattern - ), - }; - if ( - kqlQueryStateData.queryLocation === CONSTANTS.hostsPage || - kqlQueryStateData.queryLocation === CONSTANTS.hostsDetails - ) { - const hostsType = LOCATION_MAPPED_TO_MODEL[kqlQueryStateData.queryLocation]; - this.props.setHostsKql({ - filterQuery, - hostsType, - }); - } - if ( - kqlQueryStateData.queryLocation === CONSTANTS.networkPage || - kqlQueryStateData.queryLocation === CONSTANTS.networkDetails - ) { - const networkType = LOCATION_MAPPED_TO_MODEL[kqlQueryStateData.queryLocation]; - this.props.setNetworkKql({ - filterQuery, - networkType, - }); - } - } - } - }; +import { CONSTANTS } from './constants'; +import { UrlStateContainerPropTypes, UrlStateProps, KqlQueryObject } from './types'; +import { useUrlStateHooks } from './use_url_state'; - private handleInitialize = (location: Location) => { - URL_STATE_KEYS.forEach((urlKey: KeyUrlState) => { - const newUrlStateString = getParamFromQueryString( - getQueryStringFromLocation(location), - urlKey - ); - if (newUrlStateString) { - this.setInitialStateFromUrl(urlKey, newUrlStateString, location); - } else { - if (urlKey === CONSTANTS.timerange) { - this.replaceStateInLocation(this.props.urlState[urlKey], urlKey); - } - if (urlKey === CONSTANTS.kqlQuery) { - const currentLocation: LocationTypes = getCurrentLocation(location.pathname); - if (currentLocation !== null) { - this.replaceStateInLocation( - this.props.urlState[CONSTANTS.kqlQuery][currentLocation], - urlKey - ); - } - } - } - }); - }; -} +export const UrlStateContainer = (props: UrlStateContainerPropTypes) => { + const { isInitializing } = useUrlStateHooks(props); + return props.children({ isInitializing }); +}; const makeMapStateToProps = () => { const getInputsSelector = inputsSelectors.inputsSelector(); @@ -281,7 +80,7 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const UrlStateContainer = compose>( +export const UseUrlState = compose>( withRouter, connect( makeMapStateToProps, @@ -293,4 +92,4 @@ export const UrlStateContainer = compose>( toggleTimelineLinkTo: inputsActions.toggleTimelineLinkTo, } ) -)(UrlStateContainerLifecycle); +)(UrlStateContainer); 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 309d1b39f3486..04d23835aeb96 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 @@ -4,14 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { shallow } from 'enzyme'; +import { mount } from 'enzyme'; import { throttle } from 'lodash/fp'; import * as React from 'react'; -import { UrlStateContainerLifecycle } from './'; -import { getMockPropsObj, mockHistory, filterQuery, testCases } from './test_dependencies'; + +import { HookWrapper } from '../../mock/hook_wrapper'; import { hostsModel, networkModel } from '../../store'; -import { UrlStateContainerPropTypes } from './types'; + import { CONSTANTS } from './constants'; +import { getMockPropsObj, mockHistory, filterQuery, testCases } from './test_dependencies'; +import { UrlStateContainerPropTypes } from './types'; +import { useUrlStateHooks } from './use_url_state'; +import { wait } from 'react-testing-library'; jest.mock('lodash/fp'); @@ -34,7 +38,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => namespaceLower: 'network', type: networkModel.NetworkType.page, }).noSearch.definedQuery; - const wrapper = shallow(); + const wrapper = mount( useUrlStateHooks(mockProps)} />); const newUrlState = { ...mockProps.urlState, @@ -61,17 +65,19 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => }, }, }; - - wrapper.setProps({ urlState: newUrlState }); - wrapper.update(); - expect(mockHistory.replace.mock.calls[2][0]).toStrictEqual({ - hash: '', - pathname: '/network', - search: - '?_g=()&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: '', + wait(() => { + wrapper.setProps({ urlState: newUrlState }); + wrapper.update(); + expect(mockHistory.replace.mock.calls[2][0]).toStrictEqual({ + hash: '', + pathname: '/network', + search: + '?_g=()&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: '', + }); }); }); + test('kql query redux state updates the url', () => { mockProps = getMockPropsObj({ page: CONSTANTS.networkPage, @@ -79,7 +85,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => namespaceLower: 'network', type: networkModel.NetworkType.page, }).noSearch.definedQuery; - const wrapper = shallow(); + const wrapper = mount( useUrlStateHooks(mockProps)} />); const newUrlState = { [CONSTANTS.kqlQuery]: { @@ -91,15 +97,17 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => }, }; - wrapper.setProps({ urlState: newUrlState }); - wrapper.update(); + wait(() => { + wrapper.setProps({ urlState: newUrlState }); + wrapper.update(); - expect(mockHistory.replace.mock.calls[0][0]).toStrictEqual({ - hash: '', - pathname: '/network', - search: - "?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:network.page,type:page)", - state: '', + expect(mockHistory.replace.mock.calls[0][0]).toStrictEqual({ + hash: '', + pathname: '/network', + search: + "?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:network.page,type:page)", + state: '', + }); }); }); }); @@ -110,21 +118,23 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => test.each(testCases)('%o', (page, namespaceLower, namespaceUpper, examplePath, type) => { mockProps = getMockPropsObj({ page, examplePath, namespaceLower, type }).noSearch .undefinedQuery; - shallow(); + mount( useUrlStateHooks(mockProps)} />); - expect(mockHistory.replace.mock.calls[0][0]).toEqual({ - hash: '', - pathname: examplePath, - search: `?_g=()&kqlQuery=(filterQuery:!n,queryLocation:${page},type:${type})`, - state: '', - }); + wait(() => { + expect(mockHistory.replace.mock.calls[0][0]).toEqual({ + hash: '', + pathname: examplePath, + search: `?_g=()&kqlQuery=(filterQuery:!n,queryLocation:${page},type:${type})`, + state: '', + }); - expect(mockHistory.replace.mock.calls[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: '', + expect(mockHistory.replace.mock.calls[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: '', + }); }); }); }); @@ -142,24 +152,26 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => namespaceLower: 'network', type: networkModel.NetworkType.page, }).noSearch.undefinedQuery; - const wrapper = shallow(); + const wrapper = mount( useUrlStateHooks(mockProps)} />); - // @ts-ignore ignore staticContext warning - wrapper.setProps(updatedProps); - wrapper.update(); - // sets new kqlQuery - expect(mockHistory.replace.mock.calls[2][0]).toEqual({ - hash: '', - pathname: '/network', - search: `?_g=()&kqlQuery=(filterQuery:!n,queryLocation:${CONSTANTS.networkPage},type:${ - networkModel.NetworkType.page - })`, - state: '', + wait(() => { + // @ts-ignore ignore staticContext warning + wrapper.setProps(updatedProps); + wrapper.update(); + // sets new kqlQuery + expect(mockHistory.replace.mock.calls[2][0]).toEqual({ + hash: '', + pathname: '/network', + search: `?_g=()&kqlQuery=(filterQuery:!n,queryLocation:${CONSTANTS.networkPage},type:${ + networkModel.NetworkType.page + })`, + state: '', + }); + // sets same timeline + expect(mockHistory.replace.mock.calls[3][0].search).toEqual( + mockHistory.replace.mock.calls[1][0].search + ); }); - // sets same timeline - expect(mockHistory.replace.mock.calls[3][0].search).toEqual( - mockHistory.replace.mock.calls[1][0].search - ); }); }); }); 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 39a69d7ada7f6..86c3f2c422d64 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 @@ -47,6 +47,7 @@ export const mockHistory = { }; export const defaultProps: UrlStateContainerPropTypes = { + children: jest.fn(), match: { isExact: true, params: '', diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts index 520d263a2cf2e..b3ee1758fb775 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts @@ -4,12 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { StaticIndexPattern } from 'ui/index_patterns'; -import { ActionCreator } from 'typescript-fsa'; +import { Location } from 'history'; import { RouteComponentProps } from 'react-router'; +import { ActionCreator } from 'typescript-fsa'; +import { StaticIndexPattern } from 'ui/index_patterns'; + import { hostsModel, KueryFilterQuery, networkModel, SerializedFilterQuery } from '../../store'; import { UrlInputsModel } from '../../store/inputs/model'; import { InputsModelId } from '../../store/inputs/constants'; + import { CONSTANTS } from './constants'; export const LOCATION_KEYS: LocationKeysType[] = [ @@ -76,6 +79,7 @@ export interface UrlState { export type KeyUrlState = keyof UrlState; export interface UrlStateProps { + children: (args: { isInitializing: boolean }) => React.ReactNode; indexPattern: StaticIndexPattern; mapToUrlState?: (value: string) => UrlState; onChange?: (urlState: UrlState, previousUrlState: UrlState) => void; @@ -118,3 +122,8 @@ export type UrlStateContainerPropTypes = RouteComponentProps & UrlStateStateToPropsType & UrlStateDispatchToPropsType & UrlStateProps; + +export interface PreviousLocationUrlState { + location: Location; + urlState: UrlState; +} 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 new file mode 100644 index 0000000000000..c70dd1408d74b --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx @@ -0,0 +1,246 @@ +/* + * 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 { throttle, get, isEqual } from 'lodash/fp'; +import { useState, useEffect, useRef } from 'react'; + +import { convertKueryToElasticSearchQuery } from '../../lib/keury'; +import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants'; +import { + AbsoluteTimeRange, + LinkTo, + RelativeTimeRange, + UrlInputsModel, +} from '../../store/inputs/model'; + +import { CONSTANTS } from './constants'; +import { + replaceQueryStringInLocation, + getQueryStringFromLocation, + replaceStateKeyInQueryString, + getParamFromQueryString, + getCurrentLocation, + decodeRisonUrlState, + isKqlForRoute, +} from './helpers'; +import { normalizeTimeRange } from './normalize_time_range'; +import { + UrlStateContainerPropTypes, + PreviousLocationUrlState, + URL_STATE_KEYS, + KeyUrlState, + LocationKeysType, + LOCATION_KEYS, + KqlQuery, + LocationTypes, + LOCATION_MAPPED_TO_MODEL, +} from './types'; + +function usePrevious(value: PreviousLocationUrlState) { + const ref = useRef(value); + useEffect(() => { + ref.current = value; + }); + return ref.current; +} + +export const useUrlStateHooks = ({ + location, + indexPattern, + history, + setAbsoluteTimerange, + setHostsKql, + setNetworkKql, + setRelativeTimerange, + toggleTimelineLinkTo, + urlState, +}: UrlStateContainerPropTypes) => { + const [isInitializing, setIsInitializing] = useState(true); + const prevProps = usePrevious({ location, urlState }); + + const replaceStateInLocation = throttle( + 1000, + (urlStateToReplace: UrlInputsModel | KqlQuery, urlStateKey: string) => { + const newLocation = replaceQueryStringInLocation( + location, + replaceStateKeyInQueryString(urlStateKey, urlStateToReplace)( + getQueryStringFromLocation(location) + ) + ); + if (newLocation !== location) { + history.replace(newLocation); + } + } + ); + + const handleInitialize = (initLocation: Location) => { + URL_STATE_KEYS.forEach((urlKey: KeyUrlState) => { + const newUrlStateString = getParamFromQueryString( + getQueryStringFromLocation(location), + urlKey + ); + if (newUrlStateString) { + setInitialStateFromUrl(urlKey, newUrlStateString, location); + } else { + if (urlKey === CONSTANTS.timerange) { + replaceStateInLocation(urlState[urlKey], urlKey); + } + if (urlKey === CONSTANTS.kqlQuery) { + const currentLocation: LocationTypes = getCurrentLocation(location.pathname); + if (currentLocation !== null) { + replaceStateInLocation(urlState[CONSTANTS.kqlQuery][currentLocation], urlKey); + } + } + } + }); + }; + + const setInitialStateFromUrl = ( + urlKey: KeyUrlState, + newUrlStateString: string, + newLocation: Location + ) => { + if (urlKey === CONSTANTS.timerange) { + const timerangeStateData: UrlInputsModel = decodeRisonUrlState(newUrlStateString); + const globalId: InputsModelId = 'global'; + const globalLinkTo: LinkTo = { linkTo: get('global.linkTo', timerangeStateData) }; + const globalType: TimeRangeKinds = get('global.timerange.kind', timerangeStateData); + if (globalType) { + if (globalLinkTo.linkTo.length === 0) { + toggleTimelineLinkTo({ linkToId: 'global' }); + } + if (globalType === 'absolute') { + const absoluteRange = normalizeTimeRange( + get('global.timerange', timerangeStateData) + ); + setAbsoluteTimerange({ + ...absoluteRange, + id: globalId, + }); + } + if (globalType === 'relative') { + const relativeRange = normalizeTimeRange( + get('global.timerange', timerangeStateData) + ); + setRelativeTimerange({ + ...relativeRange, + id: globalId, + }); + } + } + const timelineId: InputsModelId = 'timeline'; + const timelineLinkTo: LinkTo = { linkTo: get('timeline.linkTo', timerangeStateData) }; + const timelineType: TimeRangeKinds = get('timeline.timerange.kind', timerangeStateData); + if (timelineType) { + if (timelineLinkTo.linkTo.length === 0) { + toggleTimelineLinkTo({ linkToId: 'timeline' }); + } + if (timelineType === 'absolute') { + const absoluteRange = normalizeTimeRange( + get('timeline.timerange', timerangeStateData) + ); + setAbsoluteTimerange({ + ...absoluteRange, + id: timelineId, + }); + } + if (timelineType === 'relative') { + const relativeRange = normalizeTimeRange( + get('timeline.timerange', timerangeStateData) + ); + setRelativeTimerange({ + ...relativeRange, + id: timelineId, + }); + } + } + } + if (urlKey === CONSTANTS.kqlQuery) { + const kqlQueryStateData: KqlQuery = decodeRisonUrlState(newUrlStateString); + if (isKqlForRoute(location.pathname, kqlQueryStateData)) { + const filterQuery = { + kuery: kqlQueryStateData.filterQuery, + serializedQuery: convertKueryToElasticSearchQuery( + kqlQueryStateData.filterQuery ? kqlQueryStateData.filterQuery.expression : '', + indexPattern + ), + }; + if ( + kqlQueryStateData.queryLocation === CONSTANTS.hostsPage || + kqlQueryStateData.queryLocation === CONSTANTS.hostsDetails + ) { + const hostsType = LOCATION_MAPPED_TO_MODEL[kqlQueryStateData.queryLocation]; + setHostsKql({ + filterQuery, + hostsType, + }); + } + if ( + kqlQueryStateData.queryLocation === CONSTANTS.networkPage || + kqlQueryStateData.queryLocation === CONSTANTS.networkDetails + ) { + const networkType = LOCATION_MAPPED_TO_MODEL[kqlQueryStateData.queryLocation]; + setNetworkKql({ + filterQuery, + networkType, + }); + } + } + } + }; + + useEffect(() => { + if (isInitializing) { + setIsInitializing(false); + handleInitialize(initializeLocation(location)); + } else if (!isEqual(urlState, prevProps.urlState)) { + URL_STATE_KEYS.forEach((urlKey: KeyUrlState) => { + if (urlState[urlKey] && !isEqual(urlState[urlKey], prevProps.urlState[urlKey])) { + if (urlKey === CONSTANTS.kqlQuery) { + LOCATION_KEYS.forEach((queryLocation: LocationKeysType) => { + if ( + !!urlState[CONSTANTS.kqlQuery][queryLocation] && + !isEqual( + urlState[CONSTANTS.kqlQuery][queryLocation], + prevProps.urlState[CONSTANTS.kqlQuery][queryLocation] + ) + ) { + replaceStateInLocation( + urlState[CONSTANTS.kqlQuery][queryLocation], + CONSTANTS.kqlQuery + ); + } + }); + } else { + replaceStateInLocation(urlState[urlKey], urlKey); + } + } + }); + } else if (location.pathname !== prevProps.location.pathname) { + handleInitialize(location); + } + }); + + return { isInitializing }; +}; + +/* + * Why are we doing that, it is because angular-ui router is encoding the `+` back to `2%B` after + * that react router is getting the data with the `+` and convert to `2%B` + * so we need to get back the value from the window location at initialization to avoid + * to bring back the `+` in the kql + */ +export const initializeLocation = (location: Location): Location => { + const substringIndex = + window.location.href.indexOf(`#${location.pathname}`) >= 0 + ? window.location.href.indexOf(`#${location.pathname}`) + location.pathname.length + 1 + : -1; + if (substringIndex >= 0) { + location.search = window.location.href.substring(substringIndex); + } + return location; +}; diff --git a/x-pack/legacy/plugins/siem/public/containers/authentications/index.tsx b/x-pack/legacy/plugins/siem/public/containers/authentications/index.tsx index e5587569f9f17..9545913af966c 100644 --- a/x-pack/legacy/plugins/siem/public/containers/authentications/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/authentications/index.tsx @@ -49,6 +49,7 @@ class AuthenticationsComponentQuery extends QueryTemplate< id = 'authenticationQuery', children, filterQuery, + skip, sourceId, startDate, endDate, @@ -59,6 +60,7 @@ class AuthenticationsComponentQuery extends QueryTemplate< query={authenticationsQuery} fetchPolicy={getDefaultFetchPolicy()} notifyOnNetworkStatusChange + skip={skip} variables={{ sourceId, timerange: { diff --git a/x-pack/legacy/plugins/siem/public/containers/domains/index.tsx b/x-pack/legacy/plugins/siem/public/containers/domains/index.tsx index 7d7e522df5f64..3acc7bd9f1354 100644 --- a/x-pack/legacy/plugins/siem/public/containers/domains/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/domains/index.tsx @@ -62,6 +62,7 @@ class DomainsComponentQuery extends QueryTemplate< domainsSortField, filterQuery, ip, + skip, sourceId, startDate, endDate, @@ -74,6 +75,7 @@ class DomainsComponentQuery extends QueryTemplate< query={domainsQuery} fetchPolicy="cache-and-network" notifyOnNetworkStatusChange + skip={skip} variables={{ sourceId, timerange: { diff --git a/x-pack/legacy/plugins/siem/public/containers/events/index.tsx b/x-pack/legacy/plugins/siem/public/containers/events/index.tsx index 83d7498422873..9b2373fc6bcd7 100644 --- a/x-pack/legacy/plugins/siem/public/containers/events/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/events/index.tsx @@ -50,6 +50,7 @@ class EventsComponentQuery extends QueryTemplate< filterQuery, id = 'eventsQuery', limit, + skip, sourceId, startDate, endDate, @@ -59,6 +60,7 @@ class EventsComponentQuery extends QueryTemplate< query={eventsQuery} fetchPolicy={getDefaultFetchPolicy()} notifyOnNetworkStatusChange + skip={skip} variables={{ filterQuery: createFilter(filterQuery), sourceId, diff --git a/x-pack/legacy/plugins/siem/public/containers/hosts/index.tsx b/x-pack/legacy/plugins/siem/public/containers/hosts/index.tsx index 48ae965e404f1..ce38eeb15a375 100644 --- a/x-pack/legacy/plugins/siem/public/containers/hosts/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/hosts/index.tsx @@ -78,6 +78,7 @@ class HostsComponentQuery extends QueryTemplate< endDate, limit, startDate, + skip, sourceId, sortField, } = this.props; @@ -105,6 +106,7 @@ class HostsComponentQuery extends QueryTemplate< query={HostsTableQuery} fetchPolicy="cache-first" notifyOnNetworkStatusChange + skip={skip} variables={variables} > {({ data, loading, fetchMore, refetch }) => { diff --git a/x-pack/legacy/plugins/siem/public/containers/hosts/overview/index.tsx b/x-pack/legacy/plugins/siem/public/containers/hosts/overview/index.tsx index b78ea70088002..04834075d81c8 100644 --- a/x-pack/legacy/plugins/siem/public/containers/hosts/overview/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/hosts/overview/index.tsx @@ -42,6 +42,7 @@ export class HostOverviewByNameQuery extends QueryTemplate< id = 'hostOverviewQuery', children, hostName, + skip, sourceId, startDate, endDate, @@ -51,6 +52,7 @@ export class HostOverviewByNameQuery extends QueryTemplate< query={HostOverviewQuery} fetchPolicy={getDefaultFetchPolicy()} notifyOnNetworkStatusChange + skip={skip} variables={{ sourceId, hostName, diff --git a/x-pack/legacy/plugins/siem/public/containers/ip_overview/index.tsx b/x-pack/legacy/plugins/siem/public/containers/ip_overview/index.tsx index fe500482fac54..3f6d2df7d709e 100644 --- a/x-pack/legacy/plugins/siem/public/containers/ip_overview/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/ip_overview/index.tsx @@ -31,11 +31,12 @@ export interface IpOverviewProps extends QueryTemplateProps { } export const IpOverviewQuery = pure( - ({ id = 'ipOverviewQuery', children, filterQuery, sourceId, ip }) => ( + ({ id = 'ipOverviewQuery', children, filterQuery, skip, sourceId, ip }) => ( query={ipOverviewQuery} fetchPolicy="cache-and-network" notifyOnNetworkStatusChange + skip={skip} variables={{ sourceId, filterQuery: createFilter(filterQuery), diff --git a/x-pack/legacy/plugins/siem/public/containers/kpi_hosts/index.tsx b/x-pack/legacy/plugins/siem/public/containers/kpi_hosts/index.tsx index afb5e90b93b76..a8d880ede7bcc 100644 --- a/x-pack/legacy/plugins/siem/public/containers/kpi_hosts/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/kpi_hosts/index.tsx @@ -29,11 +29,12 @@ export interface KpiHostsProps extends QueryTemplateProps { } export const KpiHostsQuery = React.memo( - ({ id = 'kpiHostsQuery', children, filterQuery, sourceId, startDate, endDate }) => ( + ({ id = 'kpiHostsQuery', children, filterQuery, skip, sourceId, startDate, endDate }) => ( query={kpiHostsQuery} fetchPolicy="cache-and-network" notifyOnNetworkStatusChange + skip={skip} variables={{ sourceId, timerange: { diff --git a/x-pack/legacy/plugins/siem/public/containers/kpi_network/index.tsx b/x-pack/legacy/plugins/siem/public/containers/kpi_network/index.tsx index 8e3175d1ddc66..277ce24dcc270 100644 --- a/x-pack/legacy/plugins/siem/public/containers/kpi_network/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/kpi_network/index.tsx @@ -29,11 +29,12 @@ export interface KpiNetworkProps extends QueryTemplateProps { } export const KpiNetworkQuery = React.memo( - ({ id = 'kpiNetworkQuery', children, filterQuery, sourceId, startDate, endDate }) => ( + ({ id = 'kpiNetworkQuery', children, filterQuery, skip, sourceId, startDate, endDate }) => ( query={kpiNetworkQuery} fetchPolicy="cache-and-network" notifyOnNetworkStatusChange + skip={skip} variables={{ sourceId, timerange: { diff --git a/x-pack/legacy/plugins/siem/public/containers/network_dns/index.tsx b/x-pack/legacy/plugins/siem/public/containers/network_dns/index.tsx index ce5e34dfcd871..f4e6c3550f1fd 100644 --- a/x-pack/legacy/plugins/siem/public/containers/network_dns/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/network_dns/index.tsx @@ -58,6 +58,7 @@ class NetworkDnsComponentQuery extends QueryTemplate< dnsSortField, filterQuery, isPtrIncluded, + skip, sourceId, startDate, endDate, @@ -68,6 +69,7 @@ class NetworkDnsComponentQuery extends QueryTemplate< query={networkDnsQuery} fetchPolicy="cache-and-network" notifyOnNetworkStatusChange + skip={skip} variables={{ sourceId, timerange: { diff --git a/x-pack/legacy/plugins/siem/public/containers/network_top_n_flow/index.tsx b/x-pack/legacy/plugins/siem/public/containers/network_top_n_flow/index.tsx index 1975cc64b91f6..8c32d21c86c11 100644 --- a/x-pack/legacy/plugins/siem/public/containers/network_top_n_flow/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/network_top_n_flow/index.tsx @@ -59,6 +59,7 @@ class NetworkTopNFlowComponentQuery extends QueryTemplate< id = 'networkTopNFlowQuery', children, filterQuery, + skip, sourceId, startDate, endDate, @@ -72,6 +73,7 @@ class NetworkTopNFlowComponentQuery extends QueryTemplate< query={networkTopNFlowQuery} fetchPolicy="cache-and-network" notifyOnNetworkStatusChange + skip={skip} variables={{ sourceId, timerange: { diff --git a/x-pack/legacy/plugins/siem/public/containers/query_template.tsx b/x-pack/legacy/plugins/siem/public/containers/query_template.tsx index 3f55b26529fb3..57af9114b8ac7 100644 --- a/x-pack/legacy/plugins/siem/public/containers/query_template.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/query_template.tsx @@ -12,10 +12,11 @@ import { ESQuery } from '../../common/typed_json'; export interface QueryTemplateProps { id?: string; - sourceId: string; - startDate?: number; endDate?: number; filterQuery?: ESQuery | string; + skip?: boolean; + sourceId: string; + startDate?: number; } // eslint-disable-next-line @typescript-eslint/no-explicit-any type FetchMoreOptionsArgs = FetchMoreQueryOptions & diff --git a/x-pack/legacy/plugins/siem/public/containers/tls/index.tsx b/x-pack/legacy/plugins/siem/public/containers/tls/index.tsx index a09d8d7b5809b..8aa710ed5b353 100644 --- a/x-pack/legacy/plugins/siem/public/containers/tls/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/tls/index.tsx @@ -50,6 +50,7 @@ class TlsComponentQuery extends QueryTemplate { const myHook = props.hook ? props.hook() : null; - return
{myHook}
; + return
{JSON.stringify(myHook)}
; }; diff --git a/x-pack/legacy/plugins/siem/public/mock/index.ts b/x-pack/legacy/plugins/siem/public/mock/index.ts index fe5ded2a26fca..620e266618c5c 100644 --- a/x-pack/legacy/plugins/siem/public/mock/index.ts +++ b/x-pack/legacy/plugins/siem/public/mock/index.ts @@ -6,9 +6,11 @@ export * from './global_state'; export * from './header'; +export * from './hook_wrapper'; export * from './index_pattern'; export * from './kibana_config'; export * from './mock_timeline_data'; export * from './mock_detail_item'; export * from './netflow'; export * from './test_providers'; +export * from './utils'; diff --git a/x-pack/legacy/plugins/siem/public/mock/utils.ts b/x-pack/legacy/plugins/siem/public/mock/utils.ts new file mode 100644 index 0000000000000..6a372f163a648 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/mock/utils.ts @@ -0,0 +1,12 @@ +/* + * 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. + */ + +interface Global extends NodeJS.Global { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + window?: any; +} + +export const globalNode: Global = global; 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 8e55b2c2ed7c1..4be974622b36f 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx @@ -26,7 +26,6 @@ import { NetworkContainer } from '../network'; import { Overview } from '../overview'; import { Timelines } from '../timelines'; import { WithSource } from '../../containers/source'; -import { UrlStateContainer } from '../../components/url_state'; const WrappedByAutoSizer = styled.div` height: 100%; @@ -99,7 +98,6 @@ export const HomePage = pure(() => ( - diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/host_details.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/host_details.tsx index cb8e6df95d07d..937aeda5d35b1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/host_details.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/host_details.tsx @@ -22,6 +22,7 @@ import { EventsTable, UncommonProcessTable } from '../../components/page/hosts'; import { AuthenticationTable } from '../../components/page/hosts/authentications_table'; import { HostOverview } from '../../components/page/hosts/host_overview'; import { manageQuery } from '../../components/page/manage_query'; +import { UseUrlState } from '../../components/url_state'; import { AuthenticationsQuery } from '../../containers/authentications'; import { EventsQuery } from '../../containers/events'; import { GlobalTime } from '../../containers/global_time'; @@ -73,116 +74,124 @@ const HostDetailsComponent = pure( {({ to, from, setQuery }) => ( - <> - - {({ hostOverview, loading, id, refetch }) => ( - - )} - - - - - - {({ - authentications, - totalCount, - loading, - pageInfo, - loadMore, - id, - refetch, - }) => ( - + {({ isInitializing }) => ( + <> + + {({ hostOverview, loading, id, refetch }) => ( + + )} + + + + + - )} - - - - - - {({ - uncommonProcesses, - totalCount, - loading, - pageInfo, - loadMore, - id, - refetch, - }) => ( - + {({ + authentications, + totalCount, + loading, + pageInfo, + loadMore, + id, + refetch, + }) => ( + + )} + + + + + - )} - - - - - - {({ events, loading, id, refetch, totalCount, pageInfo, loadMore }) => ( - + {({ + uncommonProcesses, + totalCount, + loading, + pageInfo, + loadMore, + id, + refetch, + }) => ( + + )} + + + + + - )} - - + > + {({ events, loading, id, refetch, totalCount, pageInfo, loadMore }) => ( + + )} + + + )} + )} diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx index fdd68e5f65fd0..76b937983dd11 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -22,6 +22,7 @@ import { } from '../../components/page/hosts'; import { AuthenticationTable } from '../../components/page/hosts/authentications_table'; import { manageQuery } from '../../components/page/manage_query'; +import { UseUrlState } from '../../components/url_state'; import { AuthenticationsQuery } from '../../containers/authentications'; import { EventsQuery } from '../../containers/events'; import { GlobalTime } from '../../containers/global_time'; @@ -63,141 +64,157 @@ const HostsComponent = pure(({ filterQuery }) => ( {({ to, from, setQuery }) => ( - <> - - {({ kpiHosts, loading, id, refetch }) => ( - - )} - - - - - - {({ hosts, totalCount, loading, pageInfo, loadMore, id, refetch }) => ( - + {({ isInitializing }) => ( + <> + + {({ kpiHosts, loading, id, refetch }) => ( + + )} + + + + + - )} - - - - - - {({ authentications, totalCount, loading, pageInfo, loadMore, id, refetch }) => ( - + {({ hosts, totalCount, loading, pageInfo, loadMore, id, refetch }) => ( + + )} + + + + + - )} - - - - - - {({ - uncommonProcesses, - totalCount, - loading, - pageInfo, - loadMore, - id, - refetch, - }) => ( - + {({ + authentications, + totalCount, + loading, + pageInfo, + loadMore, + id, + refetch, + }) => ( + + )} + + + + + - )} - - - - - - {({ events, loading, id, refetch, totalCount, pageInfo, loadMore }) => ( - + {({ + uncommonProcesses, + totalCount, + loading, + pageInfo, + loadMore, + id, + refetch, + }) => ( + + )} + + + + + - )} - - + > + {({ events, loading, id, refetch, totalCount, pageInfo, loadMore }) => ( + + )} + + + )} + )} ) : ( <> - ) 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 398cf9fb99f75..aefbee980eabb 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 @@ -20,21 +20,22 @@ import { manageQuery } from '../../components/page/manage_query'; import { DomainsTable } from '../../components/page/network/domains_table'; import { FlowTargetSelectConnected } from '../../components/page/network/flow_target_select_connected'; import { IpOverview } from '../../components/page/network/ip_overview'; +import { UsersTable } from '../../components/page/network/users_table'; +import { TlsTable } from '../../components/page/network/tls_table'; +import { UseUrlState } from '../../components/url_state'; import { DomainsQuery } from '../../containers/domains'; import { GlobalTime } from '../../containers/global_time'; import { IpOverviewQuery } from '../../containers/ip_overview'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; +import { TlsQuery } from '../../containers/tls'; +import { UsersQuery } from '../../containers/users'; import { FlowTarget, LastEventIndexKey } from '../../graphql/types'; import { decodeIpv6 } from '../../lib/helpers'; import { networkModel, networkSelectors, State } from '../../store'; -import { TlsTable } from '../../components/page/network/tls_table'; import { NetworkKql } from './kql'; import { NetworkEmptyPage } from './network_empty_page'; import * as i18n from './translations'; -import { TlsQuery } from '../../containers/tls'; -import { UsersTable } from '../../components/page/network/users_table'; -import { UsersQuery } from '../../containers/users'; const DomainsTableManage = manageQuery(DomainsTable); const TlsTableManage = manageQuery(TlsTable); @@ -75,109 +76,117 @@ export const IPDetailsComponent = pure( {({ to, from, setQuery }) => ( - <> - - {({ ipOverviewData, loading }) => ( - + {({ isInitializing }) => ( + <> + + {({ ipOverviewData, loading }) => ( + + )} + + + + + - )} - - - - - - {({ id, domains, totalCount, pageInfo, loading, loadMore, refetch }) => ( - - )} - - - - - - {({ id, users, totalCount, pageInfo, loading, loadMore, refetch }) => ( - + {({ id, domains, totalCount, pageInfo, loading, loadMore, refetch }) => ( + + )} + + + + + - )} - - - - - - {({ id, tls, totalCount, pageInfo, loading, loadMore, refetch }) => ( - + {({ id, users, totalCount, pageInfo, loading, loadMore, refetch }) => ( + + )} + + + + + - )} - - + > + {({ id, tls, totalCount, pageInfo, loading, loadMore, refetch }) => ( + + )} + + + )} + )} diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx index 6f6e889215045..71bf3548e8759 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx @@ -17,6 +17,7 @@ import { LastEventTime } from '../../components/last_event_time'; import { manageQuery } from '../../components/page/manage_query'; import { KpiNetworkComponent, NetworkTopNFlowTable } from '../../components/page/network'; import { NetworkDnsTable } from '../../components/page/network/network_dns_table'; +import { UseUrlState } from '../../components/url_state'; import { GlobalTime } from '../../containers/global_time'; import { KpiNetworkQuery } from '../../containers/kpi_network'; import { NetworkDnsQuery } from '../../containers/network_dns'; @@ -53,75 +54,90 @@ const NetworkComponent = pure(({ filterQuery }) => ( {({ to, from, setQuery }) => ( - <> - - {({ kpiNetwork, loading, id, refetch }) => ( - - )} - + + {({ isInitializing }) => ( + <> + + {({ kpiNetwork, loading, id, refetch }) => ( + + )} + - + - - {({ totalCount, loading, networkTopNFlow, pageInfo, loadMore, id, refetch }) => ( - - )} - + > + {({ + totalCount, + loading, + networkTopNFlow, + pageInfo, + loadMore, + id, + refetch, + }) => ( + + )} + - + - - {({ totalCount, loading, networkDns, pageInfo, loadMore, id, refetch }) => ( - - )} - - + > + {({ totalCount, loading, networkDns, pageInfo, loadMore, id, refetch }) => ( + + )} + + + )} + )}