mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[search source] remove is_partial check (#164506)
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 <jatin.kathuria@elastic.co> Co-authored-by: Patryk Kopyciński <contact@patrykkopycinski.com>
This commit is contained in:
parent
236eff4769
commit
b5bcf69022
47 changed files with 192 additions and 441 deletions
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 = <Response extends IKibanaSearchResponse>(
|
||||
search: () => Promise<Response>,
|
||||
|
@ -57,11 +57,11 @@ export const pollSearch = <Response extends IKibanaSearchResponse>(
|
|||
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<Response>(isPartialResponse, true),
|
||||
takeWhile<Response>(isRunningResponse, true),
|
||||
takeUntil<Response>(aborted$)
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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<IKibanaSearchResponse<unknown>>((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);
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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'],
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}),
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
@ -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<IKibanaSearchResponse>;
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
})
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = <DocumentSource extends unknown, TParams extends esty
|
|||
)
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (isCompleteResponse(result)) {
|
||||
if (!isRunningResponse(result)) {
|
||||
if (addInspectorRequest) {
|
||||
addInspectorRequest({
|
||||
data: {
|
||||
|
@ -72,32 +72,30 @@ export const useEsSearch = <DocumentSource extends unknown, TParams extends esty
|
|||
}
|
||||
},
|
||||
error: (err) => {
|
||||
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,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<CtiEventEnrichmentStrategyResponse> =>
|
||||
getEventEnrichment(props).pipe(filter((response) => isCompleteResponse(response)));
|
||||
getEventEnrichment(props).pipe(filter((response) => !isRunningResponse(response)));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -102,7 +102,7 @@ describe('useSearchStrategy', () => {
|
|||
useSearchStrategy<FactoryQueryTypes>({ ...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<FactoryQueryTypes>(factoryQueryType));
|
||||
|
|
|
@ -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 = <QueryType extends FactoryQueryTypes>(
|
|||
abortSignal,
|
||||
}
|
||||
)
|
||||
.pipe(filter((response) => isCompleteResponse(response)));
|
||||
.pipe(filter((response) => !isRunningResponse(response)));
|
||||
|
||||
observable.subscribe({
|
||||
next: (response) => {
|
||||
|
@ -158,7 +158,7 @@ export const useSearchStrategy = <QueryType extends FactoryQueryTypes>({
|
|||
}, [abort]);
|
||||
|
||||
const [formattedResult, inspect] = useMemo(() => {
|
||||
if (isErrorResponse(result)) {
|
||||
if (!result) {
|
||||
return [initialResult, EMPTY_INSPECT];
|
||||
}
|
||||
return [
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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`));
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -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<CtiDataSourceStrategyResponse> => {
|
||||
return getTiDataSources(props).pipe(
|
||||
filter((response) => {
|
||||
return isCompleteResponse(response);
|
||||
return !isRunningResponse(response);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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<IEsSearchRequest, IKibanaSearchResponse<RawIndicatorsResponse>>(req)
|
||||
.subscribe({
|
||||
next: (res) => {
|
||||
if (isCompleteResponse(res)) {
|
||||
if (!isRunningResponse(res)) {
|
||||
const returnedCount = res.rawResponse.hits.total || 0;
|
||||
|
||||
setCount(returnedCount);
|
||||
|
|
|
@ -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 <TResponse, T = {}>(
|
|||
})
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
if (isCompleteResponse(response)) {
|
||||
if (!isRunningResponse(response)) {
|
||||
inspect.recordRequestCompletion(searchRequest, response);
|
||||
resolve(response.rawResponse);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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 ?? [],
|
||||
|
|
|
@ -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<T>(
|
|||
})
|
||||
.subscribe({
|
||||
next: (result) => {
|
||||
if (isCompleteResponse(result)) {
|
||||
if (!isRunningResponse(result)) {
|
||||
resolve(result.rawResponse as any);
|
||||
search$.unsubscribe();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue