From b5bcf69022116a2be0cf751ff5da89ecb37eb4a6 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 2 Oct 2023 13:25:08 -0600 Subject: [PATCH] [search source] remove is_partial check (#164506) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes https://github.com/elastic/kibana/issues/164893 ### Background "is partial" has 2 meanings 1) Results are incomplete because search is still running 2) Search is finished. Results are incomplete because there are shard failures (either in local or remote clusters) [async search](https://www.elastic.co/guide/en/elasticsearch/reference/current/async-search.html) defines 2 flags. 1) `is_running`: Whether the search is still being executed or it has completed 2) `is_partial`: When the query is no longer running, indicates whether the search failed or was successfully completed on all shards. While the query is being executed, is_partial is always set to true **note**: there is a bug in async search where requests to only local clusters return `is_partial:false` when there are shard errors on the local cluster. See https://github.com/elastic/elasticsearch/issues/98725. This should be resolved in 8.11 Kibana's existing search implementation does not align with Elasticsearch's `is_running` and `is_partial` flags. Kibana defines "is partial" as definition "1)". Elasticsearch async search defines "is partial" as definition "2)". This PR aligns Kibana's usage of "is partial" with Elasticsearch's definition. This required the following changes 1) `isErrorResponse` renamed to `isAbortedResponse`. Method no longer returns true when `!response.isRunning && !!response.isPartial`. Kibana handles results with incomplete data. **Note** removed export of `isErrorResponse` from data plugin since its use outside of data plugin does not make sense. 2) Replace `isPartialResponse` with `isRunningResponse`. This aligns Kibana's definition with Elasticsearch async search flags. 3) Remove `isCompleteResponse`. The word "complete" is ambiguous. Does it mean the search is finished (no longer running)? Or does it mean the search has all results and there are no shard failures? --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Jatin Kathuria Co-authored-by: Patryk KopyciƄski --- .../search_examples/public/search/app.tsx | 6 +- .../public/search_sessions/app.tsx | 4 +- .../search_examples/public/sql_search/app.tsx | 4 +- src/plugins/data/README.mdx | 4 +- .../data/common/search/poll_search.test.ts | 13 +- src/plugins/data/common/search/poll_search.ts | 8 +- .../search/search_source/search_source.ts | 12 +- src/plugins/data/common/search/utils.test.ts | 198 ++---------------- src/plugins/data/common/search/utils.ts | 41 +--- src/plugins/data/public/index.ts | 2 +- .../search_interceptor.test.ts | 66 ++---- .../search_interceptor/search_interceptor.ts | 4 +- .../search_response_cache.test.ts | 12 +- .../search_response_cache.ts | 6 +- .../server/search/collectors/search/usage.ts | 4 +- .../search/session/get_search_status.test.ts | 4 +- .../search/session/get_search_status.ts | 4 +- .../sql_search/sql_search_strategy.test.ts | 4 +- .../sql_search/sql_search_strategy.ts | 2 +- .../application/main/utils/fetch_documents.ts | 4 +- .../public/chart/hooks/use_total_hits.ts | 4 +- .../use_categorize_request.ts | 4 +- .../public/hooks/use_cancellable_search.ts | 4 +- ...nalytics_collection_explore_table_logic.ts | 4 +- .../public/hooks/use_es_search.ts | 54 +++-- .../events_viewer/use_timelines_events.tsx | 4 +- .../containers/cti/event_enrichment/api.ts | 4 +- .../events/last_event_time/index.ts | 4 +- .../containers/matrix_histogram/index.ts | 4 +- .../use_search_strategy/index.test.ts | 6 +- .../containers/use_search_strategy/index.tsx | 6 +- .../containers/kpi_network/dns/index.tsx | 4 +- .../kpi_network/network_events/index.tsx | 4 +- .../kpi_network/tls_handshakes/index.tsx | 4 +- .../kpi_network/unique_flows/index.tsx | 4 +- .../kpi_network/unique_private_ips/index.tsx | 4 +- .../flyout/left/services/find_alerts.ts | 17 +- .../overview_cti_links/use_ti_data_sources.ts | 4 +- .../timelines/containers/details/index.tsx | 4 +- .../public/timelines/containers/index.tsx | 4 +- .../timelines/containers/kpis/index.tsx | 4 +- .../synthetics/state/elasticsearch/api.ts | 60 +++--- .../indicators/hooks/use_total_count.tsx | 4 +- .../public/utils/search.ts | 4 +- .../hooks/use_fetch_alerts.test.tsx | 4 +- .../alerts_table/hooks/use_fetch_alerts.tsx | 4 +- .../app/rum_dashboard/ux_overview_fetchers.ts | 4 +- 47 files changed, 192 insertions(+), 441 deletions(-) diff --git a/examples/search_examples/public/search/app.tsx b/examples/search_examples/public/search/app.tsx index 84bc3e1cd79be..962ad910fc08d 100644 --- a/examples/search_examples/public/search/app.tsx +++ b/examples/search_examples/public/search/app.tsx @@ -29,7 +29,7 @@ import { IInspectorInfo } from '@kbn/data-plugin/common'; import { DataPublicPluginStart, IKibanaSearchResponse, - isCompleteResponse, + isRunningResponse, } from '@kbn/data-plugin/public'; import { SearchResponseWarning } from '@kbn/data-plugin/public/search/types'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; @@ -209,7 +209,7 @@ export const SearchExamplesApp = ({ }) .subscribe({ next: (res) => { - if (isCompleteResponse(res)) { + if (!isRunningResponse(res)) { setIsLoading(false); setResponse(res); const aggResult: number | undefined = res.rawResponse.aggregations @@ -389,7 +389,7 @@ export const SearchExamplesApp = ({ .subscribe({ next: (res) => { setResponse(res); - if (isCompleteResponse(res)) { + if (!isRunningResponse(res)) { setIsLoading(false); notifications.toasts.addSuccess({ title: 'Query result', diff --git a/examples/search_examples/public/search_sessions/app.tsx b/examples/search_examples/public/search_sessions/app.tsx index 8b7c7bbcaa144..501d0b3940c2a 100644 --- a/examples/search_examples/public/search_sessions/app.tsx +++ b/examples/search_examples/public/search_sessions/app.tsx @@ -39,7 +39,7 @@ import { DataPublicPluginStart, IEsSearchRequest, IEsSearchResponse, - isCompleteResponse, + isRunningResponse, QueryState, SearchSessionState, } from '@kbn/data-plugin/public'; @@ -706,7 +706,7 @@ function doSearch( return lastValueFrom( data.search.search(req, { sessionId }).pipe( tap((res) => { - if (isCompleteResponse(res)) { + if (!isRunningResponse(res)) { const avgResult: number | undefined = res.rawResponse.aggregations ? // @ts-expect-error @elastic/elasticsearch no way to declare a type for aggregation in the search response res.rawResponse.aggregations[1]?.value ?? res.rawResponse.aggregations[2]?.value diff --git a/examples/search_examples/public/sql_search/app.tsx b/examples/search_examples/public/sql_search/app.tsx index 33406a53f9674..86580a55e714f 100644 --- a/examples/search_examples/public/sql_search/app.tsx +++ b/examples/search_examples/public/sql_search/app.tsx @@ -26,7 +26,7 @@ import { CoreStart } from '@kbn/core/public'; import { DataPublicPluginStart, IKibanaSearchResponse, - isCompleteResponse, + isRunningResponse, } from '@kbn/data-plugin/public'; import { SQL_SEARCH_STRATEGY, @@ -66,7 +66,7 @@ export const SqlSearchExampleApp = ({ notifications, data }: SearchExamplesAppDe }) .subscribe({ next: (res) => { - if (isCompleteResponse(res)) { + if (!isRunningResponse(res)) { setIsLoading(false); setResponse(res); } diff --git a/src/plugins/data/README.mdx b/src/plugins/data/README.mdx index ca1bd6fd98954..5e188553a355f 100644 --- a/src/plugins/data/README.mdx +++ b/src/plugins/data/README.mdx @@ -158,12 +158,12 @@ The `SearchSource` API is a convenient way to construct and run an Elasticsearch One benefit of using the low-level search API, is partial response support, allowing for a better and more responsive user experience. ```.ts - import { isCompleteResponse } from '../plugins/data/public'; + import { isRunningResponse } from '../plugins/data/public'; const search$ = data.search.search(request) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { // Final result search$.unsubscribe(); } else { diff --git a/src/plugins/data/common/search/poll_search.test.ts b/src/plugins/data/common/search/poll_search.test.ts index db39b5e187036..a884f1dad1558 100644 --- a/src/plugins/data/common/search/poll_search.test.ts +++ b/src/plugins/data/common/search/poll_search.test.ts @@ -10,7 +10,7 @@ import { pollSearch } from './poll_search'; import { AbortError } from '@kbn/kibana-utils-plugin/common'; describe('pollSearch', () => { - function getMockedSearch$(resolveOnI = 1, finishWithError = false) { + function getMockedSearch$(resolveOnI = 1) { let counter = 0; return jest.fn().mockImplementation(() => { counter++; @@ -19,7 +19,7 @@ describe('pollSearch', () => { if (lastCall) { resolve({ isRunning: false, - isPartial: finishWithError, + isPartial: false, rawResponse: {}, }); } else { @@ -57,15 +57,6 @@ describe('pollSearch', () => { expect(cancelFn).toBeCalledTimes(0); }); - test('Throws Error on ES error response', async () => { - const searchFn = getMockedSearch$(2, true); - const cancelFn = jest.fn(); - const poll = pollSearch(searchFn, cancelFn).toPromise(); - await expect(poll).rejects.toThrow(Error); - expect(searchFn).toBeCalledTimes(2); - expect(cancelFn).toBeCalledTimes(0); - }); - test('Throws AbortError on empty response', async () => { const searchFn = jest.fn().mockResolvedValue(undefined); const cancelFn = jest.fn(); diff --git a/src/plugins/data/common/search/poll_search.ts b/src/plugins/data/common/search/poll_search.ts index 1a6819257ab67..8637c472adb16 100644 --- a/src/plugins/data/common/search/poll_search.ts +++ b/src/plugins/data/common/search/poll_search.ts @@ -10,7 +10,7 @@ import { from, Observable, timer, defer, fromEvent, EMPTY } from 'rxjs'; import { expand, map, switchMap, takeUntil, takeWhile, tap } from 'rxjs/operators'; import { AbortError } from '@kbn/kibana-utils-plugin/common'; import type { IAsyncSearchOptions, IKibanaSearchResponse } from '..'; -import { isErrorResponse, isPartialResponse } from '..'; +import { isAbortResponse, isRunningResponse } from '..'; export const pollSearch = ( search: () => Promise, @@ -57,11 +57,11 @@ export const pollSearch = ( return timer(getPollInterval(elapsedTime)).pipe(switchMap(search)); }), tap((response) => { - if (isErrorResponse(response)) { - throw response ? new Error('Received partial response') : new AbortError(); + if (isAbortResponse(response)) { + throw new AbortError(); } }), - takeWhile(isPartialResponse, true), + takeWhile(isRunningResponse, true), takeUntil(aborted$) ); }); diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index f05f198451e0a..b42cb7fdf4f25 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -103,13 +103,7 @@ import { getSearchParamsFromRequest, RequestFailure } from './fetch'; import type { FetchHandlers, SearchRequest } from './fetch'; import { getRequestInspectorStats, getResponseInspectorStats } from './inspect'; -import { - getEsQueryConfig, - IKibanaSearchResponse, - isPartialResponse, - isCompleteResponse, - UI_SETTINGS, -} from '../..'; +import { getEsQueryConfig, IKibanaSearchResponse, isRunningResponse, UI_SETTINGS } from '../..'; import { AggsStart } from '../aggs'; import { extractReferences } from './extract_references'; import { @@ -546,7 +540,7 @@ export class SearchSource { // For testing timeout messages in UI, uncomment the next line // response.rawResponse.timed_out = true; return new Observable>((obs) => { - if (isPartialResponse(response)) { + if (isRunningResponse(response)) { obs.next(this.postFlightTransform(response)); } else { if (!this.hasPostFlightRequests()) { @@ -582,7 +576,7 @@ export class SearchSource { }); }), map((response) => { - if (!isCompleteResponse(response)) { + if (isRunningResponse(response)) { return response; } return onResponse(searchRequest, response, options); diff --git a/src/plugins/data/common/search/utils.test.ts b/src/plugins/data/common/search/utils.test.ts index cd8ba5340b352..e81a35fb6caeb 100644 --- a/src/plugins/data/common/search/utils.test.ts +++ b/src/plugins/data/common/search/utils.test.ts @@ -6,210 +6,42 @@ * Side Public License, v 1. */ -import { isErrorResponse, isCompleteResponse, isPartialResponse } from './utils'; +import type { IKibanaSearchResponse } from './types'; +import { isAbortResponse, isRunningResponse } from './utils'; describe('utils', () => { - describe('isErrorResponse', () => { + describe('isAbortResponse', () => { it('returns `true` if the response is undefined', () => { - const isError = isErrorResponse(); + const isError = isAbortResponse(); expect(isError).toBe(true); }); - it('returns `true` if the response is not running and partial', () => { - const isError = isErrorResponse({ - isPartial: true, - isRunning: false, - rawResponse: {}, - }); - expect(isError).toBe(true); - }); - - it('returns `false` if the response is not running and partial and contains failure details', () => { - const isError = isErrorResponse({ - isPartial: true, - isRunning: false, - rawResponse: { - took: 7, - timed_out: false, - _shards: { - total: 2, - successful: 1, - skipped: 0, - failed: 1, - failures: [ - { - shard: 0, - index: 'remote:tmp-00002', - node: '9SNgMgppT2-6UHJNXwio3g', - reason: { - type: 'script_exception', - reason: 'runtime error', - script_stack: [ - 'org.elasticsearch.server@8.10.0/org.elasticsearch.search.lookup.LeafDocLookup.getFactoryForDoc(LeafDocLookup.java:148)', - 'org.elasticsearch.server@8.10.0/org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:191)', - 'org.elasticsearch.server@8.10.0/org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:32)', - "doc['bar'].value < 10", - ' ^---- HERE', - ], - script: "doc['bar'].value < 10", - lang: 'painless', - position: { - offset: 4, - start: 0, - end: 21, - }, - caused_by: { - type: 'illegal_argument_exception', - reason: 'No field found for [bar] in mapping', - }, - }, - }, - ], - }, - _clusters: { - total: 1, - successful: 1, - skipped: 0, - details: { - remote: { - status: 'partial', - indices: 'tmp-*', - took: 3, - timed_out: false, - _shards: { - total: 2, - successful: 1, - skipped: 0, - failed: 1, - }, - failures: [ - { - shard: 0, - index: 'remote:tmp-00002', - node: '9SNgMgppT2-6UHJNXwio3g', - reason: { - type: 'script_exception', - reason: 'runtime error', - script_stack: [ - 'org.elasticsearch.server@8.10.0/org.elasticsearch.search.lookup.LeafDocLookup.getFactoryForDoc(LeafDocLookup.java:148)', - 'org.elasticsearch.server@8.10.0/org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:191)', - 'org.elasticsearch.server@8.10.0/org.elasticsearch.search.lookup.LeafDocLookup.get(LeafDocLookup.java:32)', - "doc['bar'].value < 10", - ' ^---- HERE', - ], - script: "doc['bar'].value < 10", - lang: 'painless', - position: { - offset: 4, - start: 0, - end: 21, - }, - caused_by: { - type: 'illegal_argument_exception', - reason: 'No field found for [bar] in mapping', - }, - }, - }, - ], - }, - }, - }, - hits: { - total: { - value: 1, - relation: 'eq', - }, - max_score: 0, - hits: [ - { - _index: 'remote:tmp-00001', - _id: 'd8JNlYoBFqAcOBVnvdqx', - _score: 0, - _source: { - foo: 'bar', - bar: 1, - }, - }, - ], - }, - }, - }); - expect(isError).toBe(false); - }); - - it('returns `false` if the response is running and partial', () => { - const isError = isErrorResponse({ - isPartial: true, - isRunning: true, - rawResponse: {}, - }); - expect(isError).toBe(false); - }); - - it('returns `false` if the response is complete', () => { - const isError = isErrorResponse({ - isPartial: false, - isRunning: false, - rawResponse: {}, - }); - expect(isError).toBe(false); - }); - }); - - describe('isCompleteResponse', () => { - it('returns `false` if the response is undefined', () => { - const isError = isCompleteResponse(); - expect(isError).toBe(false); - }); - - it('returns `false` if the response is running and partial', () => { - const isError = isCompleteResponse({ - isPartial: true, - isRunning: true, - rawResponse: {}, - }); - expect(isError).toBe(false); - }); - - it('returns `true` if the response is complete', () => { - const isError = isCompleteResponse({ - isPartial: false, - isRunning: false, - rawResponse: {}, - }); - expect(isError).toBe(true); - }); - - it('returns `true` if the response does not indicate isRunning', () => { - const isError = isCompleteResponse({ - rawResponse: {}, - }); + it('returns `true` if rawResponse is undefined', () => { + const isError = isAbortResponse({} as unknown as IKibanaSearchResponse); expect(isError).toBe(true); }); }); - describe('isPartialResponse', () => { + describe('isRunningResponse', () => { it('returns `false` if the response is undefined', () => { - const isError = isPartialResponse(); - expect(isError).toBe(false); + const isRunning = isRunningResponse(); + expect(isRunning).toBe(false); }); - it('returns `true` if the response is running and partial', () => { - const isError = isPartialResponse({ - isPartial: true, + it('returns `true` if the response is running', () => { + const isRunning = isRunningResponse({ isRunning: true, rawResponse: {}, }); - expect(isError).toBe(true); + expect(isRunning).toBe(true); }); - it('returns `false` if the response is complete', () => { - const isError = isPartialResponse({ - isPartial: false, + it('returns `false` if the response is finished running', () => { + const isRunning = isRunningResponse({ isRunning: false, rawResponse: {}, }); - expect(isError).toBe(false); + expect(isRunning).toBe(false); }); }); }); diff --git a/src/plugins/data/common/search/utils.ts b/src/plugins/data/common/search/utils.ts index c8a6ca46e5919..a369501981328 100644 --- a/src/plugins/data/common/search/utils.ts +++ b/src/plugins/data/common/search/utils.ts @@ -10,45 +10,20 @@ import moment from 'moment-timezone'; import { AggTypesDependencies } from '..'; import type { IKibanaSearchResponse } from './types'; +// TODO - investigate if this check is still needed +// There are no documented work flows where response or rawResponse is not returned +// Leaving check to prevent breaking changes until full investigation can be completed. /** - * From https://github.com/elastic/elasticsearch/issues/55572: "When is_running is false, the query has stopped, which - * may happen due to ... the search failed, in which case is_partial is set to true to indicate that any results that - * may be included in the search response come only from a subset of the shards that the query should have hit." - * @returns true if response had an error while executing in ES + * @returns true if response is abort */ -export const isErrorResponse = (response?: IKibanaSearchResponse) => { - return ( - !response || - !response.rawResponse || - (!response.isRunning && - !!response.isPartial && - // See https://github.com/elastic/elasticsearch/pull/97731. For CCS with ccs_minimize_roundtrips=true, isPartial - // is true if the search is complete but there are shard failures. In that case, the _clusters.details section - // will have information about those failures. This will also likely be the behavior of CCS with - // ccs_minimize_roundtrips=false and non-CCS after https://github.com/elastic/elasticsearch/issues/98913 is - // resolved. - !response.rawResponse?._clusters?.details) - ); +export const isAbortResponse = (response?: IKibanaSearchResponse) => { + return !response || !response.rawResponse; }; /** - * @returns true if response is completed successfully + * @returns true if request is still running */ -export const isCompleteResponse = (response?: IKibanaSearchResponse) => { - // Some custom search strategies do not indicate whether they are still running. In this case, assume it is complete. - if (response && !response.hasOwnProperty('isRunning')) { - return true; - } - - return !isErrorResponse(response) && Boolean(response && !response.isRunning); -}; - -/** - * @returns true if request is still running an/d response contains partial results - */ -export const isPartialResponse = (response?: IKibanaSearchResponse) => { - return Boolean(response && response.isRunning && response.isPartial); -}; +export const isRunningResponse = (response?: IKibanaSearchResponse) => response?.isRunning ?? false; export const getUserTimeZone = ( getConfig: AggTypesDependencies['getConfig'], diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 108b160b3b239..e51bb8f208326 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -197,7 +197,7 @@ export type { } from './search'; export type { ISearchOptions } from '../common'; -export { isErrorResponse, isCompleteResponse, isPartialResponse } from '../common'; +export { isRunningResponse } from '../common'; // Search namespace export const search = { diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts index ebc7f2528a6de..74b4a6cda7530 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts @@ -249,29 +249,6 @@ describe('SearchInterceptor', () => { expect(error).not.toHaveBeenCalled(); }); - test('should abort if request is partial and not running (ES graceful error)', async () => { - const responses = [ - { - time: 10, - value: { - isPartial: true, - isRunning: false, - rawResponse: {}, - id: 1, - }, - }, - ]; - mockFetchImplementation(responses); - - const response = searchInterceptor.search({}); - response.subscribe({ next, error }); - - await timeTravel(10); - - expect(error).toHaveBeenCalled(); - expect(error.mock.calls[0][0]).toBeInstanceOf(Error); - }); - test('should abort on user abort', async () => { const responses = [ { @@ -1005,30 +982,6 @@ describe('SearchInterceptor', () => { expect(fetchMock).toBeCalledTimes(2); }); - test('should deliver error to all replays', async () => { - const responses = [ - { - time: 10, - value: { - isPartial: true, - isRunning: false, - rawResponse: {}, - id: 1, - }, - }, - ]; - - mockFetchImplementation(responses); - - searchInterceptor.search(basicReq, { sessionId }).subscribe({ next, error, complete }); - searchInterceptor.search(basicReq, { sessionId }).subscribe({ next, error, complete }); - await timeTravel(10); - expect(fetchMock).toBeCalledTimes(1); - expect(error).toBeCalledTimes(2); - expect(error.mock.calls[0][0].message).toEqual('Received partial response'); - expect(error.mock.calls[1][0].message).toEqual('Received partial response'); - }); - test('should ignore anything outside params when hashing', async () => { mockFetchImplementation(basicCompleteResponse); @@ -1055,6 +1008,25 @@ describe('SearchInterceptor', () => { expect(fetchMock).toBeCalledTimes(1); }); + test('should deliver error to all replays', async () => { + const responses = [ + { + time: 10, + value: {}, + }, + ]; + + mockFetchImplementation(responses); + + searchInterceptor.search(basicReq, { sessionId }).subscribe({ next, error, complete }); + searchInterceptor.search(basicReq, { sessionId }).subscribe({ next, error, complete }); + await timeTravel(10); + expect(fetchMock).toBeCalledTimes(1); + expect(error).toBeCalledTimes(2); + expect(error.mock.calls[0][0].message).toEqual('Aborted'); + expect(error.mock.calls[1][0].message).toEqual('Aborted'); + }); + test('should ignore preference when hashing', async () => { mockFetchImplementation(basicCompleteResponse); diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts index 00ed4226fea3d..87f2ffe97c034 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts @@ -51,7 +51,7 @@ import { IAsyncSearchOptions, IKibanaSearchRequest, IKibanaSearchResponse, - isCompleteResponse, + isRunningResponse, ISearchOptions, ISearchOptionsSerializable, pollSearch, @@ -312,7 +312,7 @@ export class SearchInterceptor { tap((response) => { id = response.id; - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { searchTracker?.complete(); } }), diff --git a/src/plugins/data/public/search/search_interceptor/search_response_cache.test.ts b/src/plugins/data/public/search/search_interceptor/search_response_cache.test.ts index d97d31112f699..bab49b22f5a55 100644 --- a/src/plugins/data/public/search/search_interceptor/search_response_cache.test.ts +++ b/src/plugins/data/public/search/search_interceptor/search_response_cache.test.ts @@ -120,23 +120,17 @@ describe('SearchResponseCache', () => { isPartial: true, isRunning: true, rawResponse: { - t: 1, - }, - }, - { - isPartial: true, - isRunning: false, - rawResponse: { - t: 2, + t: 'a'.repeat(1000), }, }, + {} as any, ]); cache.set('123', wrapWithAbortController(err$)); const errHandler = jest.fn(); await err$.toPromise().catch(errHandler); - expect(errHandler).toBeCalledTimes(0); + expect(errHandler).toBeCalledTimes(1); expect(cache.get('123')).toBeUndefined(); }); diff --git a/src/plugins/data/public/search/search_interceptor/search_response_cache.ts b/src/plugins/data/public/search/search_interceptor/search_response_cache.ts index 1af32afdc5e90..a9bf468f54f48 100644 --- a/src/plugins/data/public/search/search_interceptor/search_response_cache.ts +++ b/src/plugins/data/public/search/search_interceptor/search_response_cache.ts @@ -8,7 +8,7 @@ import { Observable, Subscription } from 'rxjs'; import { SearchAbortController } from './search_abort_controller'; -import { IKibanaSearchResponse, isErrorResponse } from '../../../common'; +import { IKibanaSearchResponse } from '../../../common'; interface ResponseCacheItem { response$: Observable; @@ -102,14 +102,14 @@ export class SearchResponseCache { next: (r) => { // TODO: avoid stringiying. Get the size some other way! const newSize = new Blob([JSON.stringify(r)]).size; - if (this.byteToMb(newSize) < this.maxCacheSizeMB && !isErrorResponse(r)) { + if (this.byteToMb(newSize) < this.maxCacheSizeMB) { this.setItem(key, { ...cacheItem, size: newSize, }); this.shrink(); } else { - // Single item is too large to be cached, or an error response returned. + // Single item is too large to be cached // Evict and ignore. this.deleteItem(key); } diff --git a/src/plugins/data/server/search/collectors/search/usage.ts b/src/plugins/data/server/search/collectors/search/usage.ts index fb2a9acc37009..65eafd28538e3 100644 --- a/src/plugins/data/server/search/collectors/search/usage.ts +++ b/src/plugins/data/server/search/collectors/search/usage.ts @@ -9,7 +9,7 @@ import { once, debounce } from 'lodash'; import type { CoreSetup, Logger } from '@kbn/core/server'; import type { IEsSearchResponse, ISearchOptions } from '../../../../common'; -import { isCompleteResponse } from '../../../../common'; +import { isRunningResponse } from '../../../../common'; import { CollectedUsage } from './register'; const SAVED_OBJECT_ID = 'search-telemetry'; @@ -86,7 +86,7 @@ export function searchUsageObserver( ) { return { next(response: IEsSearchResponse) { - if (isRestore || !isCompleteResponse(response)) return; + if (isRestore || isRunningResponse(response)) return; logger.debug(`trackSearchStatus:success, took:${response.rawResponse.took}`); usage?.trackSuccess(response.rawResponse.took); }, diff --git a/src/plugins/data/server/search/session/get_search_status.test.ts b/src/plugins/data/server/search/session/get_search_status.test.ts index 20fcc6935b0d6..a4b051fa8b477 100644 --- a/src/plugins/data/server/search/session/get_search_status.test.ts +++ b/src/plugins/data/server/search/session/get_search_status.test.ts @@ -19,7 +19,7 @@ describe('getSearchStatus', () => { }; }); - test('returns an error status if search is partial and not running', async () => { + test('returns a complete status if search is partial and not running', async () => { mockClient.asyncSearch.status.mockResolvedValue({ body: { is_partial: true, @@ -28,7 +28,7 @@ describe('getSearchStatus', () => { }, }); const res = await getSearchStatus(mockClient, '123'); - expect(res.status).toBe(SearchStatus.ERROR); + expect(res.status).toBe(SearchStatus.COMPLETE); }); test('returns an error status if completion_status is an error', async () => { diff --git a/src/plugins/data/server/search/session/get_search_status.ts b/src/plugins/data/server/search/session/get_search_status.ts index f9b14fefdf397..46637555fe5e2 100644 --- a/src/plugins/data/server/search/session/get_search_status.ts +++ b/src/plugins/data/server/search/session/get_search_status.ts @@ -29,7 +29,7 @@ export async function getSearchStatus( { meta: true } ); const response = apiResponse.body; - if ((response.is_partial && !response.is_running) || response.completion_status >= 400) { + if (response.completion_status >= 400) { return { status: SearchStatus.ERROR, error: i18n.translate('data.search.statusError', { @@ -37,7 +37,7 @@ export async function getSearchStatus( values: { searchId: asyncId, errorCode: response.completion_status }, }), }; - } else if (!response.is_partial && !response.is_running) { + } else if (!response.is_running) { return { status: SearchStatus.COMPLETE, error: undefined, diff --git a/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.test.ts b/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.test.ts index f4cfcabf0737b..530ee16ae75b9 100644 --- a/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.test.ts +++ b/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.test.ts @@ -266,7 +266,9 @@ describe('SQL search strategy', () => { ); const esSearch = await sqlSearchStrategyProvider(mockSearchConfig, mockLogger); - await esSearch.search({ id: 'foo', params: { query: 'query' } }, {}, mockDeps).toPromise(); + esSearch.search({ id: 'foo', params: { query: 'query' } }, {}, mockDeps); + // await next tick. esSearch.search will not resolve until `is_running: false` + await new Promise((resolve) => process.nextTick(resolve)); expect(mockSqlClearCursor).not.toHaveBeenCalled(); }); diff --git a/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.ts b/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.ts index c8928a343eec5..d5d1eafc5c214 100644 --- a/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.ts +++ b/src/plugins/data/server/search/strategies/sql_search/sql_search_strategy.ts @@ -69,7 +69,7 @@ export const sqlSearchStrategyProvider = ( )); } - if (!body.is_partial && !body.is_running && body.cursor && !keepCursor) { + if (!body.is_running && body.cursor && !keepCursor) { try { await client.sql.clearCursor({ cursor: body.cursor }); } catch (error) { diff --git a/src/plugins/discover/public/application/main/utils/fetch_documents.ts b/src/plugins/discover/public/application/main/utils/fetch_documents.ts index e849b347a22ff..99e87f13558a8 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_documents.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_documents.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { filter, map } from 'rxjs/operators'; import { lastValueFrom } from 'rxjs'; -import { isCompleteResponse, ISearchSource } from '@kbn/data-plugin/public'; +import { isRunningResponse, ISearchSource } from '@kbn/data-plugin/public'; import { SAMPLE_SIZE_SETTING, buildDataTableRecordList } from '@kbn/discover-utils'; import type { EsHitRecord } from '@kbn/discover-utils/types'; import { getSearchResponseInterceptedWarnings } from '@kbn/search-response-warnings'; @@ -62,7 +62,7 @@ export const fetchDocuments = ( disableWarningToasts: true, }) .pipe( - filter((res) => isCompleteResponse(res)), + filter((res) => !isRunningResponse(res)), map((res) => { return buildDataTableRecordList(res.rawResponse.hits.hits as EsHitRecord[], dataView); }) diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_total_hits.ts b/src/plugins/unified_histogram/public/chart/hooks/use_total_hits.ts index 2beed34b3d919..5bb927747e669 100644 --- a/src/plugins/unified_histogram/public/chart/hooks/use_total_hits.ts +++ b/src/plugins/unified_histogram/public/chart/hooks/use_total_hits.ts @@ -7,7 +7,7 @@ */ import { textBasedQueryStateToAstWithValidation } from '@kbn/data-plugin/common'; -import { isCompleteResponse } from '@kbn/data-plugin/public'; +import { isRunningResponse } from '@kbn/data-plugin/public'; import { DataView, DataViewType } from '@kbn/data-views-plugin/public'; import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; import { Datatable, isExpressionValueError } from '@kbn/expressions-plugin/common'; @@ -209,7 +209,7 @@ const fetchTotalHitsSearchSource = async ({ disableWarningToasts: true, // TODO: show warnings as a badge next to total hits number }) .pipe( - filter((res) => isCompleteResponse(res)), + filter((res) => !isRunningResponse(res)), map((res) => res.rawResponse.hits.total as number), catchError((error: Error) => of(error)) ); diff --git a/x-pack/plugins/aiops/public/components/log_categorization/use_categorize_request.ts b/x-pack/plugins/aiops/public/components/log_categorization/use_categorize_request.ts index 8179751266e6e..eb9f7d403421a 100644 --- a/x-pack/plugins/aiops/public/components/log_categorization/use_categorize_request.ts +++ b/x-pack/plugins/aiops/public/components/log_categorization/use_categorize_request.ts @@ -9,7 +9,7 @@ import { useRef, useCallback, useMemo } from 'react'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { isCompleteResponse } from '@kbn/data-plugin/public'; +import { isRunningResponse } from '@kbn/data-plugin/public'; import { useStorage } from '@kbn/ml-local-storage'; import { createCategoryRequest } from '../../../common/api/log_categorization/create_category_request'; @@ -83,7 +83,7 @@ export function useCategorizeRequest() { ) .subscribe({ next: (result) => { - if (isCompleteResponse(result)) { + if (!isRunningResponse(result)) { resolve(processCategoryResults(result, field, unwrap)); } else { // partial results diff --git a/x-pack/plugins/aiops/public/hooks/use_cancellable_search.ts b/x-pack/plugins/aiops/public/hooks/use_cancellable_search.ts index a44e176c1c073..7d905fe2894ea 100644 --- a/x-pack/plugins/aiops/public/hooks/use_cancellable_search.ts +++ b/x-pack/plugins/aiops/public/hooks/use_cancellable_search.ts @@ -6,7 +6,7 @@ */ import { useCallback, useRef, useState } from 'react'; -import { type IKibanaSearchResponse, isCompleteResponse } from '@kbn/data-plugin/common'; +import { type IKibanaSearchResponse, isRunningResponse } from '@kbn/data-plugin/common'; import { tap } from 'rxjs/operators'; import { useAiopsAppContext } from './use_aiops_app_context'; @@ -31,7 +31,7 @@ export function useCancellableSearch() { ) .subscribe({ next: (result) => { - if (isCompleteResponse(result)) { + if (!isRunningResponse(result)) { setIsFetching(false); resolve(result); } else { diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.ts b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.ts index 82cddd8f74e91..5836e64599ea0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_explore_table_logic.ts @@ -11,7 +11,7 @@ import { DataView, IKibanaSearchRequest, IKibanaSearchResponse, - isCompleteResponse, + isRunningResponse, TimeRange, } from '@kbn/data-plugin/common'; @@ -415,7 +415,7 @@ export const AnalyticsCollectionExploreTableLogic = kea< KibanaLogic.values.data.search.showError(e); }, next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { const { items, totalCount } = parseResponse(response); actions.setItems(items); diff --git a/x-pack/plugins/observability_shared/public/hooks/use_es_search.ts b/x-pack/plugins/observability_shared/public/hooks/use_es_search.ts index c4fe3b050778b..43eb2596e1581 100644 --- a/x-pack/plugins/observability_shared/public/hooks/use_es_search.ts +++ b/x-pack/plugins/observability_shared/public/hooks/use_es_search.ts @@ -9,7 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { ESSearchResponse } from '@kbn/es-types'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { IInspectorInfo, isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/common'; +import { IInspectorInfo, isRunningResponse } from '@kbn/data-plugin/common'; import { getInspectResponse } from '../../common/utils/get_inspect_response'; import { useInspectorContext } from '../contexts/inspector/use_inspector_context'; import { FETCH_STATUS, useFetcher } from './use_fetcher'; @@ -42,7 +42,7 @@ export const useEsSearch = { - if (isCompleteResponse(result)) { + if (!isRunningResponse(result)) { if (addInspectorRequest) { addInspectorRequest({ data: { @@ -72,32 +72,30 @@ export const useEsSearch = { - if (isErrorResponse(err)) { - // eslint-disable-next-line no-console - console.error(err); - if (addInspectorRequest) { - addInspectorRequest({ - data: { - _inspect: [ - getInspectResponse({ - startTime, - esRequestParams: params, - esResponse: null, - esError: { originalError: err, name: err.name, message: err.message }, - esRequestStatus: 2, - operationName: name, - kibanaRequest: { - route: { - path: '/internal/bsearch', - method: 'POST', - }, - } as any, - }), - ], - }, - status: FETCH_STATUS.SUCCESS, - }); - } + // eslint-disable-next-line no-console + console.error(err); + if (addInspectorRequest) { + addInspectorRequest({ + data: { + _inspect: [ + getInspectResponse({ + startTime, + esRequestParams: params, + esResponse: null, + esError: { originalError: err, name: err.name, message: err.message }, + esRequestStatus: 2, + operationName: name, + kibanaRequest: { + route: { + path: '/internal/bsearch', + method: 'POST', + }, + } as any, + }), + ], + }, + status: FETCH_STATUS.SUCCESS, + }); } }, }); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/use_timelines_events.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/use_timelines_events.tsx index 691d0e95cb924..5a37de1a7c61c 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/use_timelines_events.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/use_timelines_events.tsx @@ -12,7 +12,7 @@ import { useDispatch } from 'react-redux'; import { Subscription } from 'rxjs'; import type { DataView } from '@kbn/data-views-plugin/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { Inspect, PaginationInputPaginated, @@ -246,7 +246,7 @@ export const useTimelineEventsHandler = ({ ) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { setTimelineResponse((prevResponse) => { const newTimelineResponse = { ...prevResponse, diff --git a/x-pack/plugins/security_solution/public/common/containers/cti/event_enrichment/api.ts b/x-pack/plugins/security_solution/public/common/containers/cti/event_enrichment/api.ts index aac311cba167f..1b46d071d244d 100644 --- a/x-pack/plugins/security_solution/public/common/containers/cti/event_enrichment/api.ts +++ b/x-pack/plugins/security_solution/public/common/containers/cti/event_enrichment/api.ts @@ -9,7 +9,7 @@ import type { Observable } from 'rxjs'; import { filter } from 'rxjs/operators'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { EventEnrichmentRequestOptionsInput } from '../../../../../common/api/search_strategy'; import type { CtiEventEnrichmentStrategyResponse } from '../../../../../common/search_strategy/security_solution/cti'; import { CtiQueries } from '../../../../../common/search_strategy/security_solution/cti'; @@ -44,4 +44,4 @@ export const getEventEnrichment = ({ export const getEventEnrichmentComplete = ( props: GetEventEnrichmentProps ): Observable => - getEventEnrichment(props).pipe(filter((response) => isCompleteResponse(response))); + getEventEnrichment(props).pipe(filter((response) => !isRunningResponse(response))); diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts index 471d43c928458..acd454f85de33 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { inputsModel } from '../../../store'; import { useKibana } from '../../../lib/kibana'; import type { @@ -77,7 +77,7 @@ export const useTimelineLastEventTime = ({ }) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { setLoading(false); setTimelineLastEventTimeResponse((prevResponse) => ({ ...prevResponse, 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 51128621da177..a9ffa6049d9f9 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 @@ -10,7 +10,7 @@ import { getOr, noop } from 'lodash/fp'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { MatrixHistogramRequestOptionsInput } from '../../../../common/api/search_strategy'; import type { MatrixHistogramQueryProps } from '../../components/matrix_histogram/types'; import type { inputsModel } from '../../store'; @@ -121,7 +121,7 @@ export const useMatrixHistogram = ({ }) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { const histogramBuckets: Buckets = getOr( bucketEmpty, MatrixHistogramTypeToAggName[histogramType], diff --git a/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.test.ts index b86eaaf386149..085a168a1b65d 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.test.ts @@ -102,7 +102,7 @@ describe('useSearchStrategy', () => { useSearchStrategy({ ...userSearchStrategyProps, initialResult }) ); - expect(result.current.result).toBe(initialResult); + expect(result.current.result).toEqual(initialResult); }); it('calls start with the given request', () => { @@ -278,9 +278,7 @@ describe('useSearchStrategy', () => { it('should handle search error', () => { mockResponse.mockImplementation(() => { - throw new Error( - 'simulated search response error, which could be 1) undefined response, 2) response without rawResponse, or 3) partial response' - ); + throw new Error('simulated search error'); }); const { result } = renderHook(() => useSearch(factoryQueryType)); diff --git a/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.tsx b/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.tsx index 320339b0ec4df..75eb80debeb3c 100644 --- a/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/use_search_strategy/index.tsx @@ -9,7 +9,7 @@ import { noop, omit } from 'lodash/fp'; import { useCallback, useEffect, useRef, useMemo } from 'react'; import type { Observable } from 'rxjs'; import { useObservable } from '@kbn/securitysolution-hook-utils'; -import { isCompleteResponse, isErrorResponse } from '@kbn/data-plugin/public'; +import { isRunningResponse } from '@kbn/data-plugin/public'; import { AbortError } from '@kbn/kibana-utils-plugin/common'; import * as i18n from './translations'; @@ -64,7 +64,7 @@ export const useSearch = ( abortSignal, } ) - .pipe(filter((response) => isCompleteResponse(response))); + .pipe(filter((response) => !isRunningResponse(response))); observable.subscribe({ next: (response) => { @@ -158,7 +158,7 @@ export const useSearchStrategy = ({ }, [abort]); const [formattedResult, inspect] = useMemo(() => { - if (isErrorResponse(result)) { + if (!result) { return [initialResult, EMPTY_INSPECT]; } return [ diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx index ce7fa7e01886e..417c3678fb06a 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { NetworkKpiDnsRequestOptionsInput } from '../../../../../../common/api/search_strategy'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; @@ -86,7 +86,7 @@ export const useNetworkKpiDns = ({ }) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { setLoading(false); setNetworkKpiDnsResponse((prevResponse) => ({ ...prevResponse, diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx index 40e238e73d56d..e675196aa0a80 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { NetworkKpiEventsRequestOptionsInput } from '../../../../../../common/api/search_strategy'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; @@ -90,7 +90,7 @@ export const useNetworkKpiNetworkEvents = ({ ) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { setLoading(false); setNetworkKpiNetworkEventsResponse((prevResponse) => ({ ...prevResponse, diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx index 5bede07b4ce2d..a94041f640d93 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { NetworkKpiTlsHandshakesRequestOptionsInput } from '../../../../../../common/api/search_strategy'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; @@ -89,7 +89,7 @@ export const useNetworkKpiTlsHandshakes = ({ }) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { setLoading(false); setNetworkKpiTlsHandshakesResponse((prevResponse) => ({ ...prevResponse, diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx index 8172c700fef12..e997a9eb94e1d 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { NetworkKpiUniqueFlowsRequestOptionsInput } from '../../../../../../common/api/search_strategy'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; @@ -89,7 +89,7 @@ export const useNetworkKpiUniqueFlows = ({ ) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { setLoading(false); setNetworkKpiUniqueFlowsResponse((prevResponse) => ({ ...prevResponse, diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx index d214a8d30bd16..a61d4a859082e 100644 --- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx @@ -10,7 +10,7 @@ import { noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { NetworkKpiUniquePrivateIpsRequestOptionsInput } from '../../../../../../common/api/search_strategy'; import { useAppToasts } from '../../../../../common/hooks/use_app_toasts'; import type { inputsModel } from '../../../../../common/store'; @@ -99,7 +99,7 @@ export const useNetworkKpiUniquePrivateIps = ({ }) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { setLoading(false); setNetworkKpiUniquePrivateIpsResponse((prevResponse) => ({ ...prevResponse, diff --git a/x-pack/plugins/security_solution/public/flyout/left/services/find_alerts.ts b/x-pack/plugins/security_solution/public/flyout/left/services/find_alerts.ts index bee4e8bbe6a20..be33f5ac38aba 100644 --- a/x-pack/plugins/security_solution/public/flyout/left/services/find_alerts.ts +++ b/x-pack/plugins/security_solution/public/flyout/left/services/find_alerts.ts @@ -6,7 +6,7 @@ */ import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; -import { isCompleteResponse, type ISearchStart, isErrorResponse } from '@kbn/data-plugin/public'; +import { isRunningResponse, type ISearchStart } from '@kbn/data-plugin/public'; export interface AlertsQueryParams { alertIds: string[]; @@ -47,14 +47,17 @@ export const createFindAlerts = }, { abortSignal: signal } ) - .subscribe((response) => { - if (isCompleteResponse(response)) { - $subscription.unsubscribe(); - resolve(response.rawResponse); - } else if (isErrorResponse(response)) { + .subscribe({ + next: (response) => { + if (!isRunningResponse(response)) { + $subscription.unsubscribe(); + resolve(response.rawResponse); + } + }, + error: (err) => { $subscription.unsubscribe(); reject(new Error(`Error while loading alerts`)); - } + }, }); }); }; diff --git a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts index 086ad78ac3c5f..5f27cbf9cdaed 100644 --- a/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts +++ b/x-pack/plugins/security_solution/public/overview/containers/overview_cti_links/use_ti_data_sources.ts @@ -9,7 +9,7 @@ import { filter } from 'rxjs/operators'; import { useEffect, useState } from 'react'; import { useObservable, withOptionalSignal } from '@kbn/securitysolution-hook-utils'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { isCompleteResponse } from '@kbn/data-plugin/public'; +import { isRunningResponse } from '@kbn/data-plugin/public'; import type { ThreatIntelSourceRequestOptionsInput } from '../../../../common/api/search_strategy'; import { useKibana } from '../../../common/lib/kibana'; import type { @@ -51,7 +51,7 @@ export const getTiDataSourcesComplete = ( ): Observable => { return getTiDataSources(props).pipe( filter((response) => { - return isCompleteResponse(response); + return !isRunningResponse(response); }) ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx index b35c2c86b9abe..5230aa6f561a5 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx @@ -11,7 +11,7 @@ import ReactDOM from 'react-dom'; import deepEqual from 'fast-deep-equal'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { TimelineEventsDetailsRequestOptionsInput } from '@kbn/timelines-plugin/common'; import { EntityType } from '@kbn/timelines-plugin/common'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; @@ -88,7 +88,7 @@ export const useTimelineEventsDetails = ({ ) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { Promise.resolve().then(() => { ReactDOM.unstable_batchedUpdates(() => { setLoading(false); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index 8d6e871f8354b..077e6448a7812 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -12,7 +12,7 @@ import { useDispatch } from 'react-redux'; import { Subscription } from 'rxjs'; import type { DataView } from '@kbn/data-plugin/common'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { TimelineEqlRequestOptionsInput, TimelineEventsAllOptionsInput, @@ -245,7 +245,7 @@ export const useTimelineEventsHandler = ({ }) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { endTracking('success'); setLoading(false); setTimelineResponse((prevResponse) => { diff --git a/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx index a260a3d9f9cdd..e217f5e121084 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/kpis/index.tsx @@ -10,7 +10,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/public'; +import { isRunningResponse } from '@kbn/data-plugin/public'; import { TimelineEventsQueries } from '@kbn/timelines-plugin/common'; import type { inputsModel } from '../../../common/store'; import { useKibana } from '../../../common/lib/kibana'; @@ -63,7 +63,7 @@ export const useTimelineKpis = ({ }) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { setLoading(false); setTimelineKpiResponse(response); searchSubscription$.current.unsubscribe(); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/state/elasticsearch/api.ts b/x-pack/plugins/synthetics/public/apps/synthetics/state/elasticsearch/api.ts index 41f3405bbd7cc..010dd3f0fdfa5 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/state/elasticsearch/api.ts +++ b/x-pack/plugins/synthetics/public/apps/synthetics/state/elasticsearch/api.ts @@ -5,11 +5,7 @@ * 2.0. */ -import { - IKibanaSearchResponse, - isCompleteResponse, - isErrorResponse, -} from '@kbn/data-plugin/common'; +import { IKibanaSearchResponse, isRunningResponse } from '@kbn/data-plugin/common'; import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ESSearchResponse } from '@kbn/es-types'; import { FETCH_STATUS } from '@kbn/observability-shared-plugin/public'; @@ -40,7 +36,7 @@ export const executeEsQueryAPI = async ({ ) .subscribe({ next: (result) => { - if (isCompleteResponse(result)) { + if (!isRunningResponse(result)) { if (addInspectorRequest) { addInspectorRequest({ data: { @@ -70,33 +66,31 @@ export const executeEsQueryAPI = async ({ } }, error: (err) => { - if (isErrorResponse(err)) { - // eslint-disable-next-line no-console - console.error(err); - reject(err); - if (addInspectorRequest) { - addInspectorRequest({ - data: { - _inspect: [ - getInspectResponse({ - startTime, - esRequestParams: params, - esResponse: null, - esError: { originalError: err, name: err.name, message: err.message }, - esRequestStatus: 2, - operationName: name, - kibanaRequest: { - route: { - path: '/internal/bsearch', - method: 'POST', - }, - } as any, - }), - ], - }, - status: FETCH_STATUS.SUCCESS, - }); - } + // eslint-disable-next-line no-console + console.error(err); + reject(err); + if (addInspectorRequest) { + addInspectorRequest({ + data: { + _inspect: [ + getInspectResponse({ + startTime, + esRequestParams: params, + esResponse: null, + esError: { originalError: err, name: err.name, message: err.message }, + esRequestStatus: 2, + operationName: name, + kibanaRequest: { + route: { + path: '/internal/bsearch', + method: 'POST', + }, + } as any, + }), + ], + }, + status: FETCH_STATUS.SUCCESS, + }); } }, }); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_total_count.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_total_count.tsx index d99d7c0fc4b01..d9b7c22f83354 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_total_count.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_total_count.tsx @@ -9,7 +9,7 @@ import { useEffect, useState } from 'react'; import { IEsSearchRequest, IKibanaSearchResponse, - isCompleteResponse, + isRunningResponse, } from '@kbn/data-plugin/common'; import { useKibana } from '../../../hooks/use_kibana'; import { useSourcererDataView } from './use_sourcerer_data_view'; @@ -61,7 +61,7 @@ export const useIndicatorsTotalCount = () => { .search>(req) .subscribe({ next: (res) => { - if (isCompleteResponse(res)) { + if (!isRunningResponse(res)) { const returnedCount = res.rawResponse.hits.total || 0; setCount(returnedCount); diff --git a/x-pack/plugins/threat_intelligence/public/utils/search.ts b/x-pack/plugins/threat_intelligence/public/utils/search.ts index 1dac39f74b230..aa0fa68f9038c 100644 --- a/x-pack/plugins/threat_intelligence/public/utils/search.ts +++ b/x-pack/plugins/threat_intelligence/public/utils/search.ts @@ -8,7 +8,7 @@ import { IEsSearchRequest, IKibanaSearchResponse, - isCompleteResponse, + isRunningResponse, } from '@kbn/data-plugin/common'; import { ISearchStart } from '@kbn/data-plugin/public'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; @@ -93,7 +93,7 @@ export const search = async ( }) .subscribe({ next: (response) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { inspect.recordRequestCompletion(searchRequest, response); resolve(response.rawResponse); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.test.tsx index ef515cf8ffdca..fdc7282f6c817 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.test.tsx @@ -287,9 +287,7 @@ describe('useFetchAlerts', () => { }); it('handles search error', () => { - const obs$ = throwError( - 'simulated search response error, which could be 1) undefined response, 2) response without rawResponse, or 3) partial response' - ); + const obs$ = throwError('simulated search error'); dataSearchMock.mockReturnValue(obs$); const { result } = renderHook(() => useFetchAlerts(args)); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.tsx index 374e2883cc089..acf00da71b5f7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/hooks/use_fetch_alerts.tsx @@ -12,7 +12,7 @@ import { noop } from 'lodash'; import { useCallback, useEffect, useReducer, useRef, useMemo } from 'react'; import { Subscription } from 'rxjs'; -import { isCompleteResponse } from '@kbn/data-plugin/common'; +import { isRunningResponse } from '@kbn/data-plugin/common'; import type { RuleRegistrySearchRequest, RuleRegistrySearchRequestPagination, @@ -206,7 +206,7 @@ const useFetchAlerts = ({ ) .subscribe({ next: (response: RuleRegistrySearchResponse) => { - if (isCompleteResponse(response)) { + if (!isRunningResponse(response)) { const { rawResponse } = response; inspectQuery.current = { request: response?.inspect?.dsl ?? [], diff --git a/x-pack/plugins/ux/public/components/app/rum_dashboard/ux_overview_fetchers.ts b/x-pack/plugins/ux/public/components/app/rum_dashboard/ux_overview_fetchers.ts index 7e7cdbcf0abad..4df3c8c1e8470 100644 --- a/x-pack/plugins/ux/public/components/app/rum_dashboard/ux_overview_fetchers.ts +++ b/x-pack/plugins/ux/public/components/app/rum_dashboard/ux_overview_fetchers.ts @@ -8,7 +8,7 @@ import type { ESSearchResponse } from '@kbn/es-types'; import { DataPublicPluginStart, - isCompleteResponse, + isRunningResponse, } from '@kbn/data-plugin/public'; import { IKibanaSearchRequest } from '@kbn/data-plugin/common'; import { @@ -117,7 +117,7 @@ async function esQuery( }) .subscribe({ next: (result) => { - if (isCompleteResponse(result)) { + if (!isRunningResponse(result)) { resolve(result.rawResponse as any); search$.unsubscribe(); }