diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/domains_table/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/network/domains_table/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 2d8836c63e2be36..000000000000000 --- a/x-pack/legacy/plugins/siem/public/components/page/network/domains_table/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,168 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Domains Table Component Rendering it renders the default Domains table 1`] = ` - -`; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/domains_table/columns.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/domains_table/columns.tsx deleted file mode 100644 index b63895f333db42d..000000000000000 --- a/x-pack/legacy/plugins/siem/public/components/page/network/domains_table/columns.tsx +++ /dev/null @@ -1,216 +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 { EuiIcon, EuiToolTip } from '@elastic/eui'; -import numeral from '@elastic/numeral'; -import { getOr, isEmpty } from 'lodash/fp'; -import moment from 'moment'; -import React from 'react'; -import { StaticIndexPattern } from 'ui/index_patterns'; - -import { - DomainsEdges, - DomainsItem, - DomainsNetworkField, - FlowDirection, - FlowTarget, -} from '../../../../graphql/types'; -import { assertUnreachable } from '../../../../lib/helpers'; -import { escapeQueryValue } from '../../../../lib/keury'; -import { networkModel } from '../../../../store'; -import { DragEffects, DraggableWrapper } from '../../../drag_and_drop/draggable_wrapper'; -import { escapeDataProviderId } from '../../../drag_and_drop/helpers'; -import { defaultToEmptyTag, getEmptyTagValue } from '../../../empty_value'; -import { PreferenceFormattedDate } from '../../../formatted_date'; -import { Columns } from '../../../paginated_table'; -import { LocalizedDateTooltip } from '../../../localized_date_tooltip'; -import { IS_OPERATOR } from '../../../timeline/data_providers/data_provider'; -import { PreferenceFormattedBytes } from '../../../formatted_bytes'; -import { Provider } from '../../../timeline/data_providers/provider'; -import { AddToKql } from '../../add_to_kql'; - -import * as i18n from './translations'; - -export type DomainsColumns = [ - Columns, - Columns, - Columns, - Columns, - Columns, - Columns -]; - -export const getDomainsColumns = ( - indexPattern: StaticIndexPattern, - ip: string, - flowDirection: FlowDirection, - flowTarget: FlowTarget, - type: networkModel.NetworkType, - tableId: string -): DomainsColumns => [ - { - field: `node.${flowTarget}.domainName`, - name: i18n.DOMAIN_NAME, - truncateText: false, - hideForMobile: false, - sortable: true, - render: domainName => { - const domainNameAttr = `${flowTarget}.domain`; - if (domainName != null) { - const id = escapeDataProviderId( - `${tableId}-table-${flowTarget}-${flowDirection}-domain-${domainName}` - ); - return ( - - snapshot.isDragging ? ( - - - - ) : ( - <>{domainName} - ) - } - /> - ); - } else { - return getEmptyTagValue(); - } - }, - }, - { - field: 'node.network.direction', - name: i18n.DIRECTION, - truncateText: false, - hideForMobile: false, - render: directions => - isEmpty(directions) - ? getEmptyTagValue() - : directions && - directions.map((direction, index) => ( - - <> - {defaultToEmptyTag(direction)} - {index < directions.length - 1 ? '\u00A0' : null} - - - )), - }, - { - field: 'node.network.bytes', - name: i18n.BYTES, - truncateText: false, - hideForMobile: false, - sortable: true, - render: bytes => { - if (bytes != null) { - return ; - } else { - return getEmptyTagValue(); - } - }, - }, - { - field: 'node.network.packets', - name: i18n.PACKETS, - truncateText: false, - hideForMobile: false, - sortable: true, - render: packets => { - if (packets != null) { - return numeral(packets).format('0,000'); - } else { - return getEmptyTagValue(); - } - }, - }, - { - field: `node.${flowTarget}.uniqueIpCount`, - name: getFlowTargetTitle(flowTarget), - truncateText: false, - hideForMobile: false, - sortable: true, - render: uniqueIpCount => { - if (uniqueIpCount != null) { - return numeral(uniqueIpCount).format('0,000'); - } else { - return getEmptyTagValue(); - } - }, - }, - { - name: ( - - <> - {i18n.LAST_SEEN}{' '} - - - - ), - truncateText: false, - hideForMobile: false, - render: ({ node }) => { - const lastSeenAttr = `${flowTarget}.lastSeen`; - const lastSeen = getOr(null, lastSeenAttr, node); - if (lastSeen != null) { - return ( - - - - ); - } - return getEmptyTagValue(); - }, - }, -]; - -const getFlowTargetTitle = (flowTarget: FlowTarget): string => { - switch (flowTarget) { - case FlowTarget.client: - return i18n.UNIQUE_CLIENTS; - case FlowTarget.server: - return i18n.UNIQUE_SERVERS; - case FlowTarget.source: - return i18n.UNIQUE_DESTINATIONS; - case FlowTarget.destination: - return i18n.UNIQUE_SOURCES; - } - assertUnreachable(flowTarget); -}; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/__snapshots__/index.test.tsx.snap index a8fde09ff5f4f5f..f80a61836b86ed7 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/__snapshots__/index.test.tsx.snap @@ -181,6 +181,12 @@ exports[`NetworkTopNFlow Table Component rendering it renders the default Networ "searchable": true, "type": "string", }, + Object { + "aggregatable": true, + "name": "host.name", + "searchable": true, + "type": "string", + }, ], "title": "filebeat-*,auditbeat-*,packetbeat-*", } diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx index eda7d73ccb40dca..f8c29b79bdc170c 100644 --- a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx @@ -5,7 +5,7 @@ */ import { Filter } from '@kbn/es-query'; -import { getOr, isEqual } from 'lodash/fp'; +import { getOr, isEqual, set } from 'lodash/fp'; import React, { memo, useEffect } from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; @@ -16,13 +16,14 @@ import { StaticIndexPattern, IndexPattern } from 'ui/index_patterns'; import { TimeRange, Query } from 'src/plugins/data/common/types'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; +import { OnTimeChangeProps } from '@elastic/eui'; import { start as data } from '../../../../../../../src/legacy/core_plugins/data/public/legacy'; import { inputsActions } from '../../store/inputs'; import { InputsRange } from '../../store/inputs/model'; import { InputsModelId } from '../../store/inputs/constants'; import { State, inputsModel } from '../../store'; -import { dispatchUpdateReduxTime, DispatchUpdateReduxTime, formatDate } from '../super_date_picker'; +import { formatDate } from '../super_date_picker'; import { endSelector, filterQuerySelector, @@ -34,6 +35,7 @@ import { startSelector, toStrSelector, } from './selectors'; +import { timelineActions, hostsActions, networkActions } from '../../store/actions'; const { ui: { SearchBar }, @@ -57,16 +59,7 @@ interface SiemSearchBarRedux { } interface SiemSearchBarDispatch { - updateReduxTime: DispatchUpdateReduxTime; - setFilterQuery: ({ - id, - query, - language, - }: { - id: InputsModelId; - query: string | { [key: string]: unknown }; - language: string; - }) => void; + updateSearch: DispatchUpdateSearch; setSavedQuery: ({ id, savedQuery, @@ -99,13 +92,12 @@ const SearchBarComponent = memo { if (fromStr != null && toStr != null) { timefilter.timefilter.setTime({ from: fromStr, to: toStr }); @@ -117,9 +109,18 @@ const SearchBarComponent = memo { - let isStateUpdated = false; const isQuickSelection = payload.dateRange.from.includes('now') || payload.dateRange.to.includes('now'); + let updateSearchBar: UpdateReduxSearchBar = { + id, + end: toStr != null ? toStr : new Date(end).toISOString(), + start: fromStr != null ? fromStr : new Date(start).toISOString(), + isInvalid: false, + isQuickSelection, + updateTime: false, + }; + let isStateUpdated = false; + if ( (isQuickSelection && (fromStr !== payload.dateRange.from || toStr !== payload.dateRange.to)) || @@ -128,53 +129,39 @@ const SearchBarComponent = memo q.refetch && (q.refetch as inputsModel.Refetch)()); } } + + updateSearch(updateSearchBar); }; const onRefresh = (payload: { dateRange: TimeRange }) => { if (payload.dateRange.from.includes('now') || payload.dateRange.to.includes('now')) { - updateReduxTime({ - end: payload.dateRange.to, + updateSearch({ id, + end: payload.dateRange.to, + start: payload.dateRange.from, isInvalid: false, isQuickSelection: true, - kql: undefined, - start: payload.dateRange.from, - timelineId, + updateTime: true, }); } else { queries.forEach(q => q.refetch && (q.refetch as inputsModel.Refetch)()); @@ -186,45 +173,54 @@ const SearchBarComponent = memo { - siemFilterManager.setFilters(savedQueryUpdated.attributes.filters || []); - const isQuickSelection = savedQueryUpdated.attributes.timefilter ? savedQueryUpdated.attributes.timefilter.from.includes('now') || savedQueryUpdated.attributes.timefilter.to.includes('now') : false; + let updateSearchBar: UpdateReduxSearchBar = { + id, + filters: savedQueryUpdated.attributes.filters || [], + end: toStr != null ? toStr : new Date(end).toISOString(), + start: fromStr != null ? fromStr : new Date(start).toISOString(), + isInvalid: false, + isQuickSelection, + updateTime: false, + }; + if (savedQueryUpdated.attributes.timefilter) { - updateReduxTime({ - end: savedQueryUpdated.attributes.timefilter - ? savedQueryUpdated.attributes.timefilter.to - : toStr, - id, - isInvalid: false, - isQuickSelection, - kql: undefined, - start: savedQueryUpdated.attributes.timefilter - ? savedQueryUpdated.attributes.timefilter.from - : fromStr, - timelineId, - }); + updateSearchBar.end = savedQueryUpdated.attributes.timefilter + ? savedQueryUpdated.attributes.timefilter.to + : updateSearchBar.end; + updateSearchBar.start = savedQueryUpdated.attributes.timefilter + ? savedQueryUpdated.attributes.timefilter.from + : updateSearchBar.start; + updateSearchBar.updateTime = true; } - setFilterQuery({ - id, - ...savedQueryUpdated.attributes.query, - }); - setSavedQuery({ id, savedQuery: savedQueryUpdated }); + updateSearchBar = set('query', savedQueryUpdated.attributes.query, updateSearchBar); + updateSearchBar = set('savedQuery', savedQueryUpdated, updateSearchBar); + + updateSearch(updateSearchBar); }; const onClearSavedQuery = () => { if (savedQuery != null) { - setFilterQuery({ + updateSearch({ id, - query: '', - language: savedQuery.attributes.query.language, + filters: [], + end: toStr != null ? toStr : new Date(end).toISOString(), + start: fromStr != null ? fromStr : new Date(start).toISOString(), + isInvalid: false, + isQuickSelection: false, + updateTime: false, + query: { + query: '', + language: savedQuery.attributes.query.language, + }, + resetSavedQuery: true, + savedQuery: undefined, }); - setSavedQuery({ id, savedQuery: undefined }); - siemFilterManager.setFilters([]); } }; @@ -303,17 +299,90 @@ const makeMapStateToProps = () => { SearchBarComponent.displayName = 'SiemSearchBarComponent'; +interface UpdateReduxSearchBar extends OnTimeChangeProps { + id: InputsModelId; + filters?: Filter[]; + query?: Query; + savedQuery?: SavedQuery; + resetSavedQuery?: boolean; + timelineId?: string; + updateTime: boolean; +} + +type DispatchUpdateSearch = ({ + end, + id, + isQuickSelection, + start, + timelineId, +}: UpdateReduxSearchBar) => void; + +export const dispatchUpdateSearch = (dispatch: Dispatch) => ({ + end, + filters, + id, + isQuickSelection, + query, + resetSavedQuery, + savedQuery, + start, + timelineId, + updateTime = false, +}: UpdateReduxSearchBar): void => { + if (updateTime) { + const fromDate = formatDate(start); + let toDate = formatDate(end, { roundUp: true }); + if (isQuickSelection) { + dispatch( + inputsActions.setRelativeRangeDatePicker({ + id, + fromStr: start, + toStr: end, + from: fromDate, + to: toDate, + }) + ); + } else { + toDate = formatDate(end); + dispatch( + inputsActions.setAbsoluteRangeDatePicker({ + id, + from: formatDate(start), + to: formatDate(end), + }) + ); + } + if (timelineId != null) { + dispatch( + timelineActions.updateRange({ + id: timelineId, + start: fromDate, + end: toDate, + }) + ); + } + } + if (query != null) { + dispatch( + inputsActions.setFilterQuery({ + id, + ...query, + }) + ); + } + if (filters != null) { + siemFilterManager.setFilters(filters); + } + if (savedQuery != null || resetSavedQuery) { + dispatch(inputsActions.setSavedQuery({ id, savedQuery })); + } + + dispatch(hostsActions.setHostTablesActivePageToZero()); + dispatch(networkActions.setNetworkTablesActivePageToZero()); +}; + const mapDispatchToProps = (dispatch: Dispatch) => ({ - updateReduxTime: dispatchUpdateReduxTime(dispatch), - setFilterQuery: ({ - id, - query, - language, - }: { - id: InputsModelId; - query: string | { [key: string]: unknown }; - language: string; - }) => dispatch(inputsActions.setFilterQuery({ id, query, language })), + updateSearch: dispatchUpdateSearch(dispatch), setSavedQuery: ({ id, savedQuery }: { id: InputsModelId; savedQuery: SavedQuery | undefined }) => dispatch(inputsActions.setSavedQuery({ id, savedQuery })), setSearchBarFilter: ({ id, filters }: { id: InputsModelId; filters: Filter[] }) => diff --git a/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx b/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx index 7dd66e21dad03b7..2d93fde336d3acc 100644 --- a/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx @@ -18,7 +18,7 @@ import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { inputsModel, State } from '../../store'; -import { inputsActions, timelineActions, hostsActions, networkActions } from '../../store/actions'; +import { inputsActions, timelineActions } from '../../store/actions'; import { InputsModelId } from '../../store/inputs/constants'; import { policySelector, @@ -55,18 +55,13 @@ interface UpdateReduxTime extends OnTimeChangeProps { timelineId?: string; } -interface ReturnUpdateReduxTime { - kqlHasBeenUpdated: boolean; -} - export type DispatchUpdateReduxTime = ({ end, id, isQuickSelection, - kql, start, timelineId, -}: UpdateReduxTime) => ReturnUpdateReduxTime; +}: UpdateReduxTime) => void; interface SuperDatePickerDispatchProps { setDuration: ({ id, duration }: { id: InputsModelId; duration: number }) => void; @@ -109,7 +104,7 @@ export const SuperDatePickerComponent = React.memo( [] ); const onRefresh = ({ start: newStart, end: newEnd }: OnRefreshProps): void => { - const { kqlHasBeenUpdated } = updateReduxTime({ + updateReduxTime({ end: newEnd, id, isInvalid: false, @@ -122,10 +117,7 @@ export const SuperDatePickerComponent = React.memo( const currentEnd = isQuickSelection ? formatDate(newEnd, { roundUp: true }) : formatDate(newEnd); - if ( - !kqlHasBeenUpdated && - (!isQuickSelection || (start === currentStart && end === currentEnd)) - ) { + if (!isQuickSelection || (start === currentStart && end === currentEnd)) { refetchQuery(queries); } }; @@ -215,10 +207,9 @@ export const dispatchUpdateReduxTime = (dispatch: Dispatch) => ({ end, id, isQuickSelection, - kql, start, timelineId, -}: UpdateReduxTime): ReturnUpdateReduxTime => { +}: UpdateReduxTime): void => { const fromDate = formatDate(start); let toDate = formatDate(end, { roundUp: true }); if (isQuickSelection) { @@ -250,21 +241,6 @@ export const dispatchUpdateReduxTime = (dispatch: Dispatch) => ({ }) ); } - - let kqlHasBeenUpdated = false; - if (kql) { - // if refetch is successful, it will have a side effect - // to set all the tables on its activePage to zero (meaning pagination) - kqlHasBeenUpdated = kql.refetch(dispatch); - } - if (!kqlHasBeenUpdated) { - // Date picker is global to all the page in the app - // (Hosts/Network are the only one having tables) - dispatch(hostsActions.setHostTablesActivePageToZero()); - dispatch(networkActions.setNetworkTablesActivePageToZero()); - } - - return { kqlHasBeenUpdated }; }; export const makeMapStateToProps = () => { diff --git a/x-pack/legacy/plugins/siem/public/store/network/reducer.ts b/x-pack/legacy/plugins/siem/public/store/network/reducer.ts index 9e4ed569cdf687b..74281bc2a4a5aab 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/reducer.ts @@ -17,6 +17,7 @@ import { import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from '../constants'; import { + setNetworkTablesActivePageToZero, updateDnsLimit, updateDnsSort, updateIpDetailsFlowTarget, @@ -30,6 +31,10 @@ import { updateUsersLimit, updateUsersSort, } from './actions'; +import { + setNetworkDetailsQueriesActivePageToZero, + setNetworkPageQueriesActivePageToZero, +} from './helpers'; import { IpDetailsTableType, NetworkModel, NetworkTableType, NetworkType } from './model'; export type NetworkState = NetworkModel; @@ -104,6 +109,17 @@ export const initialNetworkState: NetworkState = { }; export const networkReducer = reducerWithInitialState(initialNetworkState) + .case(setNetworkTablesActivePageToZero, state => ({ + ...state, + page: { + ...state.page, + queries: setNetworkPageQueriesActivePageToZero(state), + }, + details: { + ...state.details, + queries: setNetworkDetailsQueriesActivePageToZero(state), + }, + })) .case(updateIpDetailsTableActivePage, (state, { activePage, tableType }) => ({ ...state, [NetworkType.details]: {