From b02460a2a4fc138a60164b6ad56932f9d703dd8a Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Thu, 29 Apr 2021 14:38:10 -0600 Subject: [PATCH 01/20] broken start to fix --- .../events_viewer/events_viewer.tsx | 9 +++- .../public/common/lib/keury/index.ts | 6 ++- .../components/alerts_table/index.tsx | 16 ++++++- .../detection_engine/alerts/use_query.tsx | 1 + .../timelines/components/timeline/helpers.tsx | 48 +++++++++++++++++-- .../timeline/query_tab_content/index.tsx | 3 ++ 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 5dadd740ae3bc..a87f399b0053f 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -49,9 +49,13 @@ import { import { GraphOverlay } from '../../../timelines/components/graph_overlay'; import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering'; import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles'; -import { defaultControlColumn } from '../../../timelines/components/timeline/body/control_columns'; import { timelineSelectors, timelineActions } from '../../../timelines/store/timeline'; import { useDeepEqualSelector } from '../../hooks/use_selector'; +import { + defaultControlColumn, + ControlColumnProps, +} from '../../../timelines/components/timeline/body/control_columns'; +import { useAppToasts } from '../../hooks/use_app_toasts'; export const EVENTS_VIEWER_HEADER_HEIGHT = 90; // px const UTILITY_BAR_HEIGHT = 19; // px @@ -171,6 +175,8 @@ const EventsViewerComponent: React.FC = ({ const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; const kibana = useKibana(); const [isQueryLoading, setIsQueryLoading] = useState(false); + const { getManageTimelineById, setIsTimelineLoading } = useManageTimeline(); + const { addError } = useAppToasts(); useEffect(() => { dispatch(timelineActions.updateIsLoading({ id, isLoading: isQueryLoading })); @@ -203,6 +209,7 @@ const EventsViewerComponent: React.FC = ({ kqlQuery: query, kqlMode, isEventViewer: true, + addError, }); const canQueryTimeline = useMemo( diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts index a71524f9e02a8..d0f3337704f81 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts @@ -75,11 +75,13 @@ export const convertToBuildEsQuery = ({ indexPattern, queries, filters, + handleError, }: { config: EsQueryConfig; indexPattern: IIndexPattern; queries: Query[]; filters: Filter[]; + handleError: (error: Error) => void; }) => { try { return JSON.stringify( @@ -93,7 +95,7 @@ export const convertToBuildEsQuery = ({ } ) ); - } catch (exp) { - return ''; + } catch (error) { + handleError(error); } }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 7980160fea76c..b7a0270c4747b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -104,7 +104,8 @@ export const AlertsTableComponent: React.FC = ({ } = useSourcererScope(SourcererScopeName.detections); const kibana = useKibana(); const [, dispatchToaster] = useStateToaster(); - const { addWarning } = useAppToasts(); + const { addWarning, addError } = useAppToasts(); + const { initializeTimeline, setSelectAll } = useManageTimeline(); // TODO: Once we are past experimental phase this code should be removed const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled'); @@ -125,11 +126,22 @@ export const AlertsTableComponent: React.FC = ({ kqlQuery: globalQuery, kqlMode: globalQuery.language, isEventViewer: true, + addError, }); } return null; }, - [browserFields, defaultFilters, globalFilters, globalQuery, indexPatterns, kibana, to, from] + [ + browserFields, + defaultFilters, + globalFilters, + globalQuery, + indexPatterns, + kibana, + to, + from, + addError, + ] ); const setEventsLoadingCallback = useCallback( diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_query.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_query.tsx index 4d7d80b74a24d..88be60ff1d1ad 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_query.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_query.tsx @@ -50,6 +50,7 @@ export const useQueryAlerts = ({ refetch: null, }); const [loading, setLoading] = useState(false); + console.log('Query', query); useEffect(() => { let isSubscribed = true; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx index f2a4071111602..a3efbec963f43 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx @@ -32,6 +32,7 @@ import { } from '../../../../../../../src/plugins/data/public'; import { EVENTS_TABLE_CLASS_NAME } from './styles'; +import { UseAppToasts } from '../../../common/hooks/use_app_toasts'; const isNumber = (value: string | number) => !isNaN(Number(value)); @@ -152,6 +153,7 @@ export const combineQueries = ({ kqlQuery, kqlMode, isEventViewer, + addError, }: { config: EsQueryConfig; dataProviders: DataProvider[]; @@ -161,27 +163,57 @@ export const combineQueries = ({ kqlQuery: Query; kqlMode: string; isEventViewer?: boolean; + addError: UseAppToasts['addError']; }): { filterQuery: string } | null => { + const handleError = (error: Error) => { + addError(error, { title: error.name }); + }; const kuery: Query = { query: '', language: kqlQuery.language }; if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEmpty(filters) && !isEventViewer) { return null; } else if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEventViewer) { return { - filterQuery: convertToBuildEsQuery({ config, queries: [kuery], indexPattern, filters }), + filterQuery: + convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + handleError, + }) || '', }; } else if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && !isEmpty(filters)) { return { - filterQuery: convertToBuildEsQuery({ config, queries: [kuery], indexPattern, filters }), + filterQuery: convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + handleError, + }), }; } else if (isEmpty(dataProviders) && !isEmpty(kqlQuery.query)) { kuery.query = `(${kqlQuery.query})`; return { - filterQuery: convertToBuildEsQuery({ config, queries: [kuery], indexPattern, filters }), + filterQuery: convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + handleError, + }), }; } else if (!isEmpty(dataProviders) && isEmpty(kqlQuery)) { kuery.query = `(${buildGlobalQuery(dataProviders, browserFields)})`; return { - filterQuery: convertToBuildEsQuery({ config, queries: [kuery], indexPattern, filters }), + filterQuery: + convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + handleError, + }) || '', }; } const operatorKqlQuery = kqlMode === 'filter' ? 'and' : 'or'; @@ -190,7 +222,13 @@ export const combineQueries = ({ kqlQuery.query as string )})`; return { - filterQuery: convertToBuildEsQuery({ config, queries: [kuery], indexPattern, filters }), + filterQuery: convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + handleError, + }), }; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index 6f0bbd026cd7b..f3dbc335caf6d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -21,6 +21,7 @@ import { connect, ConnectedProps, useDispatch } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { InPortal } from 'react-reverse-portal'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; import { CellValueElementProps } from '../cell_rendering'; import { Direction, TimelineItem } from '../../../../../common/search_strategy'; @@ -198,6 +199,7 @@ export const QueryTabContentComponent: React.FC = ({ query: string; language: KueryFilterQueryKind; } = { query: kqlQueryExpression, language: 'kuery' }; + const { addError } = useAppToasts(); const combinedQueries = combineQueries({ config: esQueryConfig, @@ -207,6 +209,7 @@ export const QueryTabContentComponent: React.FC = ({ filters, kqlQuery, kqlMode, + addError, }); const isBlankTimeline: boolean = From 5120ce6c4c651fd09032368012da727bd36fcf87 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 3 May 2021 17:29:13 -0600 Subject: [PATCH 02/20] add new method --- .../events_viewer/events_viewer.tsx | 11 +- .../public/common/lib/keury/index.ts | 35 +++++- .../timelines/components/timeline/helpers.tsx | 112 +++++++++--------- .../timeline/query_tab_content/index.tsx | 2 - 4 files changed, 97 insertions(+), 63 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index a87f399b0053f..12d2807cdfabc 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -6,7 +6,7 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; @@ -178,6 +178,13 @@ const EventsViewerComponent: React.FC = ({ const { getManageTimelineById, setIsTimelineLoading } = useManageTimeline(); const { addError } = useAppToasts(); + const handleQueryError = useCallback( + (error: Error) => { + addError(error, { title: error.name }); + }, + [addError] + ); + useEffect(() => { dispatch(timelineActions.updateIsLoading({ id, isLoading: isQueryLoading })); }, [dispatch, id, isQueryLoading]); @@ -209,7 +216,7 @@ const EventsViewerComponent: React.FC = ({ kqlQuery: query, kqlMode, isEventViewer: true, - addError, + handleError: handleQueryError, }); const canQueryTimeline = useMemo( diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts index d0f3337704f81..a9626723b05ad 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts @@ -70,19 +70,48 @@ const escapeSpecialCharacters = (val: string) => val.replace(/["]/g, '\\$&'); // export const escapeKuery = flow(escapeSpecialCharacters, escapeWhitespace); +/** + * Deprecated in leiu of `convertToBuildEsQueryOrError` + */ export const convertToBuildEsQuery = ({ config, indexPattern, queries, filters, - handleError, }: { config: EsQueryConfig; indexPattern: IIndexPattern; queries: Query[]; filters: Filter[]; - handleError: (error: Error) => void; }) => { + try { + return JSON.stringify( + esQuery.buildEsQuery( + indexPattern, + queries, + filters.filter((f) => f.meta.disabled === false), + { + ...config, + dateFormatTZ: undefined, + } + ) + ); + } catch (exp) { + return ''; + } +}; + +export const convertToBuildEsQueryOrError = ({ + config, + indexPattern, + queries, + filters, +}: { + config: EsQueryConfig; + indexPattern: IIndexPattern; + queries: Query[]; + filters: Filter[]; +}): string | Error => { try { return JSON.stringify( esQuery.buildEsQuery( @@ -96,6 +125,6 @@ export const convertToBuildEsQuery = ({ ) ); } catch (error) { - handleError(error); + return error; } }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx index a3efbec963f43..434e6c08a09a5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx @@ -15,7 +15,7 @@ import { getTableSkipFocus, stopPropagationAndPreventDefault, } from '../../../../../timelines/public'; -import { escapeQueryValue, convertToBuildEsQuery } from '../../../common/lib/keury'; +import { escapeQueryValue, convertToBuildEsQueryOrError } from '../../../common/lib/keury'; import { DataProvider, @@ -32,7 +32,6 @@ import { } from '../../../../../../../src/plugins/data/public'; import { EVENTS_TABLE_CLASS_NAME } from './styles'; -import { UseAppToasts } from '../../../common/hooks/use_app_toasts'; const isNumber = (value: string | number) => !isNaN(Number(value)); @@ -144,6 +143,26 @@ export const buildGlobalQuery = (dataProviders: DataProvider[], browserFields: B return !index ? `(${queryMatch})` : `${globalQuery} or (${queryMatch})`; }, ''); +/** + * Abstracted out due to complexity issues in `combineQueries` + * Returns the query object or handles the Error and returns `null` + */ +const narrowQueryOrError = ( + query: string | Error, + handleError?: (error: Error) => void +): { filterQuery: string } | null => { + if (typeof query === 'string') { + return { + filterQuery: query, + }; + } + if (handleError) { + handleError(query); + } + return null; +}; + +// TODO: doc comment this export const combineQueries = ({ config, dataProviders, @@ -153,7 +172,7 @@ export const combineQueries = ({ kqlQuery, kqlMode, isEventViewer, - addError, + handleError, }: { config: EsQueryConfig; dataProviders: DataProvider[]; @@ -163,73 +182,54 @@ export const combineQueries = ({ kqlQuery: Query; kqlMode: string; isEventViewer?: boolean; - addError: UseAppToasts['addError']; + handleError?: (error: Error) => void; }): { filterQuery: string } | null => { - const handleError = (error: Error) => { - addError(error, { title: error.name }); - }; const kuery: Query = { query: '', language: kqlQuery.language }; if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEmpty(filters) && !isEventViewer) { return null; - } else if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEventViewer) { - return { - filterQuery: - convertToBuildEsQuery({ - config, - queries: [kuery], - indexPattern, - filters, - handleError, - }) || '', - }; - } else if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && !isEmpty(filters)) { - return { - filterQuery: convertToBuildEsQuery({ - config, - queries: [kuery], - indexPattern, - filters, - handleError, - }), - }; + } else if ( + isEmpty(dataProviders) && + isEmpty(kqlQuery.query) && + (isEventViewer || !isEmpty(filters)) + ) { + const query = convertToBuildEsQueryOrError({ + config, + queries: [kuery], + indexPattern, + filters, + }); + return narrowQueryOrError(query, handleError); } else if (isEmpty(dataProviders) && !isEmpty(kqlQuery.query)) { kuery.query = `(${kqlQuery.query})`; - return { - filterQuery: convertToBuildEsQuery({ - config, - queries: [kuery], - indexPattern, - filters, - handleError, - }), - }; + const query = convertToBuildEsQueryOrError({ + config, + queries: [kuery], + indexPattern, + filters, + }); + return narrowQueryOrError(query, handleError); } else if (!isEmpty(dataProviders) && isEmpty(kqlQuery)) { kuery.query = `(${buildGlobalQuery(dataProviders, browserFields)})`; - return { - filterQuery: - convertToBuildEsQuery({ - config, - queries: [kuery], - indexPattern, - filters, - handleError, - }) || '', - }; + const query = convertToBuildEsQueryOrError({ + config, + queries: [kuery], + indexPattern, + filters, + }); + return narrowQueryOrError(query, handleError); } const operatorKqlQuery = kqlMode === 'filter' ? 'and' : 'or'; const postpend = (q: string) => `${!isEmpty(q) ? ` ${operatorKqlQuery} (${q})` : ''}`; kuery.query = `((${buildGlobalQuery(dataProviders, browserFields)})${postpend( kqlQuery.query as string )})`; - return { - filterQuery: convertToBuildEsQuery({ - config, - queries: [kuery], - indexPattern, - filters, - handleError, - }), - }; + const query = convertToBuildEsQueryOrError({ + config, + queries: [kuery], + indexPattern, + filters, + }); + return narrowQueryOrError(query, handleError); }; /** diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index f3dbc335caf6d..20566c80c185e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -199,7 +199,6 @@ export const QueryTabContentComponent: React.FC = ({ query: string; language: KueryFilterQueryKind; } = { query: kqlQueryExpression, language: 'kuery' }; - const { addError } = useAppToasts(); const combinedQueries = combineQueries({ config: esQueryConfig, @@ -209,7 +208,6 @@ export const QueryTabContentComponent: React.FC = ({ filters, kqlQuery, kqlMode, - addError, }); const isBlankTimeline: boolean = From 5da4d11bb9827a1b659cad56c65e6013da412309 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Thu, 6 May 2021 00:12:23 -0600 Subject: [PATCH 03/20] adds hook for handling error bubbling --- .../events_viewer/events_viewer.tsx | 14 +--- .../common/hooks/use_invalid_filter_query.tsx | 40 +++++++++ .../public/common/lib/keury/index.ts | 4 +- .../components/alerts_table/index.tsx | 24 +++--- .../hosts/components/kpi_hosts/types.ts | 2 +- .../public/hosts/pages/details/index.tsx | 9 +++ .../public/hosts/pages/details/types.ts | 2 +- .../public/hosts/pages/hosts.tsx | 13 ++- .../public/hosts/pages/navigation/types.ts | 2 +- .../network/components/kpi_network/types.ts | 2 +- .../public/network/pages/details/index.tsx | 25 ++++-- .../public/network/pages/details/types.ts | 2 +- .../public/network/pages/network.tsx | 11 ++- .../components/alerts_by_category/index.tsx | 9 +++ .../components/event_counts/index.tsx | 17 ++++ .../components/events_by_dataset/index.tsx | 9 +++ .../network_details/expandable_network.tsx | 11 ++- .../timelines/components/timeline/helpers.tsx | 81 +++++++------------ .../timeline/query_tab_content/index.tsx | 12 ++- 19 files changed, 191 insertions(+), 98 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 12d2807cdfabc..115731d3650ca 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -6,7 +6,7 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; @@ -55,7 +55,6 @@ import { defaultControlColumn, ControlColumnProps, } from '../../../timelines/components/timeline/body/control_columns'; -import { useAppToasts } from '../../hooks/use_app_toasts'; export const EVENTS_VIEWER_HEADER_HEIGHT = 90; // px const UTILITY_BAR_HEIGHT = 19; // px @@ -176,14 +175,6 @@ const EventsViewerComponent: React.FC = ({ const kibana = useKibana(); const [isQueryLoading, setIsQueryLoading] = useState(false); const { getManageTimelineById, setIsTimelineLoading } = useManageTimeline(); - const { addError } = useAppToasts(); - - const handleQueryError = useCallback( - (error: Error) => { - addError(error, { title: error.name }); - }, - [addError] - ); useEffect(() => { dispatch(timelineActions.updateIsLoading({ id, isLoading: isQueryLoading })); @@ -216,7 +207,6 @@ const EventsViewerComponent: React.FC = ({ kqlQuery: query, kqlMode, isEventViewer: true, - handleError: handleQueryError, }); const canQueryTimeline = useMemo( @@ -257,7 +247,7 @@ const EventsViewerComponent: React.FC = ({ sort: sortField, startDate: start, endDate: end, - skip: !canQueryTimeline, + skip: !canQueryTimeline || combinedQueries?.filterQuery === undefined, }); const totalCountMinusDeleted = useMemo( diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx new file mode 100644 index 0000000000000..e540f06dc6ce0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect } from 'react'; +import { EsQueryConfig, Filter, IIndexPattern, Query } from 'src/plugins/data/public'; +import { convertToBuildEsQueryOrError } from '../lib/keury'; +import { useAppToasts } from './use_app_toasts'; + +export const useInvalidFilterQuery = ({ + filterQuery, + config, + indexPattern, + queries, + filters, +}: { + filterQuery?: string; + config: EsQueryConfig; + indexPattern: IIndexPattern; + queries: Query[]; + filters: Filter[]; +}) => { + const { addError } = useAppToasts(); + + useEffect(() => { + if (filterQuery === undefined) { + const error = convertToBuildEsQueryOrError({ + config, + indexPattern, + queries, + filters, + }) as Error; + addError(error, { title: error.name }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filterQuery, addError]); +}; diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts index a9626723b05ad..90ffadd8ea0a8 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts @@ -83,7 +83,7 @@ export const convertToBuildEsQuery = ({ indexPattern: IIndexPattern; queries: Query[]; filters: Filter[]; -}) => { +}): string | undefined => { try { return JSON.stringify( esQuery.buildEsQuery( @@ -97,7 +97,7 @@ export const convertToBuildEsQuery = ({ ) ); } catch (exp) { - return ''; + return undefined; } }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index b7a0270c4747b..c4e260205d2be 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -51,6 +51,7 @@ import { useSourcererScope } from '../../../common/containers/sourcerer'; import { buildTimeRangeFilter } from './helpers'; import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers'; import { columns, RenderCellValue } from '../../configurations/security_solution_detections'; +import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; interface OwnProps { defaultFilters?: Filter[]; @@ -104,7 +105,7 @@ export const AlertsTableComponent: React.FC = ({ } = useSourcererScope(SourcererScopeName.detections); const kibana = useKibana(); const [, dispatchToaster] = useStateToaster(); - const { addWarning, addError } = useAppToasts(); + const { addWarning } = useAppToasts(); const { initializeTimeline, setSelectAll } = useManageTimeline(); // TODO: Once we are past experimental phase this code should be removed const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled'); @@ -126,24 +127,21 @@ export const AlertsTableComponent: React.FC = ({ kqlQuery: globalQuery, kqlMode: globalQuery.language, isEventViewer: true, - addError, }); } return null; }, - [ - browserFields, - defaultFilters, - globalFilters, - globalQuery, - indexPatterns, - kibana, - to, - from, - addError, - ] + [browserFields, defaultFilters, globalFilters, globalQuery, indexPatterns, kibana, to, from] ); + useInvalidFilterQuery({ + filterQuery: getGlobalQuery([])?.filterQuery, + config: esQuery.getEsQueryConfig(kibana.services.uiSettings), + indexPattern: indexPatterns, + queries: [globalQuery], + filters: [...(defaultFilters ?? []), ...globalFilters, ...buildTimeRangeFilter(from, to)], + }); + const setEventsLoadingCallback = useCallback( ({ eventIds, isLoading }: SetEventsLoadingProps) => { setEventsLoading!({ id: timelineId, eventIds, isLoading }); diff --git a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/types.ts b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/types.ts index 71b4c2322c1fb..031e2c905fda2 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/hosts/components/kpi_hosts/types.ts @@ -9,7 +9,7 @@ import { UpdateDateRange } from '../../../common/components/charts/common'; import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; export interface HostsKpiProps { - filterQuery: string; + filterQuery?: string; from: string; to: string; indexNames: string[]; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx index 22edd2c19d6bd..1720ffa3f21d3 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx @@ -51,6 +51,7 @@ import { useSourcererScope } from '../../../common/containers/sourcerer'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { useHostDetails } from '../../containers/hosts/details'; import { manageQuery } from '../../../common/components/page/manage_query'; +import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; const HostOverviewManage = manageQuery(HostOverview); @@ -110,6 +111,14 @@ const HostDetailsComponent: React.FC = ({ detailName, hostDeta filters: getFilters(), }); + useInvalidFilterQuery({ + filterQuery, + config: esQuery.getEsQueryConfig(kibana.services.uiSettings), + indexPattern, + queries: [query], + filters: getFilters(), + }); + useEffect(() => { dispatch(setHostDetailsTablesActivePageToZero()); }, [dispatch, detailName]); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/types.ts b/x-pack/plugins/security_solution/public/hosts/pages/details/types.ts index 447fba4cd182a..21924fe929320 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/types.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/types.ts @@ -61,7 +61,7 @@ export type HostDetailsTabsProps = HostBodyComponentDispatchProps & docValueFields?: DocValueFields[]; indexNames: string[]; pageFilters?: Filter[]; - filterQuery: string; + filterQuery?: string; indexPattern: IIndexPattern; type: hostsModel.HostsType; }; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index 7d31d291e75f1..43383b4f58bc5 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -52,6 +52,7 @@ import { timelineSelectors } from '../../timelines/store/timeline'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; import { useSourcererScope } from '../../common/containers/sourcerer'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../common/hooks/use_selector'; +import { useInvalidFilterQuery } from '../../common/hooks/use_invalid_filter_query'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -131,6 +132,14 @@ const HostsComponent = () => { [indexPattern, query, tabsFilters, uiSettings] ); + useInvalidFilterQuery({ + filterQuery, + config: esQuery.getEsQueryConfig(uiSettings), + indexPattern, + queries: [query], + filters, + }); + const onSkipFocusBeforeEventsTable = useCallback(() => { containerElement.current ?.querySelector('.inspectButtonComponent:last-of-type') @@ -183,7 +192,7 @@ const HostsComponent = () => { from={from} setQuery={setQuery} to={to} - skip={isInitializing} + skip={isInitializing || !filterQuery} narrowDateRange={narrowDateRange} /> @@ -200,7 +209,7 @@ const HostsComponent = () => { deleteQuery={deleteQuery} docValueFields={docValueFields} to={to} - filterQuery={tabsFilterQuery} + filterQuery={tabsFilterQuery || ''} // TODO: is this the right thing to do isInitializing={isInitializing} indexNames={selectedPatterns} setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/types.ts b/x-pack/plugins/security_solution/public/hosts/pages/navigation/types.ts index a35e93812cbca..c051d85f05563 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/types.ts @@ -44,7 +44,7 @@ export type HostsComponentsQueryProps = QueryTabBodyProps & { }; export type AlertsComponentQueryProps = HostsComponentsQueryProps & { - filterQuery: string; + filterQuery?: string; pageFilters?: Filter[]; }; diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/types.ts b/x-pack/plugins/security_solution/public/network/components/kpi_network/types.ts index ff6f40b8c3a67..3be0177557712 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/types.ts +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/types.ts @@ -9,7 +9,7 @@ import { UpdateDateRange } from '../../../common/components/charts/common'; import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; export interface NetworkKpiProps { - filterQuery: string; + filterQuery?: string; from: string; indexNames: string[]; to: string; diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx index 02be5f78261c1..d361ef8f9fa20 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx @@ -49,6 +49,7 @@ import { esQuery } from '../../../../../../../src/plugins/data/public'; import { networkModel } from '../../store'; import { SecurityPageName } from '../../../app/types'; import { useSourcererScope } from '../../../common/containers/sourcerer'; +import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; export { getBreadcrumbs } from './utils'; const NetworkDetailsManage = manageQuery(IpOverview); @@ -100,6 +101,14 @@ const NetworkDetailsComponent: React.FC = () => { filters, }); + useInvalidFilterQuery({ + filterQuery, + config: esQuery.getEsQueryConfig(uiSettings), + indexPattern, + queries: [query], + filters, + }); + const [loading, { id, inspect, networkDetails, refetch }] = useNetworkDetails({ docValueFields, skip: isInitializing, @@ -174,7 +183,7 @@ const NetworkDetailsComponent: React.FC = () => { flowTarget={FlowTargetSourceDest.source} indexNames={selectedPatterns} ip={ip} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} startDate={from} type={type} setQuery={setQuery} @@ -189,7 +198,7 @@ const NetworkDetailsComponent: React.FC = () => { filterQuery={filterQuery} indexNames={selectedPatterns} ip={ip} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} startDate={from} type={type} setQuery={setQuery} @@ -208,7 +217,7 @@ const NetworkDetailsComponent: React.FC = () => { flowTarget={FlowTargetSourceDest.source} indexNames={selectedPatterns} ip={ip} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} startDate={from} type={type} setQuery={setQuery} @@ -223,7 +232,7 @@ const NetworkDetailsComponent: React.FC = () => { filterQuery={filterQuery} indexNames={selectedPatterns} ip={ip} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} startDate={from} type={type} setQuery={setQuery} @@ -240,7 +249,7 @@ const NetworkDetailsComponent: React.FC = () => { flowTarget={flowTarget} indexNames={selectedPatterns} ip={ip} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} startDate={from} type={type} setQuery={setQuery} @@ -253,7 +262,7 @@ const NetworkDetailsComponent: React.FC = () => { filterQuery={filterQuery} indexNames={selectedPatterns} ip={ip} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} startDate={from} type={type} setQuery={setQuery} @@ -268,7 +277,7 @@ const NetworkDetailsComponent: React.FC = () => { indexNames={selectedPatterns} ip={ip} setQuery={setQuery} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} startDate={from} type={type} /> @@ -280,7 +289,7 @@ const NetworkDetailsComponent: React.FC = () => { setQuery={setQuery} startDate={from} endDate={to} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} indexNames={selectedPatterns} ip={ip} type={type} diff --git a/x-pack/plugins/security_solution/public/network/pages/details/types.ts b/x-pack/plugins/security_solution/public/network/pages/details/types.ts index 759373d495f53..b91a22cbd5fc3 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/types.ts +++ b/x-pack/plugins/security_solution/public/network/pages/details/types.ts @@ -21,7 +21,7 @@ export interface OwnProps { type: NetworkType; startDate: string; endDate: string; - filterQuery: string | ESTermQuery; + filterQuery?: string | ESTermQuery; ip: string; indexNames: string[]; skip: boolean; diff --git a/x-pack/plugins/security_solution/public/network/pages/network.tsx b/x-pack/plugins/security_solution/public/network/pages/network.tsx index b08a75215a408..085d59e838c5a 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.tsx @@ -51,6 +51,7 @@ import { TimelineId } from '../../../common/types/timeline'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; import { useSourcererScope } from '../../common/containers/sourcerer'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../common/hooks/use_selector'; +import { useInvalidFilterQuery } from '../../common/hooks/use_invalid_filter_query'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -146,6 +147,14 @@ const NetworkComponent = React.memo( filters: tabsFilters, }); + useInvalidFilterQuery({ + filterQuery, + config: esQuery.getEsQueryConfig(kibana.services.uiSettings), + indexPattern, + queries: [query], + filters, + }); + return ( <> {indicesExist ? ( @@ -184,7 +193,7 @@ const NetworkComponent = React.memo( indexNames={selectedPatterns} narrowDateRange={narrowDateRange} setQuery={setQuery} - skip={isInitializing} + skip={isInitializing || filterQuery === undefined} to={to} /> diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx index 98874c25e0ef8..9a84052b38c08 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx @@ -33,6 +33,7 @@ import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; import { SecurityPageName } from '../../../app/types'; import { useFormatUrl } from '../../../common/components/link_to'; import { LinkButton } from '../../../common/components/links'; +import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; const ID = 'alertsByCategoryOverview'; @@ -112,6 +113,14 @@ const AlertsByCategoryComponent: React.FC = ({ [filters, indexPattern, uiSettings, query] ); + useInvalidFilterQuery({ + filterQuery, + config: esQuery.getEsQueryConfig(uiSettings), + indexPattern, + queries: [query], + filters, + }); + useEffect(() => { return () => { if (deleteQuery) { diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx index 24c290edd4527..f040f327bfae1 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx @@ -22,6 +22,7 @@ import { Query, } from '../../../../../../../src/plugins/data/public'; import { GlobalTimeArgs } from '../../../common/containers/use_global_time'; +import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; const HorizontalSpacer = styled(EuiFlexItem)` width: 24px; @@ -67,6 +68,22 @@ const EventCountsComponent: React.FC = ({ [filters, indexPattern, uiSettings, query] ); + useInvalidFilterQuery({ + filterQuery: hostFilterQuery, + config: esQuery.getEsQueryConfig(uiSettings), + indexPattern, + queries: [query], + filters, + }); + + useInvalidFilterQuery({ + filterQuery: networkFilterQuery, + config: esQuery.getEsQueryConfig(uiSettings), + indexPattern, + queries: [query], + filters, + }); + return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index a6ebfd2bbe060..5bad923d9f089 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -36,6 +36,7 @@ import * as i18n from '../../pages/translations'; import { SecurityPageName } from '../../../app/types'; import { useFormatUrl } from '../../../common/components/link_to'; import { LinkButton } from '../../../common/components/links'; +import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; const DEFAULT_STACK_BY = 'event.dataset'; @@ -129,6 +130,14 @@ const EventsByDatasetComponent: React.FC = ({ [combinedQueries, kibana, indexPattern, query, filters] ); + useInvalidFilterQuery({ + filterQuery, + config: esQuery.getEsQueryConfig(kibana.services.uiSettings), + indexPattern, + queries: [query], + filters, + }); + const eventsByDatasetHistogramConfigs: MatrixHistogramConfigs = useMemo( () => ({ ...histogramConfigs, diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx index 19f6e2c9652f9..ead9ec6eb3646 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import React, { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; +import { useInvalidFilterQuery } from '../../../../common/hooks/use_invalid_filter_query'; import { FlowTarget } from '../../../../../common/search_strategy'; import { NetworkDetailsLink } from '../../../../common/components/links'; import { IpOverview } from '../../../../network/components/details'; @@ -104,9 +105,17 @@ export const ExpandableNetworkDetails = ({ filters, }); + useInvalidFilterQuery({ + filterQuery, + config: esQuery.getEsQueryConfig(uiSettings), + indexPattern, + queries: [query], + filters, + }); + const [loading, { id, networkDetails }] = useNetworkDetails({ docValueFields, - skip: isInitializing, + skip: isInitializing || filterQuery === undefined, filterQuery, indexNames: selectedPatterns, ip, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx index 434e6c08a09a5..dbfa04fd7cd6a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx @@ -15,7 +15,7 @@ import { getTableSkipFocus, stopPropagationAndPreventDefault, } from '../../../../../timelines/public'; -import { escapeQueryValue, convertToBuildEsQueryOrError } from '../../../common/lib/keury'; +import { escapeQueryValue, convertToBuildEsQuery } from '../../../common/lib/keury'; import { DataProvider, @@ -143,26 +143,6 @@ export const buildGlobalQuery = (dataProviders: DataProvider[], browserFields: B return !index ? `(${queryMatch})` : `${globalQuery} or (${queryMatch})`; }, ''); -/** - * Abstracted out due to complexity issues in `combineQueries` - * Returns the query object or handles the Error and returns `null` - */ -const narrowQueryOrError = ( - query: string | Error, - handleError?: (error: Error) => void -): { filterQuery: string } | null => { - if (typeof query === 'string') { - return { - filterQuery: query, - }; - } - if (handleError) { - handleError(query); - } - return null; -}; - -// TODO: doc comment this export const combineQueries = ({ config, dataProviders, @@ -172,7 +152,6 @@ export const combineQueries = ({ kqlQuery, kqlMode, isEventViewer, - handleError, }: { config: EsQueryConfig; dataProviders: DataProvider[]; @@ -182,8 +161,7 @@ export const combineQueries = ({ kqlQuery: Query; kqlMode: string; isEventViewer?: boolean; - handleError?: (error: Error) => void; -}): { filterQuery: string } | null => { +}): { filterQuery?: string } | null => { const kuery: Query = { query: '', language: kqlQuery.language }; if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEmpty(filters) && !isEventViewer) { return null; @@ -192,44 +170,43 @@ export const combineQueries = ({ isEmpty(kqlQuery.query) && (isEventViewer || !isEmpty(filters)) ) { - const query = convertToBuildEsQueryOrError({ - config, - queries: [kuery], - indexPattern, - filters, - }); - return narrowQueryOrError(query, handleError); + return { + filterQuery: convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + }), + }; } else if (isEmpty(dataProviders) && !isEmpty(kqlQuery.query)) { kuery.query = `(${kqlQuery.query})`; - const query = convertToBuildEsQueryOrError({ - config, - queries: [kuery], - indexPattern, - filters, - }); - return narrowQueryOrError(query, handleError); + return { + filterQuery: convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + }), + }; } else if (!isEmpty(dataProviders) && isEmpty(kqlQuery)) { kuery.query = `(${buildGlobalQuery(dataProviders, browserFields)})`; - const query = convertToBuildEsQueryOrError({ - config, - queries: [kuery], - indexPattern, - filters, - }); - return narrowQueryOrError(query, handleError); + return { + filterQuery: convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + }), + }; } const operatorKqlQuery = kqlMode === 'filter' ? 'and' : 'or'; const postpend = (q: string) => `${!isEmpty(q) ? ` ${operatorKqlQuery} (${q})` : ''}`; kuery.query = `((${buildGlobalQuery(dataProviders, browserFields)})${postpend( kqlQuery.query as string )})`; - const query = convertToBuildEsQueryOrError({ - config, - queries: [kuery], - indexPattern, - filters, - }); - return narrowQueryOrError(query, handleError); + return { + filterQuery: convertToBuildEsQuery({ config, queries: [kuery], indexPattern, filters }), + }; }; /** diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index 20566c80c185e..08fa496ce0c6d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -21,7 +21,7 @@ import { connect, ConnectedProps, useDispatch } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { InPortal } from 'react-reverse-portal'; -import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { useInvalidFilterQuery } from '../../../../common/hooks/use_invalid_filter_query'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; import { CellValueElementProps } from '../cell_rendering'; import { Direction, TimelineItem } from '../../../../../common/search_strategy'; @@ -210,6 +210,14 @@ export const QueryTabContentComponent: React.FC = ({ kqlMode, }); + useInvalidFilterQuery({ + filterQuery: combinedQueries?.filterQuery, + config: esQueryConfig, + indexPattern, + queries: [kqlQuery], + filters, + }); + const isBlankTimeline: boolean = isEmpty(dataProviders) && isEmpty(filters) && isEmpty(kqlQuery.query); @@ -255,7 +263,7 @@ export const QueryTabContentComponent: React.FC = ({ limit: itemsPerPage, filterQuery: combinedQueries?.filterQuery ?? '', startDate: start, - skip: !canQueryTimeline(), + skip: !canQueryTimeline() || combinedQueries?.filterQuery === undefined, sort: timelineQuerySortField, timerangeKind, }); From f7c159e22fb86c180ca422d30a2d7234e1b8c656 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 11 May 2021 01:27:04 -0600 Subject: [PATCH 04/20] updates to a tuple --- .../common/hooks/use_invalid_filter_query.tsx | 26 ++---- .../public/common/lib/keury/index.ts | 87 ++++++++++--------- .../components/alerts_table/index.tsx | 4 - .../detection_engine/alerts/use_query.tsx | 1 - .../public/hosts/pages/details/index.tsx | 10 +-- .../public/hosts/pages/hosts.tsx | 12 +-- .../public/network/pages/details/index.tsx | 10 +-- .../public/network/pages/network.tsx | 12 +-- .../components/alerts_by_category/index.tsx | 10 +-- .../components/event_counts/index.tsx | 20 +---- .../components/events_by_dataset/index.tsx | 10 +-- .../network_details/expandable_network.tsx | 10 +-- .../timelines/components/timeline/helpers.tsx | 47 ++++++---- .../timeline/query_tab_content/index.tsx | 8 +- 14 files changed, 102 insertions(+), 165 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx index e540f06dc6ce0..7fcfa4b02a194 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -6,35 +6,25 @@ */ import { useEffect } from 'react'; -import { EsQueryConfig, Filter, IIndexPattern, Query } from 'src/plugins/data/public'; -import { convertToBuildEsQueryOrError } from '../lib/keury'; import { useAppToasts } from './use_app_toasts'; +/** + * Adds a toast error message whenever invalid KQL is submitted through the search bar + */ export const useInvalidFilterQuery = ({ filterQuery, - config, - indexPattern, - queries, - filters, + kqlError, }: { filterQuery?: string; - config: EsQueryConfig; - indexPattern: IIndexPattern; - queries: Query[]; - filters: Filter[]; + kqlError?: Error; }) => { const { addError } = useAppToasts(); useEffect(() => { - if (filterQuery === undefined) { - const error = convertToBuildEsQueryOrError({ - config, - indexPattern, - queries, - filters, - }) as Error; - addError(error, { title: error.name }); + if (filterQuery === undefined && kqlError != null) { + addError(kqlError, { title: kqlError.name }); } + // This disable is required to only trigger the toast once per render // eslint-disable-next-line react-hooks/exhaustive-deps }, [filterQuery, addError]); }; diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts index 90ffadd8ea0a8..ea2ccaaf336a7 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts @@ -71,7 +71,7 @@ const escapeSpecialCharacters = (val: string) => val.replace(/["]/g, '\\$&'); // export const escapeKuery = flow(escapeSpecialCharacters, escapeWhitespace); /** - * Deprecated in leiu of `convertToBuildEsQueryOrError` + * Deprecated in lieu of `convertToBuildEsQueryOrError` */ export const convertToBuildEsQuery = ({ config, @@ -83,48 +83,51 @@ export const convertToBuildEsQuery = ({ indexPattern: IIndexPattern; queries: Query[]; filters: Filter[]; -}): string | undefined => { +}): [string | undefined, Error | undefined] => { try { - return JSON.stringify( - esQuery.buildEsQuery( - indexPattern, - queries, - filters.filter((f) => f.meta.disabled === false), - { - ...config, - dateFormatTZ: undefined, - } - ) - ); - } catch (exp) { - return undefined; - } -}; - -export const convertToBuildEsQueryOrError = ({ - config, - indexPattern, - queries, - filters, -}: { - config: EsQueryConfig; - indexPattern: IIndexPattern; - queries: Query[]; - filters: Filter[]; -}): string | Error => { - try { - return JSON.stringify( - esQuery.buildEsQuery( - indexPattern, - queries, - filters.filter((f) => f.meta.disabled === false), - { - ...config, - dateFormatTZ: undefined, - } - ) - ); + return [ + JSON.stringify( + esQuery.buildEsQuery( + indexPattern, + queries, + filters.filter((f) => f.meta.disabled === false), + { + ...config, + dateFormatTZ: undefined, + } + ) + ), + undefined, + ]; } catch (error) { - return error; + return [undefined, error]; } }; + +// export const convertToBuildEsQueryOrError = ({ +// config, +// indexPattern, +// queries, +// filters, +// }: { +// config: EsQueryConfig; +// indexPattern: IIndexPattern; +// queries: Query[]; +// filters: Filter[]; +// }): string | Error => { +// try { +// return JSON.stringify( +// esQuery.buildEsQuery( +// indexPattern, +// queries, +// filters.filter((f) => f.meta.disabled === false), +// { +// ...config, +// dateFormatTZ: undefined, +// } +// ) +// ); +// } catch (error) { +// return error; +// } +// }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index c4e260205d2be..89ec56cb4c66c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -136,10 +136,6 @@ export const AlertsTableComponent: React.FC = ({ useInvalidFilterQuery({ filterQuery: getGlobalQuery([])?.filterQuery, - config: esQuery.getEsQueryConfig(kibana.services.uiSettings), - indexPattern: indexPatterns, - queries: [globalQuery], - filters: [...(defaultFilters ?? []), ...globalFilters, ...buildTimeRangeFilter(from, to)], }); const setEventsLoadingCallback = useCallback( diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_query.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_query.tsx index 88be60ff1d1ad..4d7d80b74a24d 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_query.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_query.tsx @@ -50,7 +50,6 @@ export const useQueryAlerts = ({ refetch: null, }); const [loading, setLoading] = useState(false); - console.log('Query', query); useEffect(() => { let isSubscribed = true; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx index 1720ffa3f21d3..9ea6dc943b57c 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx @@ -104,20 +104,14 @@ const HostDetailsComponent: React.FC = ({ detailName, hostDeta indexNames: selectedPatterns, skip: selectedPatterns.length === 0, }); - const filterQuery = convertToBuildEsQuery({ + const [filterQuery, kqlError] = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), indexPattern, queries: [query], filters: getFilters(), }); - useInvalidFilterQuery({ - filterQuery, - config: esQuery.getEsQueryConfig(kibana.services.uiSettings), - indexPattern, - queries: [query], - filters: getFilters(), - }); + useInvalidFilterQuery({ filterQuery, kqlError }); useEffect(() => { dispatch(setHostDetailsTablesActivePageToZero()); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index 43383b4f58bc5..ce73a5ffb1632 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -111,7 +111,7 @@ const HostsComponent = () => { [dispatch] ); const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); - const filterQuery = useMemo( + const [filterQuery, kqlError] = useMemo( () => convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), @@ -121,7 +121,7 @@ const HostsComponent = () => { }), [filters, indexPattern, uiSettings, query] ); - const tabsFilterQuery = useMemo( + const [tabsFilterQuery] = useMemo( () => convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), @@ -132,13 +132,7 @@ const HostsComponent = () => { [indexPattern, query, tabsFilters, uiSettings] ); - useInvalidFilterQuery({ - filterQuery, - config: esQuery.getEsQueryConfig(uiSettings), - indexPattern, - queries: [query], - filters, - }); + useInvalidFilterQuery({ filterQuery, kqlError }); const onSkipFocusBeforeEventsTable = useCallback(() => { containerElement.current diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx index d361ef8f9fa20..4be4d3f56c11c 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx @@ -94,20 +94,14 @@ const NetworkDetailsComponent: React.FC = () => { const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); const ip = decodeIpv6(detailName); - const filterQuery = convertToBuildEsQuery({ + const [filterQuery, kqlError] = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), indexPattern, queries: [query], filters, }); - useInvalidFilterQuery({ - filterQuery, - config: esQuery.getEsQueryConfig(uiSettings), - indexPattern, - queries: [query], - filters, - }); + useInvalidFilterQuery({ filterQuery, kqlError }); const [loading, { id, inspect, networkDetails, refetch }] = useNetworkDetails({ docValueFields, diff --git a/x-pack/plugins/security_solution/public/network/pages/network.tsx b/x-pack/plugins/security_solution/public/network/pages/network.tsx index 085d59e838c5a..205a8a791c4b6 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.tsx @@ -134,26 +134,20 @@ const NetworkComponent = React.memo( [containerElement, onSkipFocusBeforeEventsTable, onSkipFocusAfterEventsTable] ); - const filterQuery = convertToBuildEsQuery({ + const [filterQuery, kqlError] = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), indexPattern, queries: [query], filters, }); - const tabsFilterQuery = convertToBuildEsQuery({ + const [tabsFilterQuery] = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), indexPattern, queries: [query], filters: tabsFilters, }); - useInvalidFilterQuery({ - filterQuery, - config: esQuery.getEsQueryConfig(kibana.services.uiSettings), - indexPattern, - queries: [query], - filters, - }); + useInvalidFilterQuery({ filterQuery, kqlError }); return ( <> diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx index 9a84052b38c08..58089aa323046 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx @@ -102,7 +102,7 @@ const AlertsByCategoryComponent: React.FC = ({ [] ); - const filterQuery = useMemo( + const [filterQuery, kqlError] = useMemo( () => convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), @@ -113,13 +113,7 @@ const AlertsByCategoryComponent: React.FC = ({ [filters, indexPattern, uiSettings, query] ); - useInvalidFilterQuery({ - filterQuery, - config: esQuery.getEsQueryConfig(uiSettings), - indexPattern, - queries: [query], - filters, - }); + useInvalidFilterQuery({ filterQuery, kqlError }); useEffect(() => { return () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx index f040f327bfae1..a74427368bdb0 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx @@ -46,7 +46,7 @@ const EventCountsComponent: React.FC = ({ }) => { const { uiSettings } = useKibana().services; - const hostFilterQuery = useMemo( + const [hostFilterQuery, hostKqlError] = useMemo( () => convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), @@ -57,7 +57,7 @@ const EventCountsComponent: React.FC = ({ [filters, indexPattern, query, uiSettings] ); - const networkFilterQuery = useMemo( + const [networkFilterQuery, networkKqlError] = useMemo( () => convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), @@ -68,21 +68,9 @@ const EventCountsComponent: React.FC = ({ [filters, indexPattern, uiSettings, query] ); - useInvalidFilterQuery({ - filterQuery: hostFilterQuery, - config: esQuery.getEsQueryConfig(uiSettings), - indexPattern, - queries: [query], - filters, - }); + useInvalidFilterQuery({ filterQuery: hostFilterQuery, kqlError: hostKqlError }); - useInvalidFilterQuery({ - filterQuery: networkFilterQuery, - config: esQuery.getEsQueryConfig(uiSettings), - indexPattern, - queries: [query], - filters, - }); + useInvalidFilterQuery({ filterQuery: networkFilterQuery, kqlError: networkKqlError }); return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index 5bad923d9f089..f9a5e9fdb4f65 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -117,7 +117,7 @@ const EventsByDatasetComponent: React.FC = ({ [goToHostEvents, formatUrl] ); - const filterQuery = useMemo( + const [filterQuery, kqlError] = useMemo( () => combinedQueries == null ? convertToBuildEsQuery({ @@ -130,13 +130,7 @@ const EventsByDatasetComponent: React.FC = ({ [combinedQueries, kibana, indexPattern, query, filters] ); - useInvalidFilterQuery({ - filterQuery, - config: esQuery.getEsQueryConfig(kibana.services.uiSettings), - indexPattern, - queries: [query], - filters, - }); + // useInvalidFilterQuery({ filterQuery, kqlError }); const eventsByDatasetHistogramConfigs: MatrixHistogramConfigs = useMemo( () => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx index ead9ec6eb3646..46c46b5cf5b2f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx @@ -98,20 +98,14 @@ export const ExpandableNetworkDetails = ({ } = useKibana(); const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererScope(); - const filterQuery = convertToBuildEsQuery({ + const [filterQuery, kqlError] = convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), indexPattern, queries: [query], filters, }); - useInvalidFilterQuery({ - filterQuery, - config: esQuery.getEsQueryConfig(uiSettings), - indexPattern, - queries: [query], - filters, - }); + useInvalidFilterQuery({ filterQuery, kqlError }); const [loading, { id, networkDetails }] = useNetworkDetails({ docValueFields, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx index dbfa04fd7cd6a..75114e79480c2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx @@ -170,33 +170,36 @@ export const combineQueries = ({ isEmpty(kqlQuery.query) && (isEventViewer || !isEmpty(filters)) ) { + const [filterQuery, kqlError] = convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + }); return { - filterQuery: convertToBuildEsQuery({ - config, - queries: [kuery], - indexPattern, - filters, - }), + filterQuery, }; } else if (isEmpty(dataProviders) && !isEmpty(kqlQuery.query)) { kuery.query = `(${kqlQuery.query})`; + const [filterQuery, kqlError] = convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + }); return { - filterQuery: convertToBuildEsQuery({ - config, - queries: [kuery], - indexPattern, - filters, - }), + filterQuery, }; } else if (!isEmpty(dataProviders) && isEmpty(kqlQuery)) { kuery.query = `(${buildGlobalQuery(dataProviders, browserFields)})`; + const [filterQuery, kqlError] = convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + }); return { - filterQuery: convertToBuildEsQuery({ - config, - queries: [kuery], - indexPattern, - filters, - }), + filterQuery, }; } const operatorKqlQuery = kqlMode === 'filter' ? 'and' : 'or'; @@ -204,8 +207,14 @@ export const combineQueries = ({ kuery.query = `((${buildGlobalQuery(dataProviders, browserFields)})${postpend( kqlQuery.query as string )})`; + const [filterQuery, kqlError] = convertToBuildEsQuery({ + config, + queries: [kuery], + indexPattern, + filters, + }); return { - filterQuery: convertToBuildEsQuery({ config, queries: [kuery], indexPattern, filters }), + filterQuery, }; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index 08fa496ce0c6d..5e0953a2ccf52 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -210,13 +210,7 @@ export const QueryTabContentComponent: React.FC = ({ kqlMode, }); - useInvalidFilterQuery({ - filterQuery: combinedQueries?.filterQuery, - config: esQueryConfig, - indexPattern, - queries: [kqlQuery], - filters, - }); + useInvalidFilterQuery({ filterQuery: combinedQueries?.filterQuery }); const isBlankTimeline: boolean = isEmpty(dataProviders) && isEmpty(filters) && isEmpty(kqlQuery.query); From bf758903cd990c9f61b6833368b8b7d7b3ad6ba2 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 11 May 2021 16:32:38 -0600 Subject: [PATCH 05/20] adds tests and cleans up types --- .../events_viewer/events_viewer.tsx | 2 +- .../public/common/lib/keury/index.ts | 31 ------------- .../components/alerts_table/index.tsx | 1 + .../components/events_by_dataset/index.tsx | 25 +++++------ .../components/timeline/helpers.test.tsx | 44 ++++++++++++++----- .../timelines/components/timeline/helpers.tsx | 6 ++- .../timeline/query_tab_content/index.tsx | 5 ++- 7 files changed, 57 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 115731d3650ca..7b64ee1135610 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -247,7 +247,7 @@ const EventsViewerComponent: React.FC = ({ sort: sortField, startDate: start, endDate: end, - skip: !canQueryTimeline || combinedQueries?.filterQuery === undefined, + skip: !canQueryTimeline || combinedQueries?.filterQuery === undefined, // When the filterQuery comes back as undefined, it means an error has been thrown and the request should be skipped }); const totalCountMinusDeleted = useMemo( diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts index ea2ccaaf336a7..8b8a56bdeaa71 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts @@ -70,9 +70,6 @@ const escapeSpecialCharacters = (val: string) => val.replace(/["]/g, '\\$&'); // export const escapeKuery = flow(escapeSpecialCharacters, escapeWhitespace); -/** - * Deprecated in lieu of `convertToBuildEsQueryOrError` - */ export const convertToBuildEsQuery = ({ config, indexPattern, @@ -103,31 +100,3 @@ export const convertToBuildEsQuery = ({ return [undefined, error]; } }; - -// export const convertToBuildEsQueryOrError = ({ -// config, -// indexPattern, -// queries, -// filters, -// }: { -// config: EsQueryConfig; -// indexPattern: IIndexPattern; -// queries: Query[]; -// filters: Filter[]; -// }): string | Error => { -// try { -// return JSON.stringify( -// esQuery.buildEsQuery( -// indexPattern, -// queries, -// filters.filter((f) => f.meta.disabled === false), -// { -// ...config, -// dateFormatTZ: undefined, -// } -// ) -// ); -// } catch (error) { -// return error; -// } -// }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 89ec56cb4c66c..af525beba7ecc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -136,6 +136,7 @@ export const AlertsTableComponent: React.FC = ({ useInvalidFilterQuery({ filterQuery: getGlobalQuery([])?.filterQuery, + kqlError: getGlobalQuery([])?.kqlError, }); const setEventsLoadingCallback = useCallback( diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index f9a5e9fdb4f65..f9367cba99f9d 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -117,20 +117,19 @@ const EventsByDatasetComponent: React.FC = ({ [goToHostEvents, formatUrl] ); - const [filterQuery, kqlError] = useMemo( - () => - combinedQueries == null - ? convertToBuildEsQuery({ - config: esQuery.getEsQueryConfig(kibana.services.uiSettings), - indexPattern, - queries: [query], - filters, - }) - : combinedQueries, - [combinedQueries, kibana, indexPattern, query, filters] - ); + const [filterQuery, kqlError] = useMemo(() => { + if (combinedQueries == null) { + return convertToBuildEsQuery({ + config: esQuery.getEsQueryConfig(kibana.services.uiSettings), + indexPattern, + queries: [query], + filters, + }); + } + return [combinedQueries]; + }, [combinedQueries, kibana, indexPattern, query, filters]); - // useInvalidFilterQuery({ filterQuery, kqlError }); + useInvalidFilterQuery({ filterQuery, kqlError }); const eventsByDatasetHistogramConfigs: MatrixHistogramConfigs = useMemo( () => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx index 7e6dbe80dc7b0..678fd9d7a3cf5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.test.tsx @@ -305,7 +305,7 @@ describe('Combined Queries', () => { test('Only Data Provider', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -317,13 +317,14 @@ describe('Combined Queries', () => { expect(filterQuery).toEqual( '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}' ); + expect(kqlError).toBeUndefined(); }); test('Only Data Provider with timestamp (string input)', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].queryMatch.field = '@timestamp'; dataProviders[0].queryMatch.value = '2018-03-23T23:36:23.232Z'; - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -335,13 +336,14 @@ describe('Combined Queries', () => { expect(filterQuery).toMatchInlineSnapshot( `"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"range\\":{\\"@timestamp\\":{\\"gte\\":\\"1521848183232\\",\\"lte\\":\\"1521848183232\\"}}}],\\"minimum_should_match\\":1}}],\\"should\\":[],\\"must_not\\":[]}}"` ); + expect(kqlError).toBeUndefined(); }); test('Only Data Provider with timestamp (numeric input)', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].queryMatch.field = '@timestamp'; dataProviders[0].queryMatch.value = 1521848183232; - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -353,13 +355,14 @@ describe('Combined Queries', () => { expect(filterQuery).toMatchInlineSnapshot( `"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"range\\":{\\"@timestamp\\":{\\"gte\\":\\"1521848183232\\",\\"lte\\":\\"1521848183232\\"}}}],\\"minimum_should_match\\":1}}],\\"should\\":[],\\"must_not\\":[]}}"` ); + expect(kqlError).toBeUndefined(); }); test('Only Data Provider with a date type (string input)', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].queryMatch.field = 'event.end'; dataProviders[0].queryMatch.value = '2018-03-23T23:36:23.232Z'; - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -371,13 +374,14 @@ describe('Combined Queries', () => { expect(filterQuery).toMatchInlineSnapshot( `"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"event.end\\":\\"1521848183232\\"}}],\\"minimum_should_match\\":1}}],\\"should\\":[],\\"must_not\\":[]}}"` ); + expect(kqlError).toBeUndefined(); }); test('Only Data Provider with date type (numeric input)', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].queryMatch.field = 'event.end'; dataProviders[0].queryMatch.value = 1521848183232; - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -389,10 +393,11 @@ describe('Combined Queries', () => { expect(filterQuery).toMatchInlineSnapshot( `"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match\\":{\\"event.end\\":\\"1521848183232\\"}}],\\"minimum_should_match\\":1}}],\\"should\\":[],\\"must_not\\":[]}}"` ); + expect(kqlError).toBeUndefined(); }); test('Only KQL search/filter query', () => { - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders: [], indexPattern: mockIndexPattern, @@ -404,11 +409,26 @@ describe('Combined Queries', () => { expect(filterQuery).toEqual( '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}' ); + expect(kqlError).toBeUndefined(); + }); + + test('Invalid KQL search/filter query', () => { + const { filterQuery, kqlError } = combineQueries({ + config, + dataProviders: [], + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: 'host.name: "host-1', language: 'kuery' }, + kqlMode: 'search', + })!; + expect(filterQuery).toBeUndefined(); + expect(kqlError).toBeDefined(); // Not testing on the error message since we don't control changes to them }); test('Data Provider & KQL search query', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -420,11 +440,12 @@ describe('Combined Queries', () => { expect(filterQuery).toEqual( '{"bool":{"must":[],"filter":[{"bool":{"should":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}' ); + expect(kqlError).toBeUndefined(); }); test('Data Provider & KQL filter query', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -436,13 +457,14 @@ describe('Combined Queries', () => { expect(filterQuery).toEqual( '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}' ); + expect(kqlError).toBeUndefined(); }); test('Data Provider & KQL search query multiple', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -454,13 +476,14 @@ describe('Combined Queries', () => { expect(filterQuery).toMatchInlineSnapshot( `"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 1\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 3\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 4\\"}}],\\"minimum_should_match\\":1}}]}},{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 2\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 5\\"}}],\\"minimum_should_match\\":1}}]}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"host.name\\":\\"host-1\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}}],\\"should\\":[],\\"must_not\\":[]}}"` ); + expect(kqlError).toBeUndefined(); }); test('Data Provider & KQL filter query multiple', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); dataProviders[0].and = cloneDeep(mockDataProviders.slice(2, 4)); dataProviders[1].and = cloneDeep(mockDataProviders.slice(4, 5)); - const { filterQuery } = combineQueries({ + const { filterQuery, kqlError } = combineQueries({ config, dataProviders, indexPattern: mockIndexPattern, @@ -472,6 +495,7 @@ describe('Combined Queries', () => { expect(filterQuery).toMatchInlineSnapshot( `"{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 1\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 3\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 4\\"}}],\\"minimum_should_match\\":1}}]}},{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 2\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 5\\"}}],\\"minimum_should_match\\":1}}]}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"host.name\\":\\"host-1\\"}}],\\"minimum_should_match\\":1}}]}}],\\"should\\":[],\\"must_not\\":[]}}"` ); + expect(kqlError).toBeUndefined(); }); test('Data Provider & kql filter query with nested field that exists', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx index 75114e79480c2..7c38474d39dba 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/helpers.tsx @@ -161,7 +161,7 @@ export const combineQueries = ({ kqlQuery: Query; kqlMode: string; isEventViewer?: boolean; -}): { filterQuery?: string } | null => { +}): { filterQuery?: string; kqlError?: Error } | null => { const kuery: Query = { query: '', language: kqlQuery.language }; if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEmpty(filters) && !isEventViewer) { return null; @@ -178,6 +178,7 @@ export const combineQueries = ({ }); return { filterQuery, + kqlError, }; } else if (isEmpty(dataProviders) && !isEmpty(kqlQuery.query)) { kuery.query = `(${kqlQuery.query})`; @@ -189,6 +190,7 @@ export const combineQueries = ({ }); return { filterQuery, + kqlError, }; } else if (!isEmpty(dataProviders) && isEmpty(kqlQuery)) { kuery.query = `(${buildGlobalQuery(dataProviders, browserFields)})`; @@ -200,6 +202,7 @@ export const combineQueries = ({ }); return { filterQuery, + kqlError, }; } const operatorKqlQuery = kqlMode === 'filter' ? 'and' : 'or'; @@ -215,6 +218,7 @@ export const combineQueries = ({ }); return { filterQuery, + kqlError, }; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index 5e0953a2ccf52..cfd976bba7752 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -210,7 +210,10 @@ export const QueryTabContentComponent: React.FC = ({ kqlMode, }); - useInvalidFilterQuery({ filterQuery: combinedQueries?.filterQuery }); + useInvalidFilterQuery({ + filterQuery: combinedQueries?.filterQuery, + kqlError: combinedQueries?.kqlError, + }); const isBlankTimeline: boolean = isEmpty(dataProviders) && isEmpty(filters) && isEmpty(kqlQuery.query); From 03bb41b0a4373dd777dad1df3bb3c9e708e6e5cb Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 11 May 2021 18:45:36 -0600 Subject: [PATCH 06/20] removes todo --- x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index ce73a5ffb1632..d6ac156e40bda 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -203,7 +203,7 @@ const HostsComponent = () => { deleteQuery={deleteQuery} docValueFields={docValueFields} to={to} - filterQuery={tabsFilterQuery || ''} // TODO: is this the right thing to do + filterQuery={tabsFilterQuery || ''} isInitializing={isInitializing} indexNames={selectedPatterns} setAbsoluteRangeDatePicker={setAbsoluteRangeDatePicker} From ca15e22481209f53035aaa422a2a31e4e24217ee Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 11 May 2021 18:52:19 -0600 Subject: [PATCH 07/20] abstracts out skip logic --- .../public/network/pages/details/index.tsx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx index 4be4d3f56c11c..6637f457d2e88 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx @@ -123,6 +123,12 @@ const NetworkDetailsComponent: React.FC = () => { ip, ]); + // When the filterQuery comes back as undefined, it means an error has been thrown and the request should be skipped + const shouldSkip = useMemo(() => isInitializing || filterQuery === undefined, [ + isInitializing, + filterQuery, + ]); + return (
{indicesExist ? ( @@ -177,7 +183,7 @@ const NetworkDetailsComponent: React.FC = () => { flowTarget={FlowTargetSourceDest.source} indexNames={selectedPatterns} ip={ip} - skip={isInitializing || filterQuery === undefined} + skip={shouldSkip} startDate={from} type={type} setQuery={setQuery} @@ -192,7 +198,7 @@ const NetworkDetailsComponent: React.FC = () => { filterQuery={filterQuery} indexNames={selectedPatterns} ip={ip} - skip={isInitializing || filterQuery === undefined} + skip={shouldSkip} startDate={from} type={type} setQuery={setQuery} @@ -211,7 +217,7 @@ const NetworkDetailsComponent: React.FC = () => { flowTarget={FlowTargetSourceDest.source} indexNames={selectedPatterns} ip={ip} - skip={isInitializing || filterQuery === undefined} + skip={shouldSkip} startDate={from} type={type} setQuery={setQuery} @@ -226,7 +232,7 @@ const NetworkDetailsComponent: React.FC = () => { filterQuery={filterQuery} indexNames={selectedPatterns} ip={ip} - skip={isInitializing || filterQuery === undefined} + skip={shouldSkip} startDate={from} type={type} setQuery={setQuery} @@ -243,7 +249,7 @@ const NetworkDetailsComponent: React.FC = () => { flowTarget={flowTarget} indexNames={selectedPatterns} ip={ip} - skip={isInitializing || filterQuery === undefined} + skip={shouldSkip} startDate={from} type={type} setQuery={setQuery} @@ -256,7 +262,7 @@ const NetworkDetailsComponent: React.FC = () => { filterQuery={filterQuery} indexNames={selectedPatterns} ip={ip} - skip={isInitializing || filterQuery === undefined} + skip={shouldSkip} startDate={from} type={type} setQuery={setQuery} @@ -271,7 +277,7 @@ const NetworkDetailsComponent: React.FC = () => { indexNames={selectedPatterns} ip={ip} setQuery={setQuery} - skip={isInitializing || filterQuery === undefined} + skip={shouldSkip} startDate={from} type={type} /> @@ -283,7 +289,7 @@ const NetworkDetailsComponent: React.FC = () => { setQuery={setQuery} startDate={from} endDate={to} - skip={isInitializing || filterQuery === undefined} + skip={shouldSkip} indexNames={selectedPatterns} ip={ip} type={type} From f2b22d4039c691acf897474a716fde403071aaf7 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 19 May 2021 11:38:57 -0600 Subject: [PATCH 08/20] adds more dependencies to hook --- .../common/hooks/use_invalid_filter_query.tsx | 11 +++++++++-- .../public/common/lib/keury/index.ts | 2 +- .../detections/components/alerts_table/index.tsx | 3 +++ .../public/hosts/pages/details/index.tsx | 2 +- .../public/hosts/pages/hosts.tsx | 2 +- .../public/network/pages/details/index.tsx | 2 +- .../public/network/pages/network.tsx | 2 +- .../components/alerts_by_category/index.tsx | 2 +- .../overview/components/event_counts/index.tsx | 16 ++++++++++++++-- .../components/events_by_dataset/index.tsx | 2 +- .../network_details/expandable_network.tsx | 2 +- .../timeline/query_tab_content/index.tsx | 3 +++ 12 files changed, 37 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx index 7fcfa4b02a194..1522a36c2ae0c 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -6,6 +6,7 @@ */ import { useEffect } from 'react'; +import { Query } from 'src/plugins/data/public'; import { useAppToasts } from './use_app_toasts'; /** @@ -14,17 +15,23 @@ import { useAppToasts } from './use_app_toasts'; export const useInvalidFilterQuery = ({ filterQuery, kqlError, + query, + startDate, + endDate, }: { filterQuery?: string; kqlError?: Error; + query: Query; + startDate: string; + endDate: string; }) => { const { addError } = useAppToasts(); useEffect(() => { if (filterQuery === undefined && kqlError != null) { - addError(kqlError, { title: kqlError.name }); + addError(kqlError, { title: kqlError.name, toastMessage: kqlError.message }); } // This disable is required to only trigger the toast once per render // eslint-disable-next-line react-hooks/exhaustive-deps - }, [filterQuery, addError]); + }, [filterQuery, addError, query, startDate, endDate]); }; diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts index 8b8a56bdeaa71..13db6e94d2eea 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts @@ -80,7 +80,7 @@ export const convertToBuildEsQuery = ({ indexPattern: IIndexPattern; queries: Query[]; filters: Filter[]; -}): [string | undefined, Error | undefined] => { +}): [string, undefined] | [undefined, Error] => { try { return [ JSON.stringify( diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index af525beba7ecc..989d34e90a693 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -137,6 +137,9 @@ export const AlertsTableComponent: React.FC = ({ useInvalidFilterQuery({ filterQuery: getGlobalQuery([])?.filterQuery, kqlError: getGlobalQuery([])?.kqlError, + query: globalQuery, + startDate: from, + endDate: to, }); const setEventsLoadingCallback = useCallback( diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx index 9ea6dc943b57c..62fdc6a85f32d 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx @@ -111,7 +111,7 @@ const HostDetailsComponent: React.FC = ({ detailName, hostDeta filters: getFilters(), }); - useInvalidFilterQuery({ filterQuery, kqlError }); + useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); useEffect(() => { dispatch(setHostDetailsTablesActivePageToZero()); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index d6ac156e40bda..efd970e76f0ab 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -132,7 +132,7 @@ const HostsComponent = () => { [indexPattern, query, tabsFilters, uiSettings] ); - useInvalidFilterQuery({ filterQuery, kqlError }); + useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); const onSkipFocusBeforeEventsTable = useCallback(() => { containerElement.current diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx index 6637f457d2e88..3a49e0ea0924c 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx @@ -101,7 +101,7 @@ const NetworkDetailsComponent: React.FC = () => { filters, }); - useInvalidFilterQuery({ filterQuery, kqlError }); + useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); const [loading, { id, inspect, networkDetails, refetch }] = useNetworkDetails({ docValueFields, diff --git a/x-pack/plugins/security_solution/public/network/pages/network.tsx b/x-pack/plugins/security_solution/public/network/pages/network.tsx index 205a8a791c4b6..9be01147dea87 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.tsx @@ -147,7 +147,7 @@ const NetworkComponent = React.memo( filters: tabsFilters, }); - useInvalidFilterQuery({ filterQuery, kqlError }); + useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); return ( <> diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx index 58089aa323046..2645c14865a40 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx @@ -113,7 +113,7 @@ const AlertsByCategoryComponent: React.FC = ({ [filters, indexPattern, uiSettings, query] ); - useInvalidFilterQuery({ filterQuery, kqlError }); + useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); useEffect(() => { return () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx index a74427368bdb0..4311ea1b66228 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx @@ -68,9 +68,21 @@ const EventCountsComponent: React.FC = ({ [filters, indexPattern, uiSettings, query] ); - useInvalidFilterQuery({ filterQuery: hostFilterQuery, kqlError: hostKqlError }); + useInvalidFilterQuery({ + filterQuery: hostFilterQuery, + kqlError: hostKqlError, + query, + startDate: from, + endDate: to, + }); - useInvalidFilterQuery({ filterQuery: networkFilterQuery, kqlError: networkKqlError }); + useInvalidFilterQuery({ + filterQuery: networkFilterQuery, + kqlError: networkKqlError, + query, + startDate: from, + endDate: to, + }); return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index f9367cba99f9d..e68fe75dc6325 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -129,7 +129,7 @@ const EventsByDatasetComponent: React.FC = ({ return [combinedQueries]; }, [combinedQueries, kibana, indexPattern, query, filters]); - useInvalidFilterQuery({ filterQuery, kqlError }); + useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); const eventsByDatasetHistogramConfigs: MatrixHistogramConfigs = useMemo( () => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx index 46c46b5cf5b2f..74d50cecd3246 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx @@ -105,7 +105,7 @@ export const ExpandableNetworkDetails = ({ filters, }); - useInvalidFilterQuery({ filterQuery, kqlError }); + useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); const [loading, { id, networkDetails }] = useNetworkDetails({ docValueFields, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index cfd976bba7752..d242d90cee4ed 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -213,6 +213,9 @@ export const QueryTabContentComponent: React.FC = ({ useInvalidFilterQuery({ filterQuery: combinedQueries?.filterQuery, kqlError: combinedQueries?.kqlError, + query: kqlQuery, + startDate: start, + endDate: end, }); const isBlankTimeline: boolean = From 842b7d8aca39416b5384cce86e1d2bffabef815d Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 19 May 2021 13:25:30 -0600 Subject: [PATCH 09/20] removes error stack from toast --- .../public/common/hooks/use_invalid_filter_query.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx index 1522a36c2ae0c..81ea430f93279 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -29,7 +29,9 @@ export const useInvalidFilterQuery = ({ useEffect(() => { if (filterQuery === undefined && kqlError != null) { - addError(kqlError, { title: kqlError.name, toastMessage: kqlError.message }); + // Removes error stack from user view + delete kqlError.stack; + addError(kqlError, { title: kqlError.name }); } // This disable is required to only trigger the toast once per render // eslint-disable-next-line react-hooks/exhaustive-deps From 5aaeec90e057450d47ac8af12e3dc7c484383218 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 24 May 2021 16:54:18 -0600 Subject: [PATCH 10/20] adds inspect disable --- .../common/components/events_viewer/events_viewer.tsx | 1 + .../public/common/components/header_section/index.tsx | 9 ++++++++- .../common/components/ml/tables/anomalies_host_table.tsx | 1 + .../components/ml/tables/anomalies_network_table.tsx | 1 + .../components/alerts_histogram_panel/index.tsx | 5 ++++- .../public/overview/components/overview_host/index.tsx | 8 +++++++- .../overview/components/overview_network/index.tsx | 8 +++++++- .../overview/containers/overview_network/index.tsx | 4 ++-- .../public/timelines/components/flyout/header/index.tsx | 2 +- 9 files changed, 32 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 7b64ee1135610..1ee2afb910520 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -300,6 +300,7 @@ const EventsViewerComponent: React.FC = ({ height={headerFilterGroup ? COMPACT_HEADER_HEIGHT : EVENTS_VIEWER_HEADER_HEIGHT} subtitle={utilityBar ? undefined : subtitle} title={globalFullScreen ? titleWithExitFullScreen : justTitle} + isInspectDisabled={combinedQueries!.filterQuery === undefined} > {HeaderSectionContent} diff --git a/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx index fb8022292d329..fe32b7addd25e 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx @@ -41,6 +41,7 @@ export interface HeaderSectionProps extends HeaderProps { children?: React.ReactNode; height?: number; id?: string; + isInspectDisabled?: boolean; split?: boolean; subtitle?: string | React.ReactNode; title: string | React.ReactNode; @@ -55,6 +56,7 @@ const HeaderSectionComponent: React.FC = ({ children, height, id, + isInspectDisabled, split, subtitle, title, @@ -85,7 +87,12 @@ const HeaderSectionComponent: React.FC = ({ {id && ( - + )} diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/anomalies_host_table.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/anomalies_host_table.tsx index dd2f84221f5f0..49a4f22ef0a75 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/anomalies_host_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/anomalies_host_table.tsx @@ -75,6 +75,7 @@ const AnomaliesHostTableComponent: React.FC = ({ )}`} title={i18n.ANOMALIES} tooltip={i18n.TOOLTIP} + isInspectDisabled={skip} /> = ({ )}`} title={i18n.ANOMALIES} tooltip={i18n.TOOLTIP} + isInspectDisabled={skip} /> ( // create a unique, but stable (across re-renders) query id const uniqueQueryId = useMemo(() => `${DETECTIONS_HISTOGRAM_ID}-${uuid.v4()}`, []); const [isInitialLoading, setIsInitialLoading] = useState(true); + const [isInspectDisabled, setIsInspectDisabled] = useState(false); const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); const [totalAlertsObj, setTotalAlertsObj] = useState(defaultTotalAlertsObj); const [selectedStackByOption, setSelectedStackByOption] = useState( @@ -261,7 +262,7 @@ export const AlertsHistogramPanel = memo( } ); } - + setIsInspectDisabled(false); setAlertsQuery( getAlertsHistogramQuery( selectedStackByOption.value, @@ -271,6 +272,7 @@ export const AlertsHistogramPanel = memo( ) ); } catch (e) { + setIsInspectDisabled(true); setAlertsQuery(getAlertsHistogramQuery(selectedStackByOption.value, from, to, [])); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -305,6 +307,7 @@ export const AlertsHistogramPanel = memo( title={titleText} titleSize={onlyField == null ? 'm' : 's'} subtitle={!isInitialLoading && showTotalAlertsCount && totalAlerts} + isInspectDisabled={isInspectDisabled} > diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx index 0a8e817a3bfc4..a0307380ce802 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx @@ -51,6 +51,7 @@ const OverviewHostComponent: React.FC = ({ filterQuery, indexNames, startDate, + skip: filterQuery === undefined, }); const goToHost = useCallback( @@ -117,7 +118,12 @@ const OverviewHostComponent: React.FC = ({ - + <>{hostPageButton} diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx index eb5231d4ce5e0..214cd7b3f055b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx @@ -53,6 +53,7 @@ const OverviewNetworkComponent: React.FC = ({ filterQuery, indexNames, startDate, + skip: filterQuery === undefined, }); const goToNetwork = useCallback( @@ -123,7 +124,12 @@ const OverviewNetworkComponent: React.FC = ({ <> - + {networkPageButton} diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx b/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx index 846c40994aac2..2b3adc36ae746 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_network/index.tsx @@ -74,7 +74,7 @@ export const useNetworkOverview = ({ const overviewNetworkSearch = useCallback( (request: NetworkOverviewRequestOptions | null) => { - if (request == null) { + if (request == null || skip) { return; } @@ -118,7 +118,7 @@ export const useNetworkOverview = ({ asyncSearch(); refetch.current = asyncSearch; }, - [data.search, addError, addWarning] + [data.search, addError, addWarning, skip] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index 479b32c2d642e..4659b34f725fa 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -134,7 +134,7 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline queryId={`${timelineId}-${activeTab}`} inputId="timeline" inspectIndex={0} - isDisabled={!isDataInTimeline} + isDisabled={!isDataInTimeline || kqlQuery.filterQuery === undefined} title={i18n.INSPECT_TIMELINE_TITLE} /> From f885e2893440cb7a26f82b8970c42fceb8bcd985 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 24 May 2021 22:31:05 -0600 Subject: [PATCH 11/20] docs delete method --- .../public/common/hooks/use_invalid_filter_query.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx index 81ea430f93279..a6f2784e45f80 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -30,7 +30,7 @@ export const useInvalidFilterQuery = ({ useEffect(() => { if (filterQuery === undefined && kqlError != null) { // Removes error stack from user view - delete kqlError.stack; + delete kqlError.stack; // Mutates the error object and can possibly lead to side effects, only going this route for type issues. Change when we add a stackless toast error addError(kqlError, { title: kqlError.name }); } // This disable is required to only trigger the toast once per render From a78c84dd5e8e3de28127313443f77b1918d7e628 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 2 Jun 2021 15:20:25 -0600 Subject: [PATCH 12/20] fixes some stuff --- .../components/matrix_histogram/index.tsx | 1 + .../containers/matrix_histogram/index.ts | 2 +- .../common/hooks/use_invalid_filter_query.tsx | 6 +++ .../components/event_counts/index.tsx | 10 +---- .../components/flyout/header/index.tsx | 45 ++++++++++++++++--- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index 0a80ff0045cd1..19eb169505806 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -216,6 +216,7 @@ export const MatrixHistogramComponent: React.FC = titleSize={titleSize} subtitle={subtitleWithCounts} inspectMultiple + isInspectDisabled={filterQuery === undefined} > diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index d71a1a843269f..233313e47ad66 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -262,7 +262,7 @@ export const useMatrixHistogramCombined = ( const [missingDataLoading, missingDataResponse] = useMatrixHistogram({ ...matrixHistogramQueryProps, includeMissingData: false, - skip: skipMissingData, + skip: skipMissingData || matrixHistogramQueryProps.filterQuery === undefined, }); const combinedLoading = useMemo(() => mainLoading || missingDataLoading, [ diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx index a6f2784e45f80..600ed41fc8e8a 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -37,3 +37,9 @@ export const useInvalidFilterQuery = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [filterQuery, addError, query, startDate, endDate]); }; + +/** + * TODO: + * fix timeline rerender + * fix overview rerender + */ diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx index 4311ea1b66228..38a9dfed740b3 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx @@ -69,21 +69,13 @@ const EventCountsComponent: React.FC = ({ ); useInvalidFilterQuery({ - filterQuery: hostFilterQuery, + filterQuery: hostFilterQuery || networkFilterQuery, kqlError: hostKqlError, query, startDate: from, endDate: to, }); - useInvalidFilterQuery({ - filterQuery: networkFilterQuery, - kqlError: networkKqlError, - query, - startDate: from, - endDate: to, - }); - return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index 4659b34f725fa..e54da13ea6056 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -69,6 +69,9 @@ interface FlyoutHeaderPanelProps { const FlyoutHeaderPanelComponent: React.FC = ({ timelineId }) => { const dispatch = useDispatch(); + const { indexPattern, browserFields } = useSourcererScope(SourcererScopeName.timeline); + const { uiSettings } = useKibana().services; + const esQueryConfig = useMemo(() => esQuery.getEsQueryConfig(uiSettings), [uiSettings]); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const { activeTab, @@ -79,6 +82,8 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline status: timelineStatus, updated, show, + filters, + kqlMode, } = useDeepEqualSelector((state) => pick( [ @@ -90,6 +95,8 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline 'timelineType', 'updated', 'show', + 'filters', + 'kqlMode', ], getTimeline(state, timelineId) ?? timelineDefaults ) @@ -98,6 +105,30 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline () => !isEmpty(dataProviders) || !isEmpty(get('filterQuery.kuery.expression', kqlQuery)), [dataProviders, kqlQuery] ); + const getKqlQueryTimeline = useMemo(() => timelineSelectors.getKqlFilterQuerySelector(), []); + const kqlQueryTimeline = useSelector((state: State) => getKqlQueryTimeline(state, timelineId)!); + + const kqlQueryExpression = + isEmpty(dataProviders) && isEmpty(kqlQueryTimeline) && timelineType === 'template' + ? ' ' + : kqlQueryTimeline; + const kqlQueryTest = useMemo(() => ({ query: kqlQueryExpression, language: 'kuery' }), [ + kqlQueryExpression, + ]); + + const combinedQueries = useMemo( + () => + combineQueries({ + config: esQueryConfig, + dataProviders, + indexPattern, + browserFields, + filters: filters ? filters : [], + kqlQuery: kqlQueryTest, + kqlMode, + }), + [browserFields, dataProviders, esQueryConfig, filters, indexPattern, kqlMode, kqlQueryTest] + ); const handleClose = useCallback(() => { dispatch(timelineActions.showTimeline({ id: timelineId, show: false })); @@ -134,7 +165,7 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline queryId={`${timelineId}-${activeTab}`} inputId="timeline" inspectIndex={0} - isDisabled={!isDataInTimeline || kqlQuery.filterQuery === undefined} + isDisabled={!isDataInTimeline || combinedQueries?.filterQuery === undefined} title={i18n.INSPECT_TIMELINE_TITLE} /> @@ -295,10 +326,6 @@ const FlyoutHeaderComponent: React.FC = ({ timelineId }) => { kqlQueryExpression, ]); - const isBlankTimeline: boolean = useMemo( - () => isEmpty(dataProviders) && isEmpty(filters) && isEmpty(kqlQuery.query), - [dataProviders, filters, kqlQuery] - ); const combinedQueries = useMemo( () => combineQueries({ @@ -312,6 +339,14 @@ const FlyoutHeaderComponent: React.FC = ({ timelineId }) => { }), [browserFields, dataProviders, esQueryConfig, filters, indexPattern, kqlMode, kqlQuery] ); + + const isBlankTimeline: boolean = useMemo( + () => + (isEmpty(dataProviders) && isEmpty(filters) && isEmpty(kqlQuery.query)) || + combinedQueries?.filterQuery === undefined, + [dataProviders, filters, kqlQuery, combinedQueries] + ); + const [loading, kpis] = useTimelineKpis({ defaultIndex: selectedPatterns, docValueFields, From f774365717b4e2f3d3bed2f78e421526af5ac98d Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 15 Jun 2021 03:05:46 -0600 Subject: [PATCH 13/20] adds redux --- .../common/hooks/use_invalid_filter_query.tsx | 39 ++++++++++++++----- .../public/common/store/app/actions.ts | 7 ++++ .../public/common/store/app/model.ts | 2 + .../public/common/store/app/reducer.ts | 15 ++++++- .../components/alerts_table/index.tsx | 1 + .../public/hosts/containers/hosts/index.tsx | 2 +- .../public/hosts/pages/details/index.tsx | 4 +- .../public/hosts/pages/hosts.tsx | 3 +- .../network/containers/details/index.tsx | 2 +- .../public/network/pages/details/index.tsx | 4 +- .../public/network/pages/network.tsx | 5 ++- .../components/alerts_by_category/index.tsx | 2 +- .../components/event_counts/index.tsx | 4 +- .../components/events_by_dataset/index.tsx | 9 ++++- .../network_details/expandable_network.tsx | 4 +- .../timeline/query_tab_content/index.tsx | 2 +- 16 files changed, 79 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx index 600ed41fc8e8a..2dd771bda86d3 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -5,20 +5,26 @@ * 2.0. */ -import { useEffect } from 'react'; +import { useEffect, useMemo } from 'react'; +import { useDispatch } from 'react-redux'; import { Query } from 'src/plugins/data/public'; +import { appSelectors } from '../store'; +import { appActions } from '../store/app'; import { useAppToasts } from './use_app_toasts'; +import { useDeepEqualSelector } from './use_selector'; /** * Adds a toast error message whenever invalid KQL is submitted through the search bar */ export const useInvalidFilterQuery = ({ + id, filterQuery, kqlError, query, startDate, endDate, }: { + id: string; filterQuery?: string; kqlError?: Error; query: Query; @@ -26,20 +32,33 @@ export const useInvalidFilterQuery = ({ endDate: string; }) => { const { addError } = useAppToasts(); + const dispatch = useDispatch(); + const getErrorsSelector = useMemo(() => appSelectors.errorsSelector(), []); + const errors = useDeepEqualSelector(getErrorsSelector); useEffect(() => { if (filterQuery === undefined && kqlError != null) { + const errorHash = JSON.stringify(kqlError); + dispatch( + appActions.addErrorHash({ + id, + hash: errorHash, + title: kqlError.name, + message: [kqlError.message], + }) + ); + } + // This disable is required to only trigger the toast once per render + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [id, filterQuery, addError, query, startDate, endDate]); + + useEffect(() => { + const myError = errors.find((e) => e.id === id); + if (myError != null && myError.displayError && kqlError != null) { // Removes error stack from user view delete kqlError.stack; // Mutates the error object and can possibly lead to side effects, only going this route for type issues. Change when we add a stackless toast error + addError(kqlError, { title: kqlError.name }); } - // This disable is required to only trigger the toast once per render - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [filterQuery, addError, query, startDate, endDate]); + }, [addError, errors, id, kqlError]); }; - -/** - * TODO: - * fix timeline rerender - * fix overview rerender - */ diff --git a/x-pack/plugins/security_solution/public/common/store/app/actions.ts b/x-pack/plugins/security_solution/public/common/store/app/actions.ts index 316134c5b6672..a262b053d706c 100644 --- a/x-pack/plugins/security_solution/public/common/store/app/actions.ts +++ b/x-pack/plugins/security_solution/public/common/store/app/actions.ts @@ -20,3 +20,10 @@ export const addError = actionCreator<{ id: string; title: string; message: stri ); export const removeError = actionCreator<{ id: string }>('REMOVE_ERRORS'); + +export const addErrorHash = actionCreator<{ + id: string; + hash: string; + title: string; + message: string[]; +}>('ADD_ERROR_HASH'); diff --git a/x-pack/plugins/security_solution/public/common/store/app/model.ts b/x-pack/plugins/security_solution/public/common/store/app/model.ts index 5a252e4aa48f2..2888867167c14 100644 --- a/x-pack/plugins/security_solution/public/common/store/app/model.ts +++ b/x-pack/plugins/security_solution/public/common/store/app/model.ts @@ -18,6 +18,8 @@ export interface Error { id: string; title: string; message: string[]; + hash?: string; + displayError?: boolean; } export type ErrorModel = Error[]; diff --git a/x-pack/plugins/security_solution/public/common/store/app/reducer.ts b/x-pack/plugins/security_solution/public/common/store/app/reducer.ts index c55c83b5e5f01..80284402c8fa6 100644 --- a/x-pack/plugins/security_solution/public/common/store/app/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/app/reducer.ts @@ -9,7 +9,7 @@ import { reducerWithInitialState } from 'typescript-fsa-reducers'; import { Note } from '../../lib/note'; -import { addError, addNotes, removeError, updateNote } from './actions'; +import { addError, addErrorHash, addNotes, removeError, updateNote } from './actions'; import { AppModel, NotesById } from './model'; export type AppState = AppModel; @@ -46,4 +46,17 @@ export const appReducer = reducerWithInitialState(initialAppState) ...state, errors: state.errors.filter((error) => error.id !== id), })) + .case(addErrorHash, (state, { id, hash, title, message }) => { + const errorIdx = state.errors.findIndex((e) => e.id === id); + const errorObj = state.errors.find((e) => e.id === id) || { id, title, message }; + + return { + ...state, + errors: [ + ...state.errors.slice(0, errorIdx), + { ...errorObj, hash, displayError: !state.errors.some((e) => e.hash === hash) }, + ...state.errors.slice(errorIdx + 1), + ], + }; + }) .build(); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index 989d34e90a693..c6cec4108c54d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -135,6 +135,7 @@ export const AlertsTableComponent: React.FC = ({ ); useInvalidFilterQuery({ + id: timelineId, filterQuery: getGlobalQuery([])?.filterQuery, kqlError: getGlobalQuery([])?.kqlError, query: globalQuery, diff --git a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx index 6244427b45d11..1a9e86755cf7d 100644 --- a/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/containers/hosts/index.tsx @@ -33,7 +33,7 @@ import { InspectResponse } from '../../../types'; import { useTransforms } from '../../../transforms/containers/use_transforms'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; -const ID = 'hostsAllQuery'; +export const ID = 'hostsAllQuery'; type LoadPage = (newActivePage: number) => void; export interface HostsArgs { diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx index 62fdc6a85f32d..6e371bbf610e1 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx @@ -49,7 +49,7 @@ import { TimelineId } from '../../../../common/types/timeline'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { useSourcererScope } from '../../../common/containers/sourcerer'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; -import { useHostDetails } from '../../containers/hosts/details'; +import { ID, useHostDetails } from '../../containers/hosts/details'; import { manageQuery } from '../../../common/components/page/manage_query'; import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query'; @@ -111,7 +111,7 @@ const HostDetailsComponent: React.FC = ({ detailName, hostDeta filters: getFilters(), }); - useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); + useInvalidFilterQuery({ id: ID, filterQuery, kqlError, query, startDate: from, endDate: to }); useEffect(() => { dispatch(setHostDetailsTablesActivePageToZero()); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index efd970e76f0ab..f647819798ab7 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -53,6 +53,7 @@ import { timelineDefaults } from '../../timelines/store/timeline/defaults'; import { useSourcererScope } from '../../common/containers/sourcerer'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../common/hooks/use_selector'; import { useInvalidFilterQuery } from '../../common/hooks/use_invalid_filter_query'; +import { ID } from '../containers/hosts'; /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. @@ -132,7 +133,7 @@ const HostsComponent = () => { [indexPattern, query, tabsFilters, uiSettings] ); - useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); + useInvalidFilterQuery({ id: ID, filterQuery, kqlError, query, startDate: from, endDate: to }); const onSkipFocusBeforeEventsTable = useCallback(() => { containerElement.current diff --git a/x-pack/plugins/security_solution/public/network/containers/details/index.tsx b/x-pack/plugins/security_solution/public/network/containers/details/index.tsx index cf7d8e05858d5..2565b9d8d5448 100644 --- a/x-pack/plugins/security_solution/public/network/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/containers/details/index.tsx @@ -26,7 +26,7 @@ import { getInspectResponse } from '../../../helpers'; import { InspectResponse } from '../../../types'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; -const ID = 'networkDetailsQuery'; +export const ID = 'networkDetailsQuery'; export interface NetworkDetailsArgs { id: string; diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx index 3a49e0ea0924c..10588b449473a 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx @@ -29,7 +29,7 @@ import { FlowTargetSelectConnected } from '../../components/flow_target_select_c import { IpOverview } from '../../components/details'; import { SiemSearchBar } from '../../../common/components/search_bar'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; -import { useNetworkDetails } from '../../containers/details'; +import { useNetworkDetails, ID } from '../../containers/details'; import { useKibana } from '../../../common/lib/kibana'; import { decodeIpv6 } from '../../../common/lib/helpers'; import { convertToBuildEsQuery } from '../../../common/lib/keury'; @@ -101,7 +101,7 @@ const NetworkDetailsComponent: React.FC = () => { filters, }); - useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); + useInvalidFilterQuery({ id: ID, filterQuery, kqlError, query, startDate: from, endDate: to }); const [loading, { id, inspect, networkDetails, refetch }] = useNetworkDetails({ docValueFields, diff --git a/x-pack/plugins/security_solution/public/network/pages/network.tsx b/x-pack/plugins/security_solution/public/network/pages/network.tsx index 9be01147dea87..928570417c524 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.tsx @@ -52,7 +52,6 @@ import { timelineDefaults } from '../../timelines/store/timeline/defaults'; import { useSourcererScope } from '../../common/containers/sourcerer'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../common/hooks/use_selector'; import { useInvalidFilterQuery } from '../../common/hooks/use_invalid_filter_query'; - /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. */ @@ -62,6 +61,8 @@ const StyledFullHeightContainer = styled.div` flex: 1 1 auto; `; +const ID = 'NetworkQueryId'; + const NetworkComponent = React.memo( ({ hasMlUserPermissions, capabilitiesFetched }) => { const dispatch = useDispatch(); @@ -147,7 +148,7 @@ const NetworkComponent = React.memo( filters: tabsFilters, }); - useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); + useInvalidFilterQuery({ id: ID, filterQuery, kqlError, query, startDate: from, endDate: to }); return ( <> diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx index 2645c14865a40..11270fe377733 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx @@ -113,7 +113,7 @@ const AlertsByCategoryComponent: React.FC = ({ [filters, indexPattern, uiSettings, query] ); - useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); + useInvalidFilterQuery({ id: ID, filterQuery, kqlError, query, startDate: from, endDate: to }); useEffect(() => { return () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx index 38a9dfed740b3..919057d6b5eab 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.tsx @@ -9,6 +9,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useMemo } from 'react'; import styled from 'styled-components'; +import { ID as OverviewHostQueryId } from '../../containers/overview_host'; import { OverviewHost } from '../overview_host'; import { OverviewNetwork } from '../overview_network'; import { filterHostData } from '../../../hosts/pages/navigation/alerts_query_tab_body'; @@ -57,7 +58,7 @@ const EventCountsComponent: React.FC = ({ [filters, indexPattern, query, uiSettings] ); - const [networkFilterQuery, networkKqlError] = useMemo( + const [networkFilterQuery] = useMemo( () => convertToBuildEsQuery({ config: esQuery.getEsQueryConfig(uiSettings), @@ -69,6 +70,7 @@ const EventCountsComponent: React.FC = ({ ); useInvalidFilterQuery({ + id: OverviewHostQueryId, filterQuery: hostFilterQuery || networkFilterQuery, kqlError: hostKqlError, query, diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index e68fe75dc6325..6bbf9db2c6657 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -129,7 +129,14 @@ const EventsByDatasetComponent: React.FC = ({ return [combinedQueries]; }, [combinedQueries, kibana, indexPattern, query, filters]); - useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); + useInvalidFilterQuery({ + id: uniqueQueryId, + filterQuery, + kqlError, + query, + startDate: from, + endDate: to, + }); const eventsByDatasetHistogramConfigs: MatrixHistogramConfigs = useMemo( () => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx index 74d50cecd3246..e53e835cfd882 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/network_details/expandable_network.tsx @@ -105,8 +105,6 @@ export const ExpandableNetworkDetails = ({ filters, }); - useInvalidFilterQuery({ filterQuery, kqlError, query, startDate: from, endDate: to }); - const [loading, { id, networkDetails }] = useNetworkDetails({ docValueFields, skip: isInitializing || filterQuery === undefined, @@ -115,6 +113,8 @@ export const ExpandableNetworkDetails = ({ ip, }); + useInvalidFilterQuery({ id, filterQuery, kqlError, query, startDate: from, endDate: to }); + const [isLoadingAnomaliesData, anomaliesData] = useAnomaliesTableData({ criteriaFields: networkToCriteria(ip, flowTarget), startDate: from, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index d242d90cee4ed..d714cdfec6450 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -261,7 +261,7 @@ export const QueryTabContentComponent: React.FC = ({ fields: getTimelineQueryFields(), language: kqlQuery.language, limit: itemsPerPage, - filterQuery: combinedQueries?.filterQuery ?? '', + filterQuery: combinedQueries?.filterQuery, startDate: start, skip: !canQueryTimeline() || combinedQueries?.filterQuery === undefined, sort: timelineQuerySortField, From 6faa484737a83126c8195e248c1d0e582760fb67 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 15 Jun 2021 10:17:30 -0600 Subject: [PATCH 14/20] fixes types --- .../timelines/components/timeline/query_tab_content/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index d714cdfec6450..7f1caee64ce4a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -211,6 +211,7 @@ export const QueryTabContentComponent: React.FC = ({ }); useInvalidFilterQuery({ + id: timelineId, filterQuery: combinedQueries?.filterQuery, kqlError: combinedQueries?.kqlError, query: kqlQuery, From 6affaebd4f53f940f81de64c827b6827225fe1d9 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 23 Jun 2021 17:19:20 -0600 Subject: [PATCH 15/20] fixes merge conflicts --- .../common/components/events_viewer/events_viewer.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 1ee2afb910520..c2f170c58043d 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -51,10 +51,7 @@ import { CellValueElementProps } from '../../../timelines/components/timeline/ce import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles'; import { timelineSelectors, timelineActions } from '../../../timelines/store/timeline'; import { useDeepEqualSelector } from '../../hooks/use_selector'; -import { - defaultControlColumn, - ControlColumnProps, -} from '../../../timelines/components/timeline/body/control_columns'; +import { defaultControlColumn } from '../../../timelines/components/timeline/body/control_columns'; export const EVENTS_VIEWER_HEADER_HEIGHT = 90; // px const UTILITY_BAR_HEIGHT = 19; // px @@ -174,7 +171,6 @@ const EventsViewerComponent: React.FC = ({ const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; const kibana = useKibana(); const [isQueryLoading, setIsQueryLoading] = useState(false); - const { getManageTimelineById, setIsTimelineLoading } = useManageTimeline(); useEffect(() => { dispatch(timelineActions.updateIsLoading({ id, isLoading: isQueryLoading })); From 09441a3cd95c93b4278efa195f66b4d337b13d4a Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 23 Jun 2021 18:44:32 -0600 Subject: [PATCH 16/20] fixes merge conflicts --- .../public/detections/components/alerts_table/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index c6cec4108c54d..0e32df851592d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -106,7 +106,6 @@ export const AlertsTableComponent: React.FC = ({ const kibana = useKibana(); const [, dispatchToaster] = useStateToaster(); const { addWarning } = useAppToasts(); - const { initializeTimeline, setSelectAll } = useManageTimeline(); // TODO: Once we are past experimental phase this code should be removed const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled'); From 8c103fcbee638075acd96173d3939711da42d5a5 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Wed, 23 Jun 2021 21:44:14 -0600 Subject: [PATCH 17/20] fixes merge conflicts --- .../drag_and_drop/draggable_wrapper_hover_content.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx index 400b178c167f6..2531780ec4bd5 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx @@ -50,6 +50,7 @@ jest.mock('../../../common/hooks/use_selector', () => ({ useShallowEqualSelector: jest.fn(), useDeepEqualSelector: jest.fn(), })); +jest.mock('../../../common/hooks/use_invalid_filter_query.tsx'); const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings; const timelineId = TimelineId.active; From a36b363e289e7dab6ba5dab5342e5ca0a8e5b0da Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Mon, 28 Jun 2021 22:47:58 -0600 Subject: [PATCH 18/20] adds temp solution --- x-pack/plugins/security_solution/public/app/app.tsx | 2 +- .../public/common/hooks/use_invalid_filter_query.tsx | 2 +- .../public/common/store/app/reducer.ts | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index c223570c77201..319c346e16e0e 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -64,7 +64,7 @@ const StartAppComponent: FC = ({ - + {/* */} diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx index 2dd771bda86d3..e95eb7ea08cbe 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -57,7 +57,7 @@ export const useInvalidFilterQuery = ({ if (myError != null && myError.displayError && kqlError != null) { // Removes error stack from user view delete kqlError.stack; // Mutates the error object and can possibly lead to side effects, only going this route for type issues. Change when we add a stackless toast error - + console.log('here'); addError(kqlError, { title: kqlError.name }); } }, [addError, errors, id, kqlError]); diff --git a/x-pack/plugins/security_solution/public/common/store/app/reducer.ts b/x-pack/plugins/security_solution/public/common/store/app/reducer.ts index 80284402c8fa6..1bc75c896848d 100644 --- a/x-pack/plugins/security_solution/public/common/store/app/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/app/reducer.ts @@ -49,7 +49,17 @@ export const appReducer = reducerWithInitialState(initialAppState) .case(addErrorHash, (state, { id, hash, title, message }) => { const errorIdx = state.errors.findIndex((e) => e.id === id); const errorObj = state.errors.find((e) => e.id === id) || { id, title, message }; - + console.log(state, errorIdx, errorObj); + if (errorIdx === -1) { + return { + ...state, + errors: state.errors.concat({ + ...errorObj, + hash, + displayError: !state.errors.some((e) => e.hash === hash), + }), + }; + } return { ...state, errors: [ From d3805a69eda0ac73c5b7b66d7e6e92235251b386 Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 29 Jun 2021 15:40:46 -0600 Subject: [PATCH 19/20] will this be the last commit? --- x-pack/plugins/security_solution/public/app/app.tsx | 2 +- .../public/common/hooks/use_invalid_filter_query.tsx | 10 +++++++--- .../public/common/store/app/reducer.ts | 1 - .../components/timeline/query_tab_content/index.tsx | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 319c346e16e0e..c223570c77201 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -64,7 +64,7 @@ const StartAppComponent: FC = ({ - {/* */} + diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx index e95eb7ea08cbe..e03efcac4bbf6 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_invalid_filter_query.tsx @@ -38,11 +38,16 @@ export const useInvalidFilterQuery = ({ useEffect(() => { if (filterQuery === undefined && kqlError != null) { - const errorHash = JSON.stringify(kqlError); + // Local util for creating an replicatable error hash + const hashCode = kqlError.message + .split('') + // eslint-disable-next-line no-bitwise + .reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0) + .toString(); dispatch( appActions.addErrorHash({ id, - hash: errorHash, + hash: hashCode, title: kqlError.name, message: [kqlError.message], }) @@ -57,7 +62,6 @@ export const useInvalidFilterQuery = ({ if (myError != null && myError.displayError && kqlError != null) { // Removes error stack from user view delete kqlError.stack; // Mutates the error object and can possibly lead to side effects, only going this route for type issues. Change when we add a stackless toast error - console.log('here'); addError(kqlError, { title: kqlError.name }); } }, [addError, errors, id, kqlError]); diff --git a/x-pack/plugins/security_solution/public/common/store/app/reducer.ts b/x-pack/plugins/security_solution/public/common/store/app/reducer.ts index 1bc75c896848d..20c9b0e14dbd9 100644 --- a/x-pack/plugins/security_solution/public/common/store/app/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/app/reducer.ts @@ -49,7 +49,6 @@ export const appReducer = reducerWithInitialState(initialAppState) .case(addErrorHash, (state, { id, hash, title, message }) => { const errorIdx = state.errors.findIndex((e) => e.id === id); const errorObj = state.errors.find((e) => e.id === id) || { id, title, message }; - console.log(state, errorIdx, errorObj); if (errorIdx === -1) { return { ...state, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index 7f1caee64ce4a..c2e47edeae202 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -198,7 +198,7 @@ export const QueryTabContentComponent: React.FC = ({ const kqlQuery: { query: string; language: KueryFilterQueryKind; - } = { query: kqlQueryExpression, language: 'kuery' }; + } = useMemo(() => ({ query: kqlQueryExpression, language: 'kuery' }), [kqlQueryExpression]); const combinedQueries = combineQueries({ config: esQueryConfig, From c77026ac2e91f1ec3f20147f9f9925e06f73924c Mon Sep 17 00:00:00 2001 From: Davis Plumlee Date: Tue, 29 Jun 2021 17:26:12 -0600 Subject: [PATCH 20/20] no --- .../components/matrix_histogram/index.tsx | 2 + .../hosts/pages/details/details_tabs.tsx | 2 +- .../public/hosts/pages/hosts_tabs.tsx | 2 +- .../components/events_by_dataset/index.tsx | 1 + .../use_request_event_counts.ts | 38 ++++++++++--------- 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx index 19eb169505806..68a1ff14f2f0b 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx @@ -91,6 +91,7 @@ export const MatrixHistogramComponent: React.FC = title, titleSize, yTickFormatter, + skip, }) => { const dispatch = useDispatch(); const handleBrushEnd = useCallback( @@ -146,6 +147,7 @@ export const MatrixHistogramComponent: React.FC = stackByField: selectedStackByOption.value, isPtrIncluded, docValueFields, + skip, }; const [loading, { data, inspect, totalCount, refetch }] = useMatrixHistogramCombined( diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx index bb51a353f4706..dc537f2f6ffe3 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.tsx @@ -70,7 +70,7 @@ export const HostDetailsTabs = React.memo( deleteQuery, endDate: to, filterQuery, - skip: isInitializing, + skip: isInitializing || filterQuery === undefined, setQuery, startDate: from, type, diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx index 876730a8f66c4..7d7288c878369 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx @@ -69,7 +69,7 @@ export const HostsTabs = memo( endDate: to, filterQuery, indexNames, - skip: isInitializing, + skip: isInitializing || filterQuery === undefined, setQuery, startDate: from, type, diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index 6bbf9db2c6657..5cea3fa98eeb7 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -180,6 +180,7 @@ const EventsByDatasetComponent: React.FC = ({ setAbsoluteRangeDatePickerTarget={setAbsoluteRangeDatePickerTarget} setQuery={setQuery} showSpacer={showSpacer} + skip={filterQuery === undefined} startDate={from} timelineId={timelineId} {...eventsByDatasetHistogramConfigs} diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts index a6990c726dcf2..31c6593282f8c 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_request_event_counts.ts @@ -16,36 +16,38 @@ import { useKibana } from '../../../common/lib/kibana'; export const useRequestEventCounts = (to: string, from: string) => { const { uiSettings } = useKibana().services; + const [filterQuery] = convertToBuildEsQuery({ + config: esQuery.getEsQueryConfig(uiSettings), + indexPattern: { + fields: [ + { + name: 'event.kind', + aggregatable: true, + searchable: true, + type: 'string', + esTypes: ['keyword'], + }, + ], + title: 'filebeat-*', + }, + queries: [{ query: 'event.type:indicator', language: 'kuery' }], + filters: [], + }); + const matrixHistogramRequest = useMemo(() => { return { endDate: to, errorMessage: i18n.translate('xpack.securitySolution.overview.errorFetchingEvents', { defaultMessage: 'Error fetching events', }), - filterQuery: convertToBuildEsQuery({ - config: esQuery.getEsQueryConfig(uiSettings), - indexPattern: { - fields: [ - { - name: 'event.kind', - aggregatable: true, - searchable: true, - type: 'string', - esTypes: ['keyword'], - }, - ], - title: 'filebeat-*', - }, - queries: [{ query: 'event.type:indicator', language: 'kuery' }], - filters: [], - }), + filterQuery, histogramType: MatrixHistogramType.events, indexNames: DEFAULT_CTI_SOURCE_INDEX, stackByField: EVENT_DATASET, startDate: from, size: 0, }; - }, [to, from, uiSettings]); + }, [to, from, filterQuery]); const results = useMatrixHistogram(matrixHistogramRequest);