From 4f4cd8b1d6b06baf087dd28a48bca3580474d878 Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Mon, 28 Oct 2019 17:53:44 +0100 Subject: [PATCH] [SIEM] Refactor hosts routing (#47459) (#49473) --- .../details/__snapshots__/body.test.tsx.snap | 15 -- .../public/pages/hosts/details/body.test.tsx | 99 -------- .../siem/public/pages/hosts/details/body.tsx | 106 --------- .../pages/hosts/details/details_tabs.test.tsx | 108 +++++++++ .../pages/hosts/details/details_tabs.tsx | 100 +++++++++ .../siem/public/pages/hosts/details/index.tsx | 204 +++++++++-------- .../siem/public/pages/hosts/details/types.ts | 33 ++- .../siem/public/pages/hosts/details/utils.ts | 11 +- .../siem/public/pages/hosts/hosts.test.tsx | 4 +- .../plugins/siem/public/pages/hosts/hosts.tsx | 143 ++++++------ .../siem/public/pages/hosts/hosts_body.tsx | 96 -------- .../siem/public/pages/hosts/hosts_tabs.tsx | 87 +++++++ .../plugins/siem/public/pages/hosts/index.tsx | 212 ++---------------- .../plugins/siem/public/pages/hosts/types.ts | 45 ++++ 14 files changed, 581 insertions(+), 682 deletions(-) delete mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/details/__snapshots__/body.test.tsx.snap delete mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/details/body.test.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/details/body.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/hosts_body.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/hosts_tabs.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/types.ts diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/__snapshots__/body.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/hosts/details/__snapshots__/body.test.tsx.snap deleted file mode 100644 index 3815b319820ef..0000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/__snapshots__/body.test.tsx.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`body render snapshot 1`] = ` - - - - - -`; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.test.tsx deleted file mode 100644 index 83af0a616a660..0000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 { shallow, mount } from 'enzyme'; -import toJson from 'enzyme-to-json'; -import React from 'react'; -import { StaticIndexPattern } from 'ui/index_patterns'; - -import { mockIndexPattern } from '../../../mock/index_pattern'; -import { TestProviders } from '../../../mock/test_providers'; -import { mockUiSettings } from '../../../mock/ui_settings'; -import { CommonChildren } from '../navigation/types'; -import { HostDetailsBody } from './body'; -import { useKibanaCore } from '../../../lib/compose/kibana_core'; - -const mockUseKibanaCore = useKibanaCore as jest.Mock; -jest.mock('../../../lib/compose/kibana_core'); -mockUseKibanaCore.mockImplementation(() => ({ - uiSettings: mockUiSettings, -})); - -jest.mock('../../../containers/source', () => ({ - indicesExistOrDataTemporarilyUnavailable: () => true, - WithSource: ({ - children, - }: { - children: (args: { - indicesExist: boolean; - indexPattern: StaticIndexPattern; - }) => React.ReactNode; - }) => children({ indicesExist: true, indexPattern: mockIndexPattern }), -})); - -describe('body', () => { - test('render snapshot', () => { - const child: CommonChildren = () => {'I am a child'}; - const wrapper = shallow( - - {}} - to={0} - /> - - ); - expect(toJson(wrapper)).toMatchSnapshot(); - }); - - test('it should pass expected object properties to children', () => { - const child = jest.fn(); - mount( - - {}} - to={0} - /> - - ); - // match against everything but the functions to ensure they are there as expected - expect(child.mock.calls[0][0]).toMatchObject({ - endDate: 0, - filterQuery: - '{"bool":{"must":[],"filter":[{"match_all":{}},{"match_phrase":{"host.name":{"query":"host-1"}}}],"should":[],"must_not":[]}}', - skip: false, - startDate: 0, - type: 'details', - indexPattern: { - fields: [ - { name: '@timestamp', searchable: true, type: 'date', aggregatable: true }, - { name: '@version', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.ephemeral_id', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.hostname', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.id', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.test1', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.test2', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.test3', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.test4', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.test5', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.test6', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.test7', searchable: true, type: 'string', aggregatable: true }, - { name: 'agent.test8', searchable: true, type: 'string', aggregatable: true }, - { name: 'host.name', searchable: true, type: 'string', aggregatable: true }, - ], - title: 'filebeat-*,auditbeat-*,packetbeat-*', - }, - hostName: 'host-1', - }); - }); -}); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.tsx deleted file mode 100644 index ae8ebcf41cd56..0000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.tsx +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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 { getEsQueryConfig } from '@kbn/es-query'; -import React from 'react'; -import { connect } from 'react-redux'; - -import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source'; -import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../../store/inputs/actions'; -import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime'; -import { Anomaly } from '../../../components/ml/types'; -import { convertToBuildEsQuery } from '../../../lib/keury'; -import { useKibanaCore } from '../../../lib/compose/kibana_core'; - -import { HostDetailsBodyComponentProps } from './types'; -import { type, makeMapStateToProps } from './utils'; - -const HostDetailsBodyComponent = React.memo( - ({ - children, - deleteQuery, - detailName, - filters, - from, - isInitializing, - query, - setAbsoluteRangeDatePicker, - setQuery, - to, - }) => { - const core = useKibanaCore(); - return ( - - {({ indicesExist, indexPattern }) => { - const filterQuery = convertToBuildEsQuery({ - config: getEsQueryConfig(core.uiSettings), - indexPattern, - queries: [query], - filters: [ - { - meta: { - alias: null, - negate: false, - disabled: false, - type: 'phrase', - key: 'host.name', - value: detailName, - params: { - query: detailName, - }, - }, - query: { - match: { - 'host.name': { - query: detailName, - type: 'phrase', - }, - }, - }, - }, - ...filters, - ], - }); - return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - <> - {children({ - deleteQuery, - endDate: to, - filterQuery, - skip: isInitializing, - setQuery, - startDate: from, - type, - indexPattern, - hostName: detailName, - narrowDateRange: (score: Anomaly, interval: string) => { - const fromTo = scoreIntervalToDateTime(score, interval); - setAbsoluteRangeDatePicker({ - id: 'global', - from: fromTo.from, - to: fromTo.to, - }); - }, - updateDateRange: (min: number, max: number) => { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }, - })} - - ) : null; - }} - - ); - } -); - -HostDetailsBodyComponent.displayName = 'HostDetailsBodyComponent'; - -export const HostDetailsBody = connect( - makeMapStateToProps, - { - setAbsoluteRangeDatePicker: dispatchAbsoluteRangeDatePicker, - } -)(HostDetailsBodyComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx new file mode 100644 index 0000000000000..6ceebc1708b18 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx @@ -0,0 +1,108 @@ +/* + * 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 { mount } from 'enzyme'; +import React from 'react'; +import { StaticIndexPattern } from 'ui/index_patterns'; +import { MemoryRouter } from 'react-router-dom'; + +import { mockIndexPattern } from '../../../mock/index_pattern'; +import { TestProviders } from '../../../mock/test_providers'; +import { mockUiSettings } from '../../../mock/ui_settings'; +import { HostDetailsTabs } from './details_tabs'; +import { SetAbsoluteRangeDatePicker } from './types'; +import { hostDetailsPagePath } from '../types'; +import { type } from './utils'; +import { useKibanaCore } from '../../../lib/compose/kibana_core'; + +jest.mock('../../../lib/settings/use_kibana_ui_setting'); + +const mockUseKibanaCore = useKibanaCore as jest.Mock; +jest.mock('../../../lib/compose/kibana_core'); +mockUseKibanaCore.mockImplementation(() => ({ + uiSettings: mockUiSettings, +})); + +jest.mock('../../../containers/source', () => ({ + indicesExistOrDataTemporarilyUnavailable: () => true, + WithSource: ({ + children, + }: { + children: (args: { + indicesExist: boolean; + indexPattern: StaticIndexPattern; + }) => React.ReactNode; + }) => children({ indicesExist: true, indexPattern: mockIndexPattern }), +})); + +// Test will fail because we will to need to mock some core services to make the test work +// For now let's forget about SiemSearchBar +jest.mock('../../../components/search_bar', () => ({ + SiemSearchBar: () => null, +})); + +describe('body', () => { + const scenariosMap = { + authentications: 'AuthenticationsQueryTabBody', + allHosts: 'HostsQueryTabBody', + uncommonProcesses: 'UncommonProcessQueryTabBody', + anomalies: 'AnomaliesQueryTabBody', + events: 'EventsQueryTabBody', + }; + + Object.entries(scenariosMap).forEach(([path, componentName]) => + test(`it should pass expected object properties to ${componentName}`, () => { + const wrapper = mount( + + + {}} + to={0} + setAbsoluteRangeDatePicker={(jest.fn() as unknown) as SetAbsoluteRangeDatePicker} + hostDetailsPagePath={hostDetailsPagePath} + indexPattern={mockIndexPattern} + type={type} + filterQuery='{"bool":{"must":[],"filter":[{"match_all":{}},{"match_phrase":{"host.name":{"query":"host-1"}}}],"should":[],"must_not":[]}}' + /> + + + ); + + // match against everything but the functions to ensure they are there as expected + expect(wrapper.find(componentName).props()).toMatchObject({ + endDate: 0, + filterQuery: + '{"bool":{"must":[],"filter":[{"match_all":{}},{"match_phrase":{"host.name":{"query":"host-1"}}}],"should":[],"must_not":[]}}', + skip: false, + startDate: 0, + type: 'details', + indexPattern: { + fields: [ + { name: '@timestamp', searchable: true, type: 'date', aggregatable: true }, + { name: '@version', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.ephemeral_id', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.hostname', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.id', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test1', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test2', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test3', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test4', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test5', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test6', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test7', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test8', searchable: true, type: 'string', aggregatable: true }, + { name: 'host.name', searchable: true, type: 'string', aggregatable: true }, + ], + title: 'filebeat-*,auditbeat-*,packetbeat-*', + }, + hostName: 'host-1', + }); + }) + ); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx new file mode 100644 index 0000000000000..48b6d34d0b28b --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx @@ -0,0 +1,100 @@ +/* + * 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 React, { useCallback } from 'react'; +import { Route, Switch } from 'react-router-dom'; + +import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime'; +import { Anomaly } from '../../../components/ml/types'; +import { HostsTableType } from '../../../store/hosts/model'; + +import { HostDetailsTabsProps } from './types'; +import { type } from './utils'; + +import { + HostsQueryTabBody, + AuthenticationsQueryTabBody, + UncommonProcessQueryTabBody, + AnomaliesQueryTabBody, + EventsQueryTabBody, +} from '../navigation'; + +const HostDetailsTabs = React.memo( + ({ + deleteQuery, + filterQuery, + from, + isInitializing, + detailName, + setAbsoluteRangeDatePicker, + setQuery, + to, + indexPattern, + hostDetailsPagePath, + }) => { + const narrowDateRange = useCallback( + (score: Anomaly, interval: string) => { + const fromTo = scoreIntervalToDateTime(score, interval); + setAbsoluteRangeDatePicker({ + id: 'global', + from: fromTo.from, + to: fromTo.to, + }); + }, + [setAbsoluteRangeDatePicker, scoreIntervalToDateTime] + ); + + const updateDateRange = useCallback( + (min: number, max: number) => { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + [setAbsoluteRangeDatePicker, scoreIntervalToDateTime] + ); + + const tabProps = { + deleteQuery, + endDate: to, + filterQuery, + skip: isInitializing, + setQuery, + startDate: from, + type, + indexPattern, + hostName: detailName, + narrowDateRange, + updateDateRange, + }; + + return ( + + } + /> + } + /> + } + /> + } + /> + } + /> + + ); + } +); + +HostDetailsTabs.displayName = 'HostDetailsTabs'; + +export { HostDetailsTabs }; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx index d1d29c3d2ea82..aa81481bedbe7 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx @@ -11,6 +11,8 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; +import { inputsSelectors, State } from '../../../store'; + import { FiltersGlobal } from '../../../components/filters_global'; import { HeaderPage } from '../../../components/header_page'; import { KpiHostDetailsQuery } from '../../../containers/kpi_host_details'; @@ -32,22 +34,19 @@ import { LastEventIndexKey } from '../../../graphql/types'; import { convertToBuildEsQuery } from '../../../lib/keury'; import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../../store/inputs/actions'; import { SpyRoute } from '../../../utils/route/spy_routes'; +import { useKibanaCore } from '../../../lib/compose/kibana_core'; -import { HostsQueryProps } from '../hosts'; import { HostsEmptyPage } from '../hosts_empty_page'; - -export { HostDetailsBody } from './body'; import { navTabsHostDetails } from './nav_tabs'; -import { HostDetailsComponentProps } from './types'; -import { makeMapStateToProps } from './utils'; -import { useKibanaCore } from '../../../lib/compose/kibana_core'; +import { HostDetailsComponentProps, HostDetailsProps } from './types'; +import { HostDetailsTabs } from './details_tabs'; +import { type } from './utils'; const HostOverviewManage = manageQuery(HostOverview); const KpiHostDetailsManage = manageQuery(KpiHostsComponent); const HostDetailsComponent = React.memo( ({ - detailName, filters, from, isInitializing, @@ -56,6 +55,9 @@ const HostDetailsComponent = React.memo( setHostDetailsTablesActivePageToZero, setQuery, to, + detailName, + deleteQuery, + hostDetailsPagePath, }) => { useEffect(() => { setHostDetailsTablesActivePageToZero(null); @@ -96,94 +98,111 @@ const HostDetailsComponent = React.memo( ], }); return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - - - - + <> + + + + - - } - title={detailName} - /> - - {({ hostOverview, loading, id, inspect, refetch }) => ( - - {({ isLoadingAnomaliesData, anomaliesData }) => ( - { - const fromTo = scoreIntervalToDateTime(score, interval); - setAbsoluteRangeDatePicker({ - id: 'global', - from: fromTo.from, - to: fromTo.to, - }); - }} - /> - )} - - )} - + + } + title={detailName} + /> + + {({ hostOverview, loading, id, inspect, refetch }) => ( + + {({ isLoadingAnomaliesData, anomaliesData }) => ( + { + const fromTo = scoreIntervalToDateTime(score, interval); + setAbsoluteRangeDatePicker({ + id: 'global', + from: fromTo.from, + to: fromTo.to, + }); + }} + /> + )} + + )} + - + - - {({ kpiHostDetails, id, inspect, loading, refetch }) => ( - { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} - /> - )} - + + {({ kpiHostDetails, id, inspect, loading, refetch }) => ( + { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }} + /> + )} + - + + + - - + ) : ( <> - ); @@ -197,7 +216,16 @@ const HostDetailsComponent = React.memo( HostDetailsComponent.displayName = 'HostDetailsComponent'; -export const HostDetails = compose>( +export const makeMapStateToProps = () => { + const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); + const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); + return (state: State) => ({ + query: getGlobalQuerySelector(state), + filters: getGlobalFiltersQuerySelector(state), + }); +}; + +export const HostDetails = compose>( connect( makeMapStateToProps, { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts index b4dda2cee8760..9df57970176eb 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { StaticIndexPattern } from 'ui/index_patterns'; import { Filter } from '@kbn/es-query'; import { ActionCreator } from 'typescript-fsa'; import { Query } from 'src/plugins/data/common'; @@ -11,13 +12,10 @@ import { Query } from 'src/plugins/data/common'; import { InputsModelId } from '../../../store/inputs/constants'; import { HostComponentProps } from '../../../components/link_to/redirect_to_hosts'; import { HostsTableType } from '../../../store/hosts/model'; -import { HostsQueryProps } from '../hosts'; +import { HostsQueryProps } from '../types'; import { NavTab } from '../../../components/navigation/types'; -import { - AnomaliesChildren, - CommonChildren, - KeyHostsNavTabWithoutMlPermission, -} from '../navigation/types'; +import { KeyHostsNavTabWithoutMlPermission } from '../navigation/types'; +import { hostsModel } from '../../../store'; interface HostDetailsComponentReduxProps { query: Query; @@ -31,14 +29,16 @@ interface HostBodyComponentDispatchProps { to: number; }>; detailName: string; + hostDetailsPagePath: string; } interface HostDetailsComponentDispatchProps extends HostBodyComponentDispatchProps { setHostDetailsTablesActivePageToZero: ActionCreator; } -export interface HostDetailsBodyProps extends HostsQueryProps { - children: CommonChildren | AnomaliesChildren; +export interface HostDetailsProps extends HostsQueryProps { + detailName: string; + hostDetailsPagePath: string; } export type HostDetailsComponentProps = HostDetailsComponentReduxProps & @@ -46,10 +46,6 @@ export type HostDetailsComponentProps = HostDetailsComponentReduxProps & HostComponentProps & HostsQueryProps; -export type HostDetailsBodyComponentProps = HostDetailsComponentReduxProps & - HostBodyComponentDispatchProps & - HostDetailsBodyProps; - type KeyHostDetailsNavTabWithoutMlPermission = HostsTableType.authentications & HostsTableType.uncommonProcesses & HostsTableType.events; @@ -62,3 +58,16 @@ type KeyHostDetailsNavTab = | KeyHostDetailsNavTabWithMlPermission; export type HostDetailsNavTab = Record; + +export type HostDetailsTabsProps = HostBodyComponentDispatchProps & + HostsQueryProps & { + indexPattern: StaticIndexPattern; + type: hostsModel.HostsType; + filterQuery: string; + }; + +export type SetAbsoluteRangeDatePicker = ActionCreator<{ + id: InputsModelId; + from: number; + to: number; +}>; 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 52efe93c0c8dc..7483636cfe03d 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 @@ -6,7 +6,7 @@ import { Breadcrumb } from 'ui/chrome'; -import { hostsModel, inputsSelectors, State } from '../../../store'; +import { hostsModel } from '../../../store'; import { HostsTableType } from '../../../store/hosts/model'; import { getHostsUrl, getHostDetailsUrl } from '../../../components/link_to/redirect_to_hosts'; @@ -15,15 +15,6 @@ import { RouteSpyState } from '../../../utils/route/types'; export const type = hostsModel.HostsType.details; -export const makeMapStateToProps = () => { - const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); - const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); - return (state: State) => ({ - query: getGlobalQuerySelector(state), - filters: getGlobalFiltersQuerySelector(state), - }); -}; - const TabNameMappedToI18nKey = { [HostsTableType.hosts]: i18n.NAVIGATION_ALL_HOSTS_TITLE, [HostsTableType.authentications]: i18n.NAVIGATION_AUTHENTICATIONS_TITLE, diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx index 5b6444148045d..d2c9822889c26 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx @@ -19,7 +19,8 @@ import { wait } from '../../lib/helpers'; import { TestProviders } from '../../mock'; import { mockUiSettings } from '../../mock/ui_settings'; import { InputsModelId } from '../../store/inputs/constants'; -import { Hosts, HostsComponentProps } from './hosts'; +import { HostsComponentProps } from './types'; +import { Hosts } from './hosts'; import { useKibanaCore } from '../../lib/compose/kibana_core'; jest.mock('../../lib/settings/use_kibana_ui_setting'); @@ -97,6 +98,7 @@ describe('Hosts - rendering', () => { }>, query: { query: '', language: 'kuery' }, filters: [], + hostsPagePath: '', }; beforeAll(() => { 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 7c54745f872a9..334d730378b23 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -5,13 +5,11 @@ */ import { EuiSpacer } from '@elastic/eui'; -import { Filter, getEsQueryConfig } from '@kbn/es-query'; +import { getEsQueryConfig } from '@kbn/es-query'; import * as React from 'react'; import { compose } from 'redux'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; -import { ActionCreator } from 'typescript-fsa'; -import { Query } from 'src/plugins/data/common'; import { FiltersGlobal } from '../../components/filters_global'; import { GlobalTimeArgs } from '../../containers/global_time'; @@ -27,41 +25,34 @@ import { SiemSearchBar } from '../../components/search_bar'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { LastEventIndexKey } from '../../graphql/types'; import { convertToBuildEsQuery } from '../../lib/keury'; -import { inputsSelectors, State } from '../../store'; +import { inputsSelectors, State, hostsModel } from '../../store'; import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; -import { InputsModelId } from '../../store/inputs/constants'; import { SpyRoute } from '../../utils/route/spy_routes'; +import { useKibanaCore } from '../../lib/compose/kibana_core'; import { HostsEmptyPage } from './hosts_empty_page'; import { navTabsHosts } from './nav_tabs'; import * as i18n from './translations'; -import { useKibanaCore } from '../../lib/compose/kibana_core'; +import { HostsComponentProps, HostsComponentReduxProps } from './types'; +import { HostsTabs } from './hosts_tabs'; const KpiHostsComponentManage = manageQuery(KpiHostsComponent); -interface HostsComponentReduxProps { - query: Query; - filters: Filter[]; -} - -interface HostsComponentDispatchProps { - setAbsoluteRangeDatePicker: ActionCreator<{ - id: InputsModelId; - from: number; - to: number; - }>; -} - -export type HostsQueryProps = GlobalTimeArgs; - -export type HostsComponentProps = HostsComponentReduxProps & - HostsComponentDispatchProps & - HostsQueryProps; - const HostsComponent = React.memo( - ({ isInitializing, filters, from, query, setAbsoluteRangeDatePicker, setQuery, to }) => { + ({ + deleteQuery, + isInitializing, + filters, + from, + query, + setAbsoluteRangeDatePicker, + setQuery, + to, + hostsPagePath, + }) => { const capabilities = React.useContext(MlCapabilitiesContext); const core = useKibanaCore(); + return ( <> @@ -73,48 +64,62 @@ const HostsComponent = React.memo( filters, }); return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - - - - + <> + + + + - } - title={i18n.PAGE_TITLE} - /> - <> - - {({ kpiHosts, loading, id, inspect, refetch }) => ( - { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} - /> - )} - - - } + title={i18n.PAGE_TITLE} /> - - - + <> + + {({ kpiHosts, loading, id, inspect, refetch }) => ( + { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }} + /> + )} + + + + + + + + ) : ( <> @@ -142,8 +147,12 @@ const makeMapStateToProps = () => { return mapStateToProps; }; +interface HostsProps extends GlobalTimeArgs { + hostsPagePath: string; +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const Hosts = compose>( +export const Hosts = compose>( connect( makeMapStateToProps, { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_body.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_body.tsx deleted file mode 100644 index 3d7e54b4a19ac..0000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_body.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 { getEsQueryConfig } from '@kbn/es-query'; -import React, { memo } from 'react'; -import { connect } from 'react-redux'; - -import { scoreIntervalToDateTime } from '../../components/ml/score/score_interval_to_datetime'; -import { Anomaly } from '../../components/ml/types'; -import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; -import { convertToBuildEsQuery } from '../../lib/keury'; -import { useKibanaCore } from '../../lib/compose/kibana_core'; -import { hostsModel, inputsSelectors, State } from '../../store'; -import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; - -import { HostsComponentProps } from './hosts'; -import { CommonChildren, AnomaliesChildren } from './navigation/types'; - -interface HostsBodyComponentProps extends HostsComponentProps { - children: CommonChildren | AnomaliesChildren; -} - -const HostsBodyComponent = memo( - ({ - children, - deleteQuery, - filters, - from, - isInitializing, - query, - setAbsoluteRangeDatePicker, - setQuery, - to, - }) => { - const core = useKibanaCore(); - return ( - - {({ indicesExist, indexPattern }) => { - const filterQuery = convertToBuildEsQuery({ - config: getEsQueryConfig(core.uiSettings), - indexPattern, - queries: [query], - filters, - }); - return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - <> - {children({ - deleteQuery, - endDate: to, - filterQuery, - skip: isInitializing, - setQuery, - startDate: from, - type: hostsModel.HostsType.page, - indexPattern, - narrowDateRange: (score: Anomaly, interval: string) => { - const fromTo = scoreIntervalToDateTime(score, interval); - setAbsoluteRangeDatePicker({ - id: 'global', - from: fromTo.from, - to: fromTo.to, - }); - }, - updateDateRange: (min: number, max: number) => { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }, - })} - - ) : null; - }} - - ); - } -); - -HostsBodyComponent.displayName = 'HostsBodyComponent'; - -const makeMapStateToProps = () => { - const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); - const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); - const mapStateToProps = (state: State) => ({ - query: getGlobalQuerySelector(state), - filters: getGlobalFiltersQuerySelector(state), - }); - return mapStateToProps; -}; - -export const HostsBody = connect( - makeMapStateToProps, - { - setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker, - } -)(HostsBodyComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_tabs.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_tabs.tsx new file mode 100644 index 0000000000000..6dbfb422ed7a6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_tabs.tsx @@ -0,0 +1,87 @@ +/* + * 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 React, { memo } from 'react'; +import { Route, Switch } from 'react-router-dom'; + +import { HostsTabsProps } from './types'; +import { scoreIntervalToDateTime } from '../../components/ml/score/score_interval_to_datetime'; +import { Anomaly } from '../../components/ml/types'; +import { HostsTableType } from '../../store/hosts/model'; + +import { + HostsQueryTabBody, + AuthenticationsQueryTabBody, + UncommonProcessQueryTabBody, + AnomaliesQueryTabBody, + EventsQueryTabBody, +} from './navigation'; + +const HostsTabs = memo( + ({ + deleteQuery, + filterQuery, + setAbsoluteRangeDatePicker, + to, + from, + setQuery, + isInitializing, + type, + indexPattern, + hostsPagePath, + }) => { + const tabProps = { + deleteQuery, + endDate: to, + filterQuery, + skip: isInitializing, + setQuery, + startDate: from, + type, + indexPattern, + narrowDateRange: (score: Anomaly, interval: string) => { + const fromTo = scoreIntervalToDateTime(score, interval); + setAbsoluteRangeDatePicker({ + id: 'global', + from: fromTo.from, + to: fromTo.to, + }); + }, + updateDateRange: (min: number, max: number) => { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + }; + + return ( + + } + /> + } + /> + } + /> + } + /> + } + /> + + ); + } +); + +HostsTabs.displayName = 'HostsTabs'; + +export { HostsTabs }; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/index.tsx index 72b19dbbb13b2..c8d450a62cc57 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/index.tsx @@ -7,21 +7,13 @@ import React from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; -import { HostDetailsBody, HostDetails } from './details'; -import { - HostsQueryTabBody, - AuthenticationsQueryTabBody, - UncommonProcessQueryTabBody, - AnomaliesQueryTabBody, - EventsQueryTabBody, -} from './navigation'; -import { HostsBody } from './hosts_body'; +import { HostDetails } from './details'; import { HostsTableType } from '../../store/hosts/model'; + import { GlobalTime } from '../../containers/global_time'; import { SiemPageName } from '../home/types'; import { Hosts } from './hosts'; - -const hostsPagePath = `/:pageName(${SiemPageName.hosts})`; +import { hostsPagePath, hostDetailsPagePath } from './types'; const getHostsTabPath = (pagePath: string) => `${pagePath}/:tabName(` + @@ -32,7 +24,7 @@ const getHostsTabPath = (pagePath: string) => `${HostsTableType.events})`; const getHostDetailsTabPath = (pagePath: string) => - `${pagePath}/:detailName/:tabName(` + + `${hostDetailsPagePath}/:tabName(` + `${HostsTableType.authentications}|` + `${HostsTableType.uncommonProcesses}|` + `${HostsTableType.anomalies}|` + @@ -44,191 +36,38 @@ export const HostsContainer = React.memo(({ url }) => ( {({ to, from, setQuery, deleteQuery, isInitializing }) => ( - ( - ( - <> - - - - )} - /> - )} - /> ( - <> - - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - + )} /> ( - <> - - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - + )} /> (({ url }) => ( }) => } /> ( - + )} /> diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/types.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/types.ts new file mode 100644 index 0000000000000..980c5535129aa --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/types.ts @@ -0,0 +1,45 @@ +/* + * 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 { StaticIndexPattern } from 'ui/index_patterns'; +import { ActionCreator } from 'typescript-fsa'; +import { Filter } from '@kbn/es-query'; +import { Query } from 'src/plugins/data/common'; + +import { SiemPageName } from '../home/types'; +import { hostsModel } from '../../store'; +import { InputsModelId } from '../../store/inputs/constants'; +import { GlobalTimeArgs } from '../../containers/global_time'; + +export const hostsPagePath = `/:pageName(${SiemPageName.hosts})`; +export const hostDetailsPagePath = `${hostsPagePath}/:detailName`; + +export interface HostsComponentReduxProps { + query: Query; + filters: Filter[]; +} + +export interface HostsComponentDispatchProps { + setAbsoluteRangeDatePicker: ActionCreator<{ + id: InputsModelId; + from: number; + to: number; + }>; + hostsPagePath: string; +} + +export type HostsTabsProps = HostsComponentDispatchProps & + HostsQueryProps & { + filterQuery: string; + type: hostsModel.HostsType; + indexPattern: StaticIndexPattern; + }; + +export type HostsQueryProps = GlobalTimeArgs; + +export type HostsComponentProps = HostsComponentReduxProps & + HostsComponentDispatchProps & + HostsQueryProps;